Ignore key order for APC bespokes
[hiphop-php.git] / hphp / util / service-data.h
blob17d60bbf11748d31f9c0f67456c5f70add5e9927
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #pragma once
19 #include <atomic>
20 #include <chrono>
21 #include <map>
22 #include <string>
23 #include <vector>
25 #include <folly/RWSpinLock.h>
26 #include <folly/Synchronized.h>
27 #include <folly/stats/Histogram.h>
28 #include <folly/stats/MultiLevelTimeSeries.h>
29 #include <folly/Optional.h>
31 #include "hphp/util/assertions.h"
33 namespace HPHP {
35 ///////////////////////////////////////////////////////////////////////////////
38 * A globally accessible statistics tracking facility. This can be used to keep
39 * track of internal runtime statistics in the form of flat counters, timeseries
40 * counters or histograms.
42 * ServiceData provides a globally accessible entry point to all the internal
43 * statistics. A 'statistic counter' of different types could be created by
44 * calling createCouter() createTimeSeries() or createHistogram(). The caller
45 * can then add values at different time points to the statistic counters. The
46 * statistic can then be retrieved and reported via the exportAll() call on
47 * ServiceData.
49 * Thread safety:
50 * ==============
51 * All functions in ServiceData namespace are thread safe. It is safe
52 * (and recommended) to cache the object returned by create...() methods and
53 * repeatedly add data points to it. It is safe to call create...() with the
54 * same name from multiple threads. In this case, only one object will be
55 * created and passed back to different threads.
57 * All objects returned by returned by the various create...() calls are thread
58 * safe. It is okay to add data points to it from multiple threads concurrently.
59 * These objects are internally synchronized with spin locks.
61 * Example Usage:
62 * ==============
63 * // create a flat counter named foo.
64 * auto counter = ServiceData::createCounter("foo");
65 * counter->increment();
67 * // create timeseries data named bar with default setting (avg value for the
68 * // last 1 minute, 10 minute, hour and all time).
69 * auto timeseries = ServiceData::createTimeSeries("bar");
70 * timeseries->addValue(3);
72 * // create a histogram with 10 buckets, min of 1, max of 100 and export the
73 * // 50th and 90th percentile value for reporting.
74 * auto histogram = ServiceData::createHistogram("blah", 10, 1, 100,
75 * {0.5, 0.9});
76 * histogram->addValue(10);
78 * // You can report the data like so.
79 * std::map<std::string, int64_t> statsMap;
80 * ServiceData::exportAll(statsMap);
82 * and statsMap will contain these keys:
84 * "foo"
85 * "bar.avg.60", "bar.avg.600", "bar.avg.3600", "bar.avg"
86 * "blah.hist.p50", "blah.hist.p90"
88 * Anti pattern:
89 * =============
90 * ServiceData::createCounter("foo")->increment(); // don't do this.
91 * Don't do this in performance critical code. You will incur the cost of a
92 * std::map look up whenever createCounter() is called. Rather, you should call
93 * ServiceData::createCounter("foo") just once, cache the returned pointer and
94 * repeatedly adding data points to it.
96 namespace ServiceData {
98 struct ExportedCounter;
99 struct ExportedHistogram;
100 struct ExportedTimeSeries;
102 namespace detail {
103 template <class ClassWithPrivateDestructor>
104 struct FriendDeleter;
107 enum class StatsType { AVG, SUM, RATE, COUNT, PCT };
110 * Create a flat counter named 'name'. Return an existing counter if it has
111 * already been created.
113 ExportedCounter* createCounter(const std::string& name);
116 * Callback-based counters
118 * Some counters have values that are updated much more frequently than data is
119 * requested from ServiceData, or there may not be a good location in the code
120 * to periodically update the value. For these cases, a callback-based counter
121 * may be used instead.
123 * registerCounterCallback() returns a unique handle that may be passed to
124 * deregisterCounterCallback() if the callback should be deregistered.
126 * The CounterCallback struct is provided as a convenience wrapper to manage
127 * the handle. The callback may be provided to its constructor, or to the
128 * init() function, if the callback isn't safe to call at CounterCallback's
129 * construction. Similarly, deinit() may be be called if the callback should be
130 * deregistered before CounterCallback's destruction.
132 * Once registered, callbacks must be safe to call at any time, from any
133 * thread, and may even be called before registerCounterCallback()
134 * returns. Callbacks are passed a reference to the std::map<std::string,
135 * int64_t> being populated, so one callback can add many counters (callbacks
136 * can also remove or modify existing counters, but this is discouraged).
138 using CounterFunc = std::function<void(std::map<std::string, int64_t>&)>;
139 using CounterHandle = uint32_t;
141 CounterHandle registerCounterCallback(CounterFunc func);
142 void deregisterCounterCallback(CounterHandle key);
144 struct CounterCallback {
145 CounterCallback() = default;
147 explicit CounterCallback(CounterFunc func) {
148 init(std::move(func));
151 ~CounterCallback() {
152 deinit();
155 void init(CounterFunc func) {
156 assertx(!m_key);
157 m_key = registerCounterCallback(std::move(func));
160 void deinit() {
161 if (m_key) {
162 deregisterCounterCallback(*m_key);
163 m_key = folly::none;
167 private:
168 folly::Optional<CounterHandle> m_key;
172 * Create a timeseries counter named 'name'. Return an existing one if it
173 * has already been created.
175 * Timeseries data is implemented as a number of buckets (buckted by time).
176 * As data point is added and time rolls forward, new bucket is created and
177 * the earliest bucket expires.
179 * We keep multiple of timeseries data at different granularity and update
180 * them simultaneously. This allows us to commute statistics at different
181 * levels. For example, we can simultaneously compute the avg of some counter
182 * value over the last 5 minutes, 10 minutes and hour. This is a similar
183 * concept to the load counters from the unix 'uptime' command.
185 * 'exportTypes' specifies what kind of statistics to export for each level.
186 * More export types can be added after the timeseries is created.
188 * 'levels' specifies at which granularity should the stats be tracked. The
189 * time duration must be strictly increasing. Special value '0' means all
190 * time and should always come last.
192 * 'numBuckets' specifies how many buckets to keep at each level. More buckets
193 * will produce more precise data at the expense of memory.
195 ExportedTimeSeries* createTimeSeries(
196 const std::string& name,
197 const std::vector<StatsType>& exportTypes =
198 std::vector<StatsType>{ StatsType::AVG },
199 const std::vector<std::chrono::seconds>& levels =
200 std::vector<std::chrono::seconds>{
201 std::chrono::seconds(60),
202 std::chrono::seconds(600),
203 std::chrono::seconds(3600),
204 std::chrono::seconds(0) /* all time */ },
205 int numBuckets = 60);
208 * Create a histogram counter named 'name'. Return an existing one if it has
209 * already been created.
211 * 'bucketSize' specifies how many buckets to track for the histogram.
212 * 'min' is the minimal value in the histogram.
213 * 'max' is the maximal value in the histogram.
214 * 'exportPercentile' specifies at what percentile values we should report the
215 * stat. A set of doubles between 0 and 1.0. For example, 0.5 means p50 and
216 * 0.99 means p99.
218 ExportedHistogram* createHistogram(
219 const std::string& name,
220 int64_t bucketSize,
221 int64_t min,
222 int64_t max,
223 const std::vector<double>& exportPercentile);
226 * Export all the statistics as simple key, value pairs.
228 void exportAll(std::map<std::string, int64_t>& statsMap);
231 * Export a specific counter by key name.
233 folly::Optional<int64_t> exportCounterByKey(const std::string& key);
235 // Interface for a flat counter. All methods are thread safe.
236 struct ExportedCounter {
237 ExportedCounter() : m_value(0) {}
238 void increment() { m_value.fetch_add(1, std::memory_order_relaxed); }
239 void decrement() { m_value.fetch_sub(1, std::memory_order_relaxed); }
240 void addValue(int64_t value) {
241 m_value.fetch_add(value, std::memory_order_relaxed);
243 void setValue(int64_t value) {
244 m_value.store(value, std::memory_order_relaxed);
246 int64_t getValue() const { return m_value.load(std::memory_order_relaxed); }
248 private:
249 friend struct detail::FriendDeleter<ExportedCounter>;
250 ~ExportedCounter() {}
252 std::atomic_int_fast64_t m_value;
255 // Interface for timeseries data. All methods are thread safe.
256 struct ExportedTimeSeries {
257 ExportedTimeSeries(int numBuckets,
258 const std::vector<std::chrono::seconds>& durations,
259 const std::vector<StatsType>& exportTypes);
261 void addValue(int64_t value);
262 void addValue(int64_t value, int64_t times);
263 void addValueAggregated(int64_t sum, int64_t nsamples);
265 int64_t getSum();
266 int64_t getRateByDuration(std::chrono::seconds duration);
268 folly::Optional<int64_t> getCounter(StatsType type, int seconds);
270 void exportAll(const std::string& prefix,
271 std::map<std::string, int64_t>& statsMap);
273 private:
274 friend struct detail::FriendDeleter<ExportedTimeSeries>;
275 ~ExportedTimeSeries() {}
277 folly::Synchronized<folly::MultiLevelTimeSeries<int64_t>,
278 folly::RWSpinLock > m_timeseries;
279 const std::vector<ServiceData::StatsType> m_exportTypes;
282 // Interface for histogram data. All methods are thread safe.
283 struct ExportedHistogram {
284 ExportedHistogram(int64_t bucketSize, int64_t min, int64_t max,
285 const std::vector<double>& exportPercentiles);
286 void addValue(int64_t value);
287 void removeValue(int64_t value);
288 void exportAll(const std::string& prefix,
289 std::map<std::string, int64_t>& statsMap);
291 private:
292 friend struct detail::FriendDeleter<ExportedHistogram>;
293 ~ExportedHistogram() {}
295 folly::Synchronized<folly::Histogram<int64_t>, folly::RWSpinLock> m_histogram;
296 const std::vector<double> m_exportPercentiles;
299 }; // namespace ServiceData
301 ///////////////////////////////////////////////////////////////////////////////
304 #include "hphp/util/service-data-inl.h"