7#include <userver/utils/datetime.hpp>
8#include <userver/utils/fixed_array.hpp>
9#include <userver/utils/statistics/fwd.hpp>
10#include <userver/utils/statistics/recentperiod_detail.hpp>
12USERVER_NAMESPACE_BEGIN
14namespace utils::statistics {
17
18
19
20
21
22
26 using Duration =
typename Timer::duration;
29 (detail::kResultWantsAddFunction<Result, Counter, Duration> || detail::kResultCanUseAddAssign<Result, Counter>),
30 "The Result template type argument must provide either Add(Counter, "
31 "Duration, Duration) function or add assignment operator"
34 static constexpr bool kUseAddFunction = detail::kResultWantsAddFunction<Result, Counter, Duration>;
37
38
39
40
47 Counter& GetCurrentCounter() {
return items_[GetCurrentIndex()].counter; }
49 Counter& GetPreviousCounter(
int epochs_ago) {
return items_[GetPreviousIndex(epochs_ago)].counter; }
52
53
54
55
56
57
58
59
60
62 const Result
GetStatsForPeriod(Duration duration = Duration::min(),
bool with_current_epoch =
false)
const {
63 if (duration == Duration::min()) {
64 duration = max_duration_;
68 Duration now = Timer::now().time_since_epoch();
69 Duration current_epoch = GetEpochForDuration(now);
70 Duration start_epoch = current_epoch - duration;
71 Duration first_epoch_duration = now - current_epoch;
72 std::size_t index = epoch_index_.load();
74 for (std::size_t i = 0; i < items_.size(); i++, index = (index + items_.size() - 1) % items_.size()) {
75 Duration epoch = items_[index].epoch;
77 if (epoch > current_epoch)
continue;
78 if (epoch == current_epoch && !with_current_epoch)
continue;
79 if (epoch < start_epoch)
break;
81 if constexpr (kUseAddFunction) {
82 Duration this_epoch_duration = (i == 0) ? first_epoch_duration : epoch_duration_;
84 Duration before_this_epoch_duration = epoch - start_epoch;
85 result.Add(items_[index].counter, this_epoch_duration, before_this_epoch_duration);
87 result += items_[index].counter;
94 Duration GetEpochDuration()
const {
return epoch_duration_; }
96 Duration GetMaxDuration()
const {
return max_duration_; }
98 void UpdateEpochIfOld() { [[maybe_unused]]
auto ignore = GetCurrentIndex(); }
101 for (
auto& item : items_) {
107 size_t GetCurrentIndex()
const {
109 Duration now = Timer::now().time_since_epoch();
110 Duration epoch = GetEpochForDuration(now);
111 std::size_t index = epoch_index_.load();
112 Duration bucket_epoch = items_[index].epoch.load();
114 if (epoch != bucket_epoch) {
115 std::size_t new_index = (index + 1) % items_.size();
117 if (epoch_index_.compare_exchange_weak(index, new_index)) {
118 items_[new_index].epoch = epoch;
119 items_[(new_index + 1) % items_.size()].Reset();
128 std::size_t GetPreviousIndex(
int epochs_ago) {
129 int index =
static_cast<
int>(GetCurrentIndex()) - epochs_ago;
130 while (index < 0) index += items_.size();
131 return index % items_.size();
134 Duration GetEpochForDuration(Duration duration)
const {
135 auto now = std::chrono::duration_cast<Duration>(duration);
136 return now - now % epoch_duration_;
139 static std::size_t GetSizeForDuration(Duration epoch_duration, Duration max_duration) {
141
142 return max_duration.count() / epoch_duration.count() + 3;
146 static constexpr bool kUseReset = detail::kCanReset<Counter>;
147 std::atomic<Duration> epoch;
150 EpochBucket() { Reset(); }
153 epoch = Duration::min();
154 if constexpr (kUseReset) {
162 const Duration epoch_duration_;
163 const Duration max_duration_;
164 mutable std::atomic_size_t epoch_index_;
165 mutable utils::FixedArray<EpochBucket> items_;
169template <
typename Counter,
typename Result,
typename Timer>
170void DumpMetric(Writer& writer,
const RecentPeriod<Counter, Result, Timer>& recent_period) {
171 writer = recent_period.GetStatsForPeriod();
175template <
typename Counter,
typename Result,
typename Timer>
176void ResetMetric(
RecentPeriod<Counter, Result, Timer>& recent_period) {
177 recent_period.Reset();