Fix copyright lines
[folly.git] / folly / stats / MultiLevelTimeSeries.h
index be75805bab1462bbb366b1ad3eacc43e6b6b5c0b..1818a947ca8476bdfcb184ed8267349e0d4b0b94 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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 {
 
@@ -48,12 +49,14 @@ 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.
@@ -66,9 +69,14 @@ class 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.
@@ -81,7 +89,9 @@ class MultiLevelTimeSeries {
   /*
    * 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.
@@ -90,8 +100,7 @@ class MultiLevelTimeSeries {
    * 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];
   }
@@ -104,7 +113,7 @@ class MultiLevelTimeSeries {
    * 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;
@@ -119,10 +128,30 @@ class MultiLevelTimeSeries {
     }
     // 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.
    *
@@ -130,7 +159,7 @@ class MultiLevelTimeSeries {
    * 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();
   }
 
@@ -145,8 +174,8 @@ class MultiLevelTimeSeries {
    * 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>();
   }
 
@@ -158,8 +187,8 @@ class MultiLevelTimeSeries {
    * 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>();
   }
 
@@ -170,7 +199,7 @@ class MultiLevelTimeSeries {
    * 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();
   }
 
@@ -181,11 +210,87 @@ class MultiLevelTimeSeries {
    * 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.
@@ -204,51 +309,51 @@ class MultiLevelTimeSeries {
    * 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);
   }
 
@@ -265,18 +370,19 @@ class MultiLevelTimeSeries {
    * 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
@@ -286,7 +392,7 @@ class MultiLevelTimeSeries {
    * 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
@@ -299,17 +405,36 @@ class MultiLevelTimeSeries {
    */
   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