-
Notifications
You must be signed in to change notification settings - Fork 83
/
Copy pathstatistic_impl.h
265 lines (233 loc) · 10 KB
/
statistic_impl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#pragma once
#include <memory>
#include <vector>
#include "nighthawk/common/statistic.h"
#include "external/dep_hdrhistogram_c/src/hdr_histogram.h"
#include "external/envoy/source/common/common/logger.h"
#include "external/envoy/source/common/stats/histogram_impl.h"
#include "source/common/frequency.h"
namespace Nighthawk {
/**
* Base class for all statistics implementations.
*/
class StatisticImpl : public Statistic, public Envoy::Logger::Loggable<Envoy::Logger::Id::main> {
public:
void addValue(uint64_t value) override;
std::string toString() const override;
nighthawk::client::Statistic toProto(SerializationDomain domain) const override;
std::string id() const override;
void setId(absl::string_view id) override;
uint64_t count() const override;
uint64_t max() const override;
uint64_t min() const override;
absl::StatusOr<std::unique_ptr<std::istream>> serializeNative() const override;
absl::Status deserializeNative(std::istream&) override;
protected:
std::string id_;
uint64_t min_{UINT64_MAX};
uint64_t max_{0};
uint64_t count_{0};
};
/**
* Dummy statistic for future use.
* Intended be plugged into the system as a no-op in cases where statistic tracking
* is not desired.
*/
class NullStatistic : public StatisticImpl {
public:
void addValue(uint64_t) override {}
double mean() const override { return 0.0; }
double pvariance() const override { return 0.0; }
double pstdev() const override { return 0.0; }
StatisticPtr combine(const Statistic&) const override { return createNewInstanceOfSameType(); };
uint64_t significantDigits() const override { return 0; }
StatisticPtr createNewInstanceOfSameType() const override {
return std::make_unique<NullStatistic>();
};
};
/**
* Simple statistic that keeps track of count/mean/pvariance/pstdev with low memory
* requirements, but the potential for errors due to catastrophic cancellation.
*/
class SimpleStatistic : public StatisticImpl {
public:
void addValue(uint64_t value) override;
double mean() const override;
double pvariance() const override;
double pstdev() const override;
StatisticPtr combine(const Statistic& statistic) const override;
uint64_t significantDigits() const override { return 8; }
StatisticPtr createNewInstanceOfSameType() const override {
return std::make_unique<SimpleStatistic>();
};
absl::StatusOr<std::unique_ptr<std::istream>> serializeNative() const override;
absl::Status deserializeNative(std::istream&) override;
private:
double sum_x_{0};
double sum_x2_{0};
};
/**
* Statistic that keeps track of count/mean/pvariance/pstdev with low memory
* requirements. Resistant to catastrophic cancellation and pretty accurate.
* Based on Donald Knuth's online variance computation algorithm:
* (Art of Computer Programming, Vol 2, page 232).
* Knuth attributes this algorithm to B. P. Welford.
* (Technometrics, Vol 4, No 3, Aug 1962 pp 419-420).
*/
class StreamingStatistic : public StatisticImpl {
public:
void addValue(uint64_t value) override;
double mean() const override;
double pvariance() const override;
double pstdev() const override;
StatisticPtr combine(const Statistic& statistic) const override;
bool resistsCatastrophicCancellation() const override { return true; }
StatisticPtr createNewInstanceOfSameType() const override {
return std::make_unique<StreamingStatistic>();
};
absl::StatusOr<std::unique_ptr<std::istream>> serializeNative() const override;
absl::Status deserializeNative(std::istream&) override;
private:
double mean_{0};
double accumulated_variance_{0};
};
/**
* InMemoryStatistic uses StreamingStatistic under the hood to compute statistics.
* Stores the raw latencies in-memory, which may accumulate to a lot
* of data(!). Not used right now, but useful for debugging purposes.
*/
class InMemoryStatistic : public StatisticImpl {
public:
InMemoryStatistic();
void addValue(uint64_t sample_value) override;
double mean() const override;
double pvariance() const override;
double pstdev() const override;
StatisticPtr combine(const Statistic& statistic) const override;
bool resistsCatastrophicCancellation() const override {
return streaming_stats_->resistsCatastrophicCancellation();
}
uint64_t significantDigits() const override { return streaming_stats_->significantDigits(); }
StatisticPtr createNewInstanceOfSameType() const override {
return std::make_unique<InMemoryStatistic>();
};
private:
std::vector<int64_t> samples_;
StatisticPtr streaming_stats_;
};
/**
* HdrStatistic uses HdrHistogram under the hood to compute statistics.
*/
class HdrStatistic : public StatisticImpl {
public:
HdrStatistic();
~HdrStatistic() override;
void addValue(uint64_t sample_value) override;
uint64_t count() const override;
double mean() const override;
double pvariance() const override;
double pstdev() const override;
uint64_t max() const override;
uint64_t min() const override;
StatisticPtr combine(const Statistic& statistic) const override;
nighthawk::client::Statistic toProto(SerializationDomain domain) const override;
uint64_t significantDigits() const override { return SignificantDigits; }
StatisticPtr createNewInstanceOfSameType() const override {
return std::make_unique<HdrStatistic>();
};
absl::StatusOr<std::unique_ptr<std::istream>> serializeNative() const override;
absl::Status deserializeNative(std::istream&) override;
private:
static const int SignificantDigits;
struct hdr_histogram* histogram_;
};
/**
* CircllhistStatistic uses Circllhist under the hood to compute statistics.
* Circllhist is used in the implementation of Envoy Histograms, compared to HdrHistogram it trades
* precision for fast performance in merge and insertion. For more info, please see
* https://github.com/circonus-labs/libcircllhist
*/
class CircllhistStatistic : public StatisticImpl {
public:
CircllhistStatistic();
~CircllhistStatistic() override;
void addValue(uint64_t value) override;
double mean() const override;
double pvariance() const override;
double pstdev() const override;
StatisticPtr combine(const Statistic& statistic) const override;
// circllhist has low significant digit precision as a result of base 10
// algorithm.
uint64_t significantDigits() const override { return 1; }
StatisticPtr createNewInstanceOfSameType() const override;
nighthawk::client::Statistic toProto(SerializationDomain domain) const override;
private:
histogram_t* histogram_;
};
/**
* In order to be able to flush a histogram value to downstream Envoy stats Sinks, abstract class
* SinkableStatistic takes the Scope reference in the constructor and wraps the
* Envoy::Stats::HistogramHelper interface. Implementation of sinkable Nighthawk Statistic class
* will inherit from this class.
*/
class SinkableStatistic : public Envoy::Stats::HistogramImplHelper {
public:
// Calling HistogramImplHelper(SymbolTable& symbol_table) constructor to construct an empty
// MetricImpl. This is to bypass the complicated logic of setting up SymbolTable/StatName in
// Envoy.
SinkableStatistic(Envoy::Stats::Scope& scope, absl::optional<int> worker_id);
~SinkableStatistic() override;
// Currently Envoy Histogram Unit supports {Unspecified, Bytes, Microseconds, Milliseconds}. By
// default, Nighthawk::Statistic uses nanosecond as the unit of latency histograms, so Unspecified
// is returned here to isolate Nighthawk Statistic from Envoy Histogram Unit.
Envoy::Stats::Histogram::Unit unit() const override;
Envoy::Stats::SymbolTable& symbolTable() override;
// Return the id of the worker where this statistic is defined. Per worker
// statistic should always set worker_id. Return absl::nullopt when the
// statistic is not defined per worker.
const absl::optional<int> worker_id() const { return worker_id_; }
protected:
// This is used in child class for delivering the histogram data to sinks.
Envoy::Stats::Scope& scope_;
private:
// worker_id can be used in downstream stats Sinks as the stats tag.
absl::optional<int> worker_id_;
};
// Implementation of sinkable Nighthawk Statistic with HdrHistogram.
class SinkableHdrStatistic : public SinkableStatistic, public HdrStatistic {
public:
// The constructor takes the Scope reference which is used to flush a histogram value to
// downstream stats Sinks through deliverHistogramToSinks().
SinkableHdrStatistic(Envoy::Stats::Scope& scope, absl::optional<int> worker_id = absl::nullopt);
// Envoy::Stats::Histogram
void recordValue(uint64_t value) override;
bool used() const override { return count() > 0; }
bool hidden() const override { return false; }
// Overriding name() to return Nighthawk::Statistic::id().
std::string name() const override { return id(); }
// Overriding tagExtractedName() to return string(worker_id) + "." + Nighthawk::Statistic::id()
// when worker_id is set. The worker_id prefix can be used in customized stats sinks.
std::string tagExtractedName() const override;
// Nighthawk::Statistic
void addValue(uint64_t value) override { recordValue(value); }
};
// Implementation of sinkable Nighthawk Statistic with Circllhist Histogram.
class SinkableCircllhistStatistic : public SinkableStatistic, public CircllhistStatistic {
public:
// The constructor takes the Scope reference which is used to flush a histogram value to
// downstream stats Sinks through deliverHistogramToSinks().
SinkableCircllhistStatistic(Envoy::Stats::Scope& scope,
absl::optional<int> worker_id = absl::nullopt);
// Envoy::Stats::Histogram
void recordValue(uint64_t value) override;
bool used() const override { return count() > 0; }
bool hidden() const override { return false; }
// Overriding name() to return Nighthawk::Statistic::id().
std::string name() const override { return id(); }
// Overriding tagExtractedName() to return string(worker_id) + "." + Nighthawk::Statistic::id()
// when worker_id is set. The worker_id prefix can be used in customized stats sinks.
std::string tagExtractedName() const override;
// Nighthawk::Statistic
void addValue(uint64_t value) override { recordValue(value); }
};
} // namespace Nighthawk