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();