Skip to content

Commit 186e60d

Browse files
uvdn7facebook-github-bot
authored andcommitted
Change all fb303 timeseries to use millisecond duration
Summary: This diff changes the clock for all fb303 timeseries to be of millisecond granularity. MultiLevelTimeSeries is also used for fb303 historgram, its clock type is left unchanged to keep this diff small. The fb303 timeseries test is carefully updated to ensure the rate calculation is per-second instead of per-Duration (which is the default) for all fb303 timeseries, and captures other existing nuances from bucketed timeseries that are not obvious before due to its clock granularity. A few things are temporarily introduced * `fb303::ExportedStatForHistogram` which is the MultiLevelTimeSeries with second granularity still used for fb303 histogram * `fb303::MultiLevelTimeSeries` still takes a CT template argument instead of hard-coding to have millisecond clock because it needs to support both timeseries (moving to second granularity clock in this diff) and histogram (staying on second granularity clock temporarily) The change should be a no-op for services because all clients are still adding value to the timeseries using a clock with second granularity. That being said, the underlying timeseires will have more buckets now especially for the small timeseires (e.g. 1s, 5s). It would have a small memory regression. We will measure the memory regression with zippydb, which has the most number of timeseries per server process. The default timeseries starts from minute (https://fburl.com/code/ia0wbvhb), so the overall memory regression should not be a major concern. For 1s timeseries the memory regression is only 59 (buckets) * 16 bytes (8 bytes for sum, and 8 bytes for count per bucket). D82152395 has more info about the impact and why we are making this change. #buildall Reviewed By: islamismailov Differential Revision: D80005386 fbshipit-source-id: 1d378ee32fd5b087e8445c482bedbb8e6ec6b45d
1 parent e4a6750 commit 186e60d

8 files changed

Lines changed: 367 additions & 146 deletions

fb303/ExportedHistogramMap.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ namespace facebook {
3030
namespace fb303 {
3131

3232
using ExportedHistogram = TimeseriesHistogram<CounterType>;
33-
using ExportedStat = MultiLevelTimeSeries<CounterType>;
33+
using ExportedStatForHistogram = MultiLevelTimeSeries<
34+
CounterType,
35+
folly::LegacyStatsClock<std::chrono::seconds>>;
3436

3537
/**
3638
* class ExportedHistogramMap
@@ -95,23 +97,25 @@ class ExportedHistogramMap {
9597
/**
9698
* Set defaultStat_ field.
9799
*/
98-
void setDefaultStat(const ExportedStat& defaultStat) {
99-
*defaultStat_.wlock() = std::make_shared<ExportedStat>(defaultStat);
100+
void setDefaultStat(const ExportedStatForHistogram& defaultStat) {
101+
*defaultStat_.wlock() =
102+
std::make_shared<ExportedStatForHistogram>(defaultStat);
100103
}
101104

102105
/**
103106
* Set defaultStat_ field.
104107
*/
105-
void setDefaultStatPtr(std::shared_ptr<ExportedStat> defaultStat) {
108+
void setDefaultStatPtr(
109+
std::shared_ptr<ExportedStatForHistogram> defaultStat) {
106110
*defaultStat_.wlock() = std::move(defaultStat);
107111
}
108112

109-
[[deprecated("Use getDefaultStatPtr")]] const ExportedStat& getDefaultStat()
110-
const {
113+
[[deprecated("Use getDefaultStatPtr")]] const ExportedStatForHistogram&
114+
getDefaultStat() const {
111115
return **defaultStat_.rlock();
112116
}
113117

114-
std::shared_ptr<ExportedStat> getDefaultStatPtr() const {
118+
std::shared_ptr<ExportedStatForHistogram> getDefaultStatPtr() const {
115119
return *defaultStat_.rlock();
116120
}
117121

@@ -407,13 +411,13 @@ class ExportedHistogramMap {
407411
DynamicCounters* dynamicCounters_;
408412
DynamicStrings* dynamicStrings_;
409413
folly::Synchronized<std::shared_ptr<ExportedHistogram>> defaultHist_;
410-
// note: We slice defaultStat by copying it to a ExportedStat
414+
// note: We slice defaultStat by copying it to a ExportedStatForHistogram
411415
// (non-pointer, non-reference), but that's according to plan: the
412416
// derived classes only set data members in the base class, nothing
413417
// more (they have no data members of their own).
414-
folly::Synchronized<std::shared_ptr<ExportedStat>> defaultStat_{
415-
std::make_shared<ExportedStat>(
416-
MinuteTenMinuteHourTimeSeries<CounterType>())};
418+
folly::Synchronized<std::shared_ptr<ExportedStatForHistogram>> defaultStat_{
419+
std::make_shared<ExportedStatForHistogram>(
420+
HistogramMinuteTenMinuteHourTimeSeries<CounterType>())};
417421
};
418422

419423
// Lock the histogram and calculate the percentile

fb303/ThreadCachedServiceData.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ struct HistogramSpec {
373373
int64_t max;
374374
std::vector<ExportType> stats;
375375
std::vector<int> percentiles;
376-
MultiLevelTimeSeries<CounterType> levels;
376+
ExportedStatForHistogram levels;
377377

378378
template <typename... Args>
379379
HistogramSpec(
@@ -384,7 +384,7 @@ struct HistogramSpec {
384384
: bucketWidth(bucketWidth_),
385385
min(min_),
386386
max(max_),
387-
levels(MinuteTenMinuteHourTimeSeries<CounterType>()) {
387+
levels(HistogramMinuteTenMinuteHourTimeSeries<CounterType>()) {
388388
ctorHandleArgs(args...);
389389
}
390390

@@ -409,7 +409,7 @@ struct HistogramSpec {
409409
this->percentiles.push_back(pctile);
410410
}
411411

412-
void ctorHandleArg(const MultiLevelTimeSeries<CounterType>& levels_) {
412+
void ctorHandleArg(const ExportedStatForHistogram& levels_) {
413413
this->levels = levels_;
414414
}
415415

@@ -1211,7 +1211,9 @@ class MultiLevelTimeseriesWrapper : public TimeseriesWrapperBase {
12111211
key,
12121212
60,
12131213
sizeof...(LevelDurations),
1214-
std::array{std::chrono::seconds{LevelDurations}...}.data());
1214+
std::array{std::chrono::duration_cast<std::chrono::milliseconds>(
1215+
std::chrono::seconds{LevelDurations})...}
1216+
.data());
12151217
}
12161218

12171219
private:
@@ -1225,7 +1227,9 @@ class MultiLevelTimeseriesWrapper : public TimeseriesWrapperBase {
12251227
static const folly::Indestructible<MultiLevelTimeSeries<CounterType>> obj(
12261228
sizeof...(LevelDurations),
12271229
60,
1228-
std::array{std::chrono::seconds{LevelDurations}...}.data());
1230+
std::array{std::chrono::duration_cast<std::chrono::milliseconds>(
1231+
std::chrono::seconds{LevelDurations})...}
1232+
.data());
12291233
return *obj.get();
12301234
}
12311235
};
@@ -1290,7 +1294,7 @@ class MinuteOnlyHistogram : public HistogramWrapper {
12901294
min,
12911295
max,
12921296
args...,
1293-
MinuteOnlyTimeSeries<CounterType>()) {}
1297+
HistogramMinuteOnlyTimeSeries<CounterType>()) {}
12941298
};
12951299

12961300
class SubminuteMinuteOnlyHistogram : public HistogramWrapper {
@@ -1308,7 +1312,7 @@ class SubminuteMinuteOnlyHistogram : public HistogramWrapper {
13081312
min,
13091313
max,
13101314
args...,
1311-
SubminuteMinuteOnlyTimeSeries<CounterType>()) {}
1315+
HistogramSubminuteMinuteOnlyTimeSeries<CounterType>()) {}
13121316
};
13131317

13141318
/**

fb303/Timeseries.h

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,29 @@
2121

2222
namespace facebook::fb303 {
2323

24+
template <typename T, typename CT>
25+
class MultiLevelTimeSeries;
26+
27+
namespace detail {
28+
template <typename Duration, typename ValueType, typename CLOCK>
29+
std::vector<Duration> getDurations(
30+
const MultiLevelTimeSeries<ValueType, CLOCK>& timeseries) {
31+
std::vector<Duration> res;
32+
size_t levels = timeseries.numLevels();
33+
res.reserve(levels);
34+
for (size_t i = 0; i < levels; ++i) {
35+
typename CLOCK::duration original = timeseries.getLevel(i).duration();
36+
Duration duration = std::chrono::duration_cast<Duration>(original);
37+
DCHECK(
38+
std::chrono::duration_cast<typename CLOCK::duration>(duration) ==
39+
original)
40+
<< "precision loss when converting MultiLevelTimeSeries to a different clock type";
41+
res.push_back(duration);
42+
}
43+
return res;
44+
}
45+
} // namespace detail
46+
2447
/** class MultiLevelTimeSeries
2548
*
2649
* This class has been moved to folly/stats/MultiLevelTimeSeries.h. This is left
@@ -50,10 +73,12 @@ namespace facebook::fb303 {
5073
*
5174
* @author Mark Rabkin (mrabkin@facebook.com)
5275
*/
53-
template <class T>
54-
class MultiLevelTimeSeries : public folly::MultiLevelTimeSeries<T> {
76+
template <
77+
class T,
78+
typename CT = folly::LegacyStatsClock<std::chrono::milliseconds>>
79+
class MultiLevelTimeSeries : public folly::MultiLevelTimeSeries<T, CT> {
5580
public:
56-
using BaseType = folly::MultiLevelTimeSeries<T>;
81+
using BaseType = folly::MultiLevelTimeSeries<T, CT>;
5782
using Duration = typename BaseType::Duration;
5883
using TimePoint = typename BaseType::TimePoint;
5984
// The legacy TimeType. The older code used this instead of Duration and
@@ -72,9 +97,49 @@ class MultiLevelTimeSeries : public folly::MultiLevelTimeSeries<T> {
7297
const Duration* durations)
7398
: MultiLevelTimeSeries(num_buckets, folly::Range(durations, num_levels)) {
7499
}
100+
101+
/*
102+
* Convert between MultiLevelTimeSeries of the same value type but different
103+
* clock type.
104+
*/
105+
template <typename CLOCK>
106+
explicit MultiLevelTimeSeries(
107+
const MultiLevelTimeSeries<T, CLOCK>& timeseries)
108+
: MultiLevelTimeSeries(
109+
timeseries.numBuckets(),
110+
detail::getDurations<Duration>(timeseries)) {}
111+
112+
/* convert all the rate to std::chrono::seconds to be backwards compatible */
113+
template <typename ReturnType = double>
114+
ReturnType rate(size_t level) const {
115+
return BaseType::template rate<ReturnType, std::chrono::seconds>(level);
116+
}
117+
118+
template <typename ReturnType = double>
119+
ReturnType rate(Duration duration) const {
120+
return BaseType::template rate<ReturnType, std::chrono::seconds>(duration);
121+
}
122+
123+
template <typename ReturnType = double>
124+
ReturnType rate(TimePoint start, TimePoint end) const {
125+
return BaseType::template rate<ReturnType, std::chrono::seconds>(
126+
start, end);
127+
}
128+
129+
template <typename ReturnType = double>
130+
ReturnType countRate(size_t level) const {
131+
return BaseType::template countRate<ReturnType, std::chrono::seconds>(
132+
level);
133+
}
134+
135+
template <typename ReturnType = double>
136+
ReturnType countRate(Duration duration) const {
137+
return BaseType::template countRate<ReturnType, std::chrono::seconds>(
138+
duration);
139+
}
75140
};
76141

77-
const std::chrono::seconds kMinuteDurations[] = {
142+
const std::chrono::milliseconds kMinuteDurations[] = {
78143
std::chrono::seconds(60),
79144
std::chrono::seconds(0)};
80145

@@ -91,7 +156,7 @@ class MinuteTimeSeries : public MultiLevelTimeSeries<T> {
91156
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kMinuteDurations) {}
92157
};
93158

94-
const std::chrono::seconds kMinuteHourDurations[] = {
159+
const std::chrono::milliseconds kMinuteHourDurations[] = {
95160
std::chrono::seconds(60),
96161
std::chrono::seconds(3600),
97162
std::chrono::seconds(0)};
@@ -110,7 +175,7 @@ class MinuteHourTimeSeries : public MultiLevelTimeSeries<T> {
110175
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kMinuteHourDurations) {}
111176
};
112177

113-
const std::chrono::seconds kMinuteTenMinuteHourDurations[] = {
178+
const std::chrono::milliseconds kMinuteTenMinuteHourDurations[] = {
114179
std::chrono::seconds(60),
115180
std::chrono::seconds(600),
116181
std::chrono::seconds(3600),
@@ -132,7 +197,7 @@ class MinuteTenMinuteHourTimeSeries : public MultiLevelTimeSeries<T> {
132197
}
133198
};
134199

135-
const std::chrono::seconds kMinuteHourDayDurations[] = {
200+
const std::chrono::milliseconds kMinuteHourDayDurations[] = {
136201
std::chrono::seconds(60),
137202
std::chrono::seconds(3600),
138203
std::chrono::seconds(86400),
@@ -153,7 +218,7 @@ class MinuteHourDayTimeSeries : public MultiLevelTimeSeries<T> {
153218
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kMinuteHourDayDurations) {}
154219
};
155220

156-
const std::chrono::seconds kMinuteTenMinuteDurations[] = {
221+
const std::chrono::milliseconds kMinuteTenMinuteDurations[] = {
157222
std::chrono::seconds(60),
158223
std::chrono::seconds(600),
159224
std::chrono::seconds(0)};
@@ -172,7 +237,7 @@ class MinuteTenMinuteTimeSeries : public MultiLevelTimeSeries<T> {
172237
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kMinuteTenMinuteDurations) {}
173238
};
174239

175-
const std::chrono::seconds kTenMinuteHourDurations[] = {
240+
const std::chrono::milliseconds kTenMinuteHourDurations[] = {
176241
std::chrono::seconds(600),
177242
std::chrono::seconds(3600)};
178243

@@ -189,7 +254,7 @@ class TenMinuteHourTimeSeries : public MultiLevelTimeSeries<T> {
189254
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kTenMinuteHourDurations) {}
190255
};
191256

192-
const std::chrono::seconds kQuarterMinuteOnlyDurations[] = {
257+
const std::chrono::milliseconds kQuarterMinuteOnlyDurations[] = {
193258
std::chrono::seconds(15)};
194259

195260
template <class T>
@@ -204,7 +269,8 @@ class QuarterMinuteOnlyTimeSeries : public MultiLevelTimeSeries<T> {
204269
: MultiLevelTimeSeries<T>(NUM_LEVELS, 15, kQuarterMinuteOnlyDurations) {}
205270
};
206271

207-
const std::chrono::seconds kMinuteOnlyDurations[] = {std::chrono::seconds(60)};
272+
const std::chrono::milliseconds kMinuteOnlyDurations[] = {
273+
std::chrono::seconds(60)};
208274

209275
template <class T>
210276
class MinuteOnlyTimeSeries : public MultiLevelTimeSeries<T> {
@@ -218,7 +284,7 @@ class MinuteOnlyTimeSeries : public MultiLevelTimeSeries<T> {
218284
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kMinuteOnlyDurations) {}
219285
};
220286

221-
const std::chrono::seconds kTenMinuteOnlyDurations[] = {
287+
const std::chrono::milliseconds kTenMinuteOnlyDurations[] = {
222288
std::chrono::seconds(600)};
223289

224290
template <class T>
@@ -233,7 +299,7 @@ class TenMinuteOnlyTimeSeries : public MultiLevelTimeSeries<T> {
233299
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kTenMinuteOnlyDurations) {}
234300
};
235301

236-
const std::chrono::seconds kMinuteTenMinuteOnlyDurations[] = {
302+
const std::chrono::milliseconds kMinuteTenMinuteOnlyDurations[] = {
237303
std::chrono::seconds(60),
238304
std::chrono::seconds(600)};
239305

@@ -251,7 +317,7 @@ class MinuteTenMinuteOnlyTimeSeries : public MultiLevelTimeSeries<T> {
251317
}
252318
};
253319

254-
const std::chrono::seconds kHourDurations[] = {
320+
const std::chrono::milliseconds kHourDurations[] = {
255321
std::chrono::seconds(3600),
256322
std::chrono::seconds(0)};
257323

@@ -267,7 +333,7 @@ class HourTimeSeries : public MultiLevelTimeSeries<T> {
267333
HourTimeSeries() : MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kHourDurations) {}
268334
};
269335

270-
const std::chrono::seconds kTenMinutesChunksDurations[] = {
336+
const std::chrono::milliseconds kTenMinutesChunksDurations[] = {
271337
std::chrono::seconds(600),
272338
std::chrono::seconds(1200),
273339
std::chrono::seconds(1800)};
@@ -286,7 +352,7 @@ class TenMinutesChunksTimeSeries : public MultiLevelTimeSeries<T> {
286352
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kTenMinutesChunksDurations) {}
287353
};
288354

289-
const std::chrono::seconds kSubminuteMinuteDurations[] = {
355+
const std::chrono::milliseconds kSubminuteMinuteDurations[] = {
290356
std::chrono::seconds(5),
291357
std::chrono::seconds(10),
292358
std::chrono::seconds(20),
@@ -311,7 +377,7 @@ class SubminuteMinuteTimeSeries : public MultiLevelTimeSeries<T> {
311377
: MultiLevelTimeSeries<T>(NUM_LEVELS, 60, kSubminuteMinuteDurations) {}
312378
};
313379

314-
const std::chrono::seconds kSubminuteMinuteOnlyDurations[] = {
380+
const std::chrono::milliseconds kSubminuteMinuteOnlyDurations[] = {
315381
std::chrono::seconds(5),
316382
std::chrono::seconds(10),
317383
std::chrono::seconds(20),

fb303/TimeseriesExporter.cpp

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -80,44 +80,6 @@ CounterType TimeseriesExporter::getStatValue(
8080
LOG(FATAL) << "invalid stat export type: " << type;
8181
}
8282

83-
/* static */
84-
void TimeseriesExporter::getCounterName(
85-
char* counterName,
86-
const int counterNameSize,
87-
const ExportedStat* stat,
88-
StringPiece statName,
89-
ExportType type,
90-
const int level) {
91-
// NOTE: We access the stat object here without locking. This depends
92-
// on the fact that getLevel(), and Level::isAllTime() and
93-
// Level::duration() are all non-volatile calls meaning they only read things
94-
// that are constant once the stat is constructed (number of levels can never
95-
// change, nor their durations).
96-
// - mrabkin
97-
if (stat->getLevel(level).isAllTime()) {
98-
// typical name: 'ad_request.rate' or 'ad_request_elapsed_time.avg'
99-
snprintf(
100-
counterName,
101-
counterNameSize,
102-
"%.*s.%s",
103-
static_cast<int>(statName.size()),
104-
statName.data(),
105-
TimeseriesExporter::getTypeString()[type]);
106-
} else {
107-
// typical name: 'ad_request.rate.600' or 'ad_request_elapsed_time.avg.3600'
108-
auto durationSecs =
109-
duration_cast<std::chrono::seconds>(stat->getLevel(level).duration());
110-
snprintf(
111-
counterName,
112-
counterNameSize,
113-
"%.*s.%s.%ld",
114-
static_cast<int>(statName.size()),
115-
statName.data(),
116-
TimeseriesExporter::getTypeString()[type],
117-
static_cast<long>(durationSecs.count()));
118-
}
119-
}
120-
12183
/* static */
12284
void TimeseriesExporter::exportStat(
12385
const StatPtr& stat,

0 commit comments

Comments
 (0)