27 using Duration =
typename Timer::duration;
30 (detail::kResultWantsAddFunction<Result, Counter, Duration> || detail::kResultCanUseAddAssign<Result, Counter>),
31 "The Result template type argument must provide either Add(Counter, "
32 "Duration, Duration) function or add assignment operator"
35 static constexpr bool kUseAddFunction = detail::kResultWantsAddFunction<Result, Counter, Duration>;
38
39
40
41
42 RecentPeriod(Duration epoch_duration = std::chrono::seconds(5), Duration max_duration = std::chrono::seconds(60))
43 : epoch_duration_(epoch_duration),
44 max_duration_(max_duration),
46 items_(GetSizeForDuration(epoch_duration, max_duration))
48 UINVARIANT(Duration::zero() < epoch_duration_,
"epoch_duration should be greater than 0");
51 Counter& GetCurrentCounter() {
return items_[GetCurrentIndex()].counter; }
53 Counter& GetPreviousCounter(
int epochs_ago) {
return items_[GetPreviousIndex(epochs_ago)].counter; }
56
57
58
59
60
61
62
63
64
66 const Result
GetStatsForPeriod(Duration duration = Duration::min(),
bool with_current_epoch =
false)
const {
67 if (duration == Duration::min()) {
68 duration = max_duration_;
72 const Duration now = Timer::now().time_since_epoch();
73 const Duration current_epoch = GetEpochForDuration(now);
74 const Duration start_epoch = current_epoch - duration;
75 const Duration first_epoch_duration = now - current_epoch;
76 std::size_t index = epoch_index_.load();
78 for (std::size_t i = 0; i < items_.size(); i++, index = (index + items_.size() - 1) % items_.size()) {
79 const Duration epoch = items_[index].epoch;
81 if (epoch > current_epoch) {
84 if (epoch == current_epoch && !with_current_epoch) {
87 if (epoch < start_epoch) {
91 if constexpr (kUseAddFunction) {
92 const Duration this_epoch_duration = (i == 0) ? first_epoch_duration : epoch_duration_;
94 const Duration before_this_epoch_duration = epoch - start_epoch;
95 result.Add(items_[index].counter, this_epoch_duration, before_this_epoch_duration);
97 result += items_[index].counter;
104 Duration GetEpochDuration()
const {
return epoch_duration_; }
106 Duration GetMaxDuration()
const {
return max_duration_; }
108 void UpdateEpochIfOld() { [[maybe_unused]]
auto ignore = GetCurrentIndex(); }
111 for (
auto& item : items_) {
112 item.Reset(Duration::min());
117 size_t GetCurrentIndex()
const {
119 const Duration now = Timer::now().time_since_epoch();
120 const Duration epoch = GetEpochForDuration(now);
121 std::size_t index = epoch_index_.load();
122 const Duration bucket_epoch = items_[index].epoch.load();
126 if (epoch > bucket_epoch || epoch + max_duration_ < bucket_epoch) {
127 const std::size_t new_index = (index + 1) % items_.size();
129 if (epoch_index_.compare_exchange_weak(index, new_index)) {
130 items_[new_index].epoch = epoch;
131 items_[(new_index + 1) % items_.size()].Reset(epoch + epoch_duration_);
140 std::size_t GetPreviousIndex(
int epochs_ago) {
141 int index =
static_cast<
int>(GetCurrentIndex()) - epochs_ago;
143 index += items_.size();
145 return index % items_.size();
148 Duration GetEpochForDuration(Duration duration)
const {
149 auto now = std::chrono::duration_cast<Duration>(duration);
150 return now - now % epoch_duration_;
153 static std::size_t GetSizeForDuration(Duration epoch_duration, Duration max_duration) {
155
156 return max_duration.count() / epoch_duration.count() + 3;
160 static constexpr bool kUseReset = detail::kCanReset<Counter>;
161 std::atomic<Duration> epoch;
164 EpochBucket() { Reset(Duration::min()); }
166 void Reset(Duration epoch_duration) {
167 epoch = epoch_duration;
168 if constexpr (kUseReset) {
176 const Duration epoch_duration_;
177 const Duration max_duration_;
178 mutable std::atomic_size_t epoch_index_;
179 mutable utils::FixedArray<EpochBucket> items_;
184void DumpMetric(Writer& writer,
const RecentPeriod<Counter, Result, Timer>& recent_period) {