From ecda7852a99eef99b77dd9f35aee8a4d4f7f3962 Mon Sep 17 00:00:00 2001 From: Wang Xiaofeng Date: Mon, 5 Oct 2026 18:49:39 +0800 Subject: [PATCH] Add latency recorder window statistics test Exercise latency observations split across sampler ticks using a window longer than the bounded wait budget. Confirm the sampled count is complete before checking the sum and average from the saved snapshot, and verify the maximum through its independent sampler. --- test/bvar_recorder_unittest.cpp | 61 +++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/test/bvar_recorder_unittest.cpp b/test/bvar_recorder_unittest.cpp index e383c410cc..d9d3fd7fb3 100644 --- a/test/bvar_recorder_unittest.cpp +++ b/test/bvar_recorder_unittest.cpp @@ -214,6 +214,67 @@ TEST(RecorderTest, perf) { << " threads"; } +class LatencyRecorderWithWindowSample : public bvar::LatencyRecorder { +public: + explicit LatencyRecorderWithWindowSample(time_t window_size) + : bvar::LatencyRecorder(window_size) {} + + bool get_latency_sample(bvar::detail::Sample* sample) const { + return _latency_window.get_span(sample); + } +}; + +TEST(RecorderTest, latency_recorder_window_statistics) { + // Keep both batches across sampler ticks. A ten-second window is longer + // than the five-second wait budget, which allows for busy CI runners. + const time_t window_size = 10; + LatencyRecorderWithWindowSample recorder(window_size); + const int64_t sampling_timeout_us = 5 * 1000000L; + const int64_t deadline = + butil::monotonic_time_us() + sampling_timeout_us; + bvar::detail::Sample sample; + + // Deliberately sample a partial batch before recording the remaining + // values, rather than assuming all writes fall between sampler ticks. + recorder << 1 << 2 << 3; + while (butil::monotonic_time_us() < deadline) { + if (recorder.get_latency_sample(&sample) && sample.data.num >= 3) { + break; + } + usleep(10000); + } + ASSERT_EQ(3, sample.data.num) << "First batch was not sampled"; + EXPECT_EQ(6, sample.data.sum); + + recorder << 4 << 5 << 6 << 7; + EXPECT_EQ(7, recorder.count()); + bool complete_sample = false; + int64_t max_latency = 0; + while (butil::monotonic_time_us() < deadline) { + if (!complete_sample && recorder.get_latency_sample(&sample) && + sample.data.num >= 7) { + complete_sample = true; + } + if (max_latency < 7) { + max_latency = recorder.max_latency(); + } + if (complete_sample && max_latency >= 7) { + break; + } + usleep(10000); + } + + // Validate completeness before checking sum / count from the same saved + // snapshot. Rereading latency() could observe a different sampling span. + ASSERT_TRUE(complete_sample) << "Full batch was not sampled; count=" + << sample.data.num; + ASSERT_EQ(7, sample.data.num); + EXPECT_EQ(28, sample.data.sum); + EXPECT_EQ(4, sample.data.get_average_int()); + // The maximum has an independent sampler and is saved separately. + EXPECT_EQ(7, max_latency); +} + TEST(RecorderTest, latency_recorder_qps_accuracy) { bvar::LatencyRecorder lr1(2); // set windows size to 2s bvar::LatencyRecorder lr2(2);