blob: 8a296c32258d2863061f87ccdfb61cfc24cab345 (
plain) (
tree)
|
|
#include "ewma.h"
#include "metric.h"
#include <atomic>
#include <cmath>
namespace NMonitoring {
namespace {
constexpr auto DEFAULT_INTERVAL = TDuration::Seconds(5);
const double ALPHA1 = 1. - std::exp(-double(DEFAULT_INTERVAL.Seconds())/60./1.);
const double ALPHA5 = 1. - std::exp(-double(DEFAULT_INTERVAL.Seconds())/60./5.);
const double ALPHA15 = 1. - std::exp(-double(DEFAULT_INTERVAL.Seconds())/60./15.);
class TExpMovingAverage final: public IExpMovingAverage {
public:
explicit TExpMovingAverage(IGauge* metric, double alpha, TDuration interval)
: Metric_{metric}
, Alpha_{alpha}
, Interval_{interval.Seconds()}
{
Y_VERIFY(metric != nullptr, "Passing nullptr metric is not allowed");
}
~TExpMovingAverage() override = default;
// This method NOT thread safe
void Tick() override {
const auto current = Uncounted_.fetch_and(0);
const double instantRate = double(current) / Interval_;
if (Y_UNLIKELY(!IsInitialized())) {
Metric_->Set(instantRate);
Init_ = true;
} else {
const double currentRate = Metric_->Get();
Metric_->Set(Alpha_ * (instantRate - currentRate) + currentRate);
}
}
void Update(i64 value) override {
Uncounted_ += value;
}
double Rate() const override {
return Metric_->Get();
}
void Reset() override {
Init_ = false;
Uncounted_ = 0;
}
private:
bool IsInitialized() const {
return Init_;
}
private:
std::atomic<i64> Uncounted_{0};
std::atomic<bool> Init_{false};
IGauge* Metric_{nullptr};
double Alpha_;
ui64 Interval_;
};
struct TFakeEwma: IExpMovingAverage {
void Tick() override {}
void Update(i64) override {}
double Rate() const override { return 0; }
void Reset() override {}
};
} // namespace
TEwmaMeter::TEwmaMeter()
: Ewma_{MakeHolder<TFakeEwma>()}
{
}
TEwmaMeter::TEwmaMeter(IExpMovingAveragePtr&& ewma)
: Ewma_{std::move(ewma)}
{
}
TEwmaMeter::TEwmaMeter(TEwmaMeter&& other) {
if (&other == this) {
return;
}
*this = std::move(other);
}
TEwmaMeter& TEwmaMeter::operator=(TEwmaMeter&& other) {
Ewma_ = std::move(other.Ewma_);
LastTick_.store(other.LastTick_);
return *this;
}
void TEwmaMeter::TickIfNeeded() {
constexpr ui64 INTERVAL_SECONDS = DEFAULT_INTERVAL.Seconds();
const auto now = TInstant::Now().Seconds();
ui64 old = LastTick_.load();
const auto secondsSinceLastTick = now - old;
if (secondsSinceLastTick > INTERVAL_SECONDS) {
// round to the interval grid
const ui64 newLast = now - (secondsSinceLastTick % INTERVAL_SECONDS);
if (LastTick_.compare_exchange_strong(old, newLast)) {
for (size_t i = 0; i < secondsSinceLastTick / INTERVAL_SECONDS; ++i) {
Ewma_->Tick();
}
}
}
}
void TEwmaMeter::Mark() {
TickIfNeeded();
Ewma_->Update(1);
}
void TEwmaMeter::Mark(i64 value) {
TickIfNeeded();
Ewma_->Update(value);
}
double TEwmaMeter::Get() {
TickIfNeeded();
return Ewma_->Rate();
}
IExpMovingAveragePtr OneMinuteEwma(IGauge* metric) {
return MakeHolder<TExpMovingAverage>(metric, ALPHA1, DEFAULT_INTERVAL);
}
IExpMovingAveragePtr FiveMinuteEwma(IGauge* metric) {
return MakeHolder<TExpMovingAverage>(metric, ALPHA5, DEFAULT_INTERVAL);
}
IExpMovingAveragePtr FiveteenMinuteEwma(IGauge* metric) {
return MakeHolder<TExpMovingAverage>(metric, ALPHA15, DEFAULT_INTERVAL);
}
IExpMovingAveragePtr CreateEwma(IGauge* metric, double alpha, TDuration interval) {
return MakeHolder<TExpMovingAverage>(metric, alpha, interval);
}
} // namespace NMonitoring
|