9#include <userver/utils/datetime.hpp>
10#include <userver/utils/statistics/fwd.hpp>
11#include <userver/utils/statistics/recentperiod_detail.hpp>
13USERVER_NAMESPACE_BEGIN
15namespace utils::statistics {
18
19
20
21
22
23
24template <
typename Counter,
typename Result,
28 using Duration =
typename Timer::duration;
31 (detail::kResultWantsAddFunction<Result, Counter, Duration> ||
32 detail::kResultCanUseAddAssign<Result, Counter>),
33 "The Result template type argument must provide either Add(Counter, "
34 "Duration, Duration) function or add assignment operator");
36 static constexpr bool kUseAddFunction =
37 detail::kResultWantsAddFunction<Result, Counter, Duration>;
40
41
42
43
51 Counter& GetCurrentCounter() {
return items_[get_current_index()].counter; }
53 Counter& GetPreviousCounter(
int epochs_ago) {
54 return items_[get_previous_index(epochs_ago)].counter;
58
59
60
61
62
63
64
65
66
69 bool with_current_epoch =
false)
const {
70 if (duration == Duration::min()) {
71 duration = max_duration_;
75 Duration now = Timer::now().time_since_epoch();
76 Duration current_epoch = get_epoch_for_duration(now);
77 Duration start_epoch = current_epoch - duration;
78 Duration first_epoch_duration = now - current_epoch;
79 size_t index = epoch_index_.load();
81 for (size_t i = 0; i < items_.size();
82 i++, index = (index + items_.size() - 1) % items_.size()) {
83 Duration epoch = items_[index].epoch;
85 if (epoch > current_epoch)
continue;
86 if (epoch == current_epoch && !with_current_epoch)
continue;
87 if (epoch < start_epoch)
break;
89 if constexpr (kUseAddFunction) {
90 Duration this_epoch_duration =
91 (i == 0) ? first_epoch_duration : epoch_duration_;
93 Duration before_this_epoch_duration = epoch - start_epoch;
94 result.Add(items_[index].counter, this_epoch_duration,
95 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() { std::ignore = get_current_index(); }
111 for (
auto& item : items_) {
117 size_t get_current_index()
const {
119 Duration now = Timer::now().time_since_epoch();
120 Duration epoch = get_epoch_for_duration(now);
121 size_t index = epoch_index_.load();
122 Duration bucket_epoch = items_[index].epoch.load();
124 if (epoch != bucket_epoch) {
125 size_t new_index = (index + 1) % items_.size();
127 if (epoch_index_.compare_exchange_weak(index, new_index)) {
128 items_[new_index].epoch = epoch;
129 items_[(new_index + 1) % items_.size()].Reset();
138 size_t get_previous_index(
int epochs_ago) {
139 int index =
static_cast<
int>(get_current_index()) - epochs_ago;
140 while (index < 0) index += items_.size();
141 return index % items_.size();
144 Duration get_epoch_for_duration(Duration duration)
const {
145 auto now = std::chrono::duration_cast<Duration>(duration);
146 return now - now % epoch_duration_;
149 static size_t get_size_for_duration(Duration epoch_duration,
150 Duration max_duration) {
152
153 return max_duration.count() / epoch_duration.count() + 3;
157 static constexpr bool kUseReset = detail::kCanReset<Counter>;
158 std::atomic<Duration> epoch;
161 EpochBucket() { Reset(); }
164 epoch = Duration::min();
165 if constexpr (kUseReset) {
173 const Duration epoch_duration_;
174 const Duration max_duration_;
175 mutable std::atomic_size_t epoch_index_;
176 mutable std::vector<EpochBucket> items_;
180template <
typename Counter,
typename Result,
typename Timer>
181void DumpMetric(Writer& writer,
182 const RecentPeriod<Counter, Result, Timer>& recent_period) {
183 writer = recent_period.GetStatsForPeriod();
187template <
typename Counter,
typename Result,
typename Timer>
188void ResetMetric(
RecentPeriod<Counter, Result, Timer>& recent_period) {
189 recent_period.Reset();