/*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef FOLLY_STATS_MULTILEVELTIMESERIES_H_
-#define FOLLY_STATS_MULTILEVELTIMESERIES_H_
+#pragma once
#include <chrono>
+#include <stdexcept>
#include <string>
#include <vector>
-#include <glog/logging.h>
+#include <folly/String.h>
#include <folly/stats/BucketedTimeSeries.h>
+#include <glog/logging.h>
namespace folly {
*
* The class is not thread-safe -- use your own synchronization!
*/
-template <typename VT, typename TT=std::chrono::seconds>
+template <typename VT, typename CT = LegacyStatsClock<std::chrono::seconds>>
class MultiLevelTimeSeries {
public:
- typedef VT ValueType;
- typedef TT TimeType;
- typedef folly::BucketedTimeSeries<ValueType, TimeType> Level;
+ using ValueType = VT;
+ using Clock = CT;
+ using Duration = typename Clock::duration;
+ using TimePoint = typename Clock::time_point;
+ using Level = folly::BucketedTimeSeries<ValueType, Clock>;
/*
* Create a new MultiLevelTimeSeries.
* be provided with a duration of '0' -- this will be an "all-time" level. If
* an all-time level is provided, it MUST be the last level present.
*/
- MultiLevelTimeSeries(size_t numBuckets,
- size_t numLevels,
- const TimeType levelDurations[]);
+ MultiLevelTimeSeries(
+ size_t numBuckets,
+ size_t numLevels,
+ const Duration levelDurations[]);
+
+ MultiLevelTimeSeries(
+ size_t numBuckets,
+ std::initializer_list<Duration> durations);
/*
* Return the number of buckets used to track time series at each level.
/*
* Return the number of levels tracked by MultiLevelTimeSeries.
*/
- size_t numLevels() const { return levels_.size(); }
+ size_t numLevels() const {
+ return levels_.size();
+ }
/*
* Get the BucketedTimeSeries backing the specified level.
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- const Level& getLevel(int level) const {
- CHECK(level >= 0);
+ const Level& getLevel(size_t level) const {
CHECK_LT(level, levels_.size());
return levels_[level];
}
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- const Level& getLevel(TimeType start) const {
+ const Level& getLevel(TimePoint start) const {
for (const auto& level : levels_) {
if (level.isAllTime()) {
return level;
}
// We should always have an all-time level, so this is never reached.
LOG(FATAL) << "No level of timeseries covers internval"
- << " from " << start.count() << " to now";
+ << " from " << start.time_since_epoch().count() << " to now";
return levels_.back();
}
+ /*
+ * Get the BucketedTimeSeries backing the specified level.
+ *
+ * Note: you should generally call update() or flush() before accessing the
+ * data. Otherwise you may be reading stale data if update() or flush() has
+ * not been called recently.
+ */
+ const Level& getLevelByDuration(Duration duration) const {
+ // since the number of levels is expected to be small (less than 5 in most
+ // cases), a simple linear scan would be efficient and is intentionally
+ // chosen here over other alternatives for lookup.
+ for (const auto& level : levels_) {
+ if (level.duration() == duration) {
+ return level;
+ }
+ }
+ throw std::out_of_range(folly::to<std::string>(
+ "No level of duration ", duration.count(), " found"));
+ }
+
/*
* Return the sum of all the data points currently tracked at this level.
*
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- ValueType sum(int level) const {
+ ValueType sum(size_t level) const {
return getLevel(level).sum();
}
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- template <typename ReturnType=double>
- ReturnType avg(int level) const {
+ template <typename ReturnType = double>
+ ReturnType avg(size_t level) const {
return getLevel(level).template avg<ReturnType>();
}
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- template <typename ReturnType=double, typename Interval=TimeType>
- ReturnType rate(int level) const {
+ template <typename ReturnType = double, typename Interval = Duration>
+ ReturnType rate(size_t level) const {
return getLevel(level).template rate<ReturnType, Interval>();
}
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- int64_t count(int level) const {
+ uint64_t count(size_t level) const {
return getLevel(level).count();
}
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- template <typename ReturnType=double, typename Interval=TimeType>
- ReturnType countRate(int level) const {
+ template <typename ReturnType = double, typename Interval = Duration>
+ ReturnType countRate(size_t level) const {
return getLevel(level).template countRate<ReturnType, Interval>();
}
+ /*
+ * Return the sum of all the data points currently tracked at this level.
+ *
+ * This method is identical to sum(size_t level) above but takes in the
+ * duration that the user is interested in querying as the parameter.
+ *
+ * Note: you should generally call update() or flush() before accessing the
+ * data. Otherwise you may be reading stale data if update() or flush() has
+ * not been called recently.
+ */
+ ValueType sum(Duration duration) const {
+ return getLevelByDuration(duration).sum();
+ }
+
+ /*
+ * Return the average (sum / count) of all the data points currently tracked
+ * at this level.
+ *
+ * This method is identical to avg(size_t level) above but takes in the
+ * duration that the user is interested in querying as the parameter.
+ *
+ * Note: you should generally call update() or flush() before accessing the
+ * data. Otherwise you may be reading stale data if update() or flush() has
+ * not been called recently.
+ */
+ template <typename ReturnType = double>
+ ReturnType avg(Duration duration) const {
+ return getLevelByDuration(duration).template avg<ReturnType>();
+ }
+
+ /*
+ * Return the rate (sum divided by elaspsed time) of the all data points
+ * currently tracked at this level.
+ *
+ * This method is identical to rate(size_t level) above but takes in the
+ * duration that the user is interested in querying as the parameter.
+ *
+ * Note: you should generally call update() or flush() before accessing the
+ * data. Otherwise you may be reading stale data if update() or flush() has
+ * not been called recently.
+ */
+ template <typename ReturnType = double, typename Interval = Duration>
+ ReturnType rate(Duration duration) const {
+ return getLevelByDuration(duration).template rate<ReturnType, Interval>();
+ }
+
+ /*
+ * Return the number of data points currently tracked at this level.
+ *
+ * This method is identical to count(size_t level) above but takes in the
+ * duration that the user is interested in querying as the parameter.
+ *
+ * Note: you should generally call update() or flush() before accessing the
+ * data. Otherwise you may be reading stale data if update() or flush() has
+ * not been called recently.
+ */
+ uint64_t count(Duration duration) const {
+ return getLevelByDuration(duration).count();
+ }
+
+ /*
+ * Return the count divided by the elapsed time tracked at this level.
+ *
+ * This method is identical to countRate(size_t level) above but takes in the
+ * duration that the user is interested in querying as the parameter.
+ *
+ * Note: you should generally call update() or flush() before accessing the
+ * data. Otherwise you may be reading stale data if update() or flush() has
+ * not been called recently.
+ */
+ template <typename ReturnType = double, typename Interval = Duration>
+ ReturnType countRate(Duration duration) const {
+ return getLevelByDuration(duration)
+ .template countRate<ReturnType, Interval>();
+ }
+
/*
* Estimate the sum of the data points that occurred in the specified time
* period at this level.
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- ValueType sum(TimeType start, TimeType end) const {
+ ValueType sum(TimePoint start, TimePoint end) const {
return getLevel(start).sum(start, end);
}
/*
* Estimate the average value during the specified time period.
*
- * The same caveats documented in the sum(TimeType start, TimeType end)
+ * The same caveats documented in the sum(TimePoint start, TimePoint end)
* comments apply here as well.
*
* Note: you should generally call update() or flush() before accessing the
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- template <typename ReturnType=double>
- ReturnType avg(TimeType start, TimeType end) const {
+ template <typename ReturnType = double>
+ ReturnType avg(TimePoint start, TimePoint end) const {
return getLevel(start).template avg<ReturnType>(start, end);
}
/*
* Estimate the rate during the specified time period.
*
- * The same caveats documented in the sum(TimeType start, TimeType end)
+ * The same caveats documented in the sum(TimePoint start, TimePoint end)
* comments apply here as well.
*
* Note: you should generally call update() or flush() before accessing the
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- template <typename ReturnType=double>
- ReturnType rate(TimeType start, TimeType end) const {
+ template <typename ReturnType = double>
+ ReturnType rate(TimePoint start, TimePoint end) const {
return getLevel(start).template rate<ReturnType>(start, end);
}
/*
* Estimate the count during the specified time period.
*
- * The same caveats documented in the sum(TimeType start, TimeType end)
+ * The same caveats documented in the sum(TimePoint start, TimePoint end)
* comments apply here as well.
*
* Note: you should generally call update() or flush() before accessing the
* data. Otherwise you may be reading stale data if update() or flush() has
* not been called recently.
*/
- int64_t count(TimeType start, TimeType end) const {
+ uint64_t count(TimePoint start, TimePoint end) const {
return getLevel(start).count(start, end);
}
* addValue() or update(), now will be ignored and the latest timestamp will
* be used.
*/
- void addValue(TimeType now, const ValueType& val);
+ void addValue(TimePoint now, const ValueType& val);
/*
* Adds the value 'val' at time 'now' to all levels.
*/
- void addValue(TimeType now, const ValueType& val, int64_t times);
+ void addValue(TimePoint now, const ValueType& val, uint64_t times);
/*
- * Adds the value 'val' at time 'now' to all levels as the sum of 'nsamples'
- * samples.
+ * Adds the value 'total' at time 'now' to all levels as the sum of
+ * 'nsamples' samples.
*/
- void addValueAggregated(TimeType now, const ValueType& sum, int64_t nsamples);
+ void
+ addValueAggregated(TimePoint now, const ValueType& total, uint64_t nsamples);
/*
* Update all the levels to the specified time, doing all the necessary
* call update() before accessing the data. Otherwise you may be reading
* stale data if update() has not been called recently.
*/
- void update(TimeType now);
+ void update(TimePoint now);
/*
* Reset all the timeseries to an empty state as if no data points have ever
*/
void flush();
+ /*
+ * Legacy APIs that accept a Duration parameters rather than TimePoint.
+ *
+ * These treat the Duration as relative to the clock epoch.
+ * Prefer using the correct TimePoint-based APIs instead. These APIs will
+ * eventually be deprecated and removed.
+ */
+ void update(Duration now) {
+ update(TimePoint(now));
+ }
+ void addValue(Duration now, const ValueType& value) {
+ addValue(TimePoint(now), value);
+ }
+ void addValue(Duration now, const ValueType& value, uint64_t times) {
+ addValue(TimePoint(now), value, times);
+ }
+ void
+ addValueAggregated(Duration now, const ValueType& total, uint64_t nsamples) {
+ addValueAggregated(TimePoint(now), total, nsamples);
+ }
+
private:
std::vector<Level> levels_;
// Updates within the same time interval are cached
// They are flushed out when updates from a different time comes,
// or flush() is called.
- TimeType cachedTime_;
+ TimePoint cachedTime_;
ValueType cachedSum_;
- int cachedCount_;
+ uint64_t cachedCount_;
};
-} // folly
-
-#endif // FOLLY_STATS_MULTILEVELTIMESERIES_H_
+} // namespace folly