Skip to content

Commit 57a4090

Browse files
Rustamchukru.nazarov
andauthored
sample prob sdk support (#35)
Co-authored-by: ru.nazarov <ru.nazarov@vkteam.ru>
1 parent 4115773 commit 57a4090

1 file changed

Lines changed: 83 additions & 3 deletions

File tree

statshouse.hpp

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ class string_view {
8282

8383
#endif
8484

85+
enum { TL_MAX_TINY_STRING_LEN = 253 };
86+
87+
inline string_view shorten(string_view x) {
88+
return string_view{x.data(), std::min(x.size(), static_cast<size_t>(TL_MAX_TINY_STRING_LEN))};
89+
}
90+
8591
namespace wyhash {
8692
// https://github.com/wangyi-fudan/wyhash
8793

@@ -409,7 +415,6 @@ class TransportUDPBase {
409415
TL_INT_SIZE = 4,
410416
TL_LONG_SIZE = 8,
411417
TL_DOUBLE_SIZE = 8,
412-
TL_MAX_TINY_STRING_LEN = 253,
413418
TL_BIG_STRING_LEN = 0xffffff,
414419
TL_BIG_STRING_MARKER = 0xfe,
415420
TL_STATSHOUSE_METRICS_BATCH_TAG = 0x56580239,
@@ -474,7 +479,6 @@ class TransportUDPBase {
474479
std::memcpy(&v64, &v, sizeof(v));
475480
return v64;
476481
}
477-
static string_view shorten(string_view x) { return string_view{x.data(), std::min<size_t>(x.size(), TL_MAX_TINY_STRING_LEN)}; }
478482
// Trim is VERY costly, 2x slowdown even when no actual trim performed. We do not want to punish good guys, and for bad guys
479483
// we have 'err_header_too_big' usage meta metric
480484
// static char * pack_string_trim(char * begin, const char * end, const char *str, size_t len) {
@@ -1366,9 +1370,27 @@ class Registry {
13661370
std::function<int (const char *)> logger;
13671371
bool incremental_flush_disabled{false};
13681372
bool sampling_disabled{false};
1373+
std::unordered_map<uint64_t, int> metric_sample_prob; // use add_metric_sample_prob
1374+
1375+
void add_metric_sample_prob(string_view metric_name, int keep_percent) {
1376+
const string_view sn = shorten(metric_name);
1377+
if (sn.size() == 0) {
1378+
return;
1379+
}
1380+
const uint64_t h = wyhash::wyhash(sn.data(), sn.size(), 0, wyhash::_wyp);
1381+
int p = keep_percent;
1382+
if (p < 0) {
1383+
p = 0;
1384+
}
1385+
if (p > 100) {
1386+
p = 100;
1387+
}
1388+
metric_sample_prob[h] = p;
1389+
}
13691390
};
13701391
explicit Registry(const options &o)
1371-
: max_bucket_size{o.max_bucket_size}
1392+
: metric_sample_prob(o.metric_sample_prob)
1393+
, max_bucket_size{o.max_bucket_size}
13721394
, time_external{0}
13731395
, metrics_logging_enabled{false}
13741396
, sampling_disabled{o.sampling_disabled}
@@ -1594,6 +1616,11 @@ class Registry {
15941616
}
15951617
explicit operator bool() const { return ptr.get() != nullptr; }
15961618
bool write_count(double count, uint32_t timestamp = 0) const {
1619+
const double m = registry->metric_sample_prob_mult(ptr->key);
1620+
if (m <= 0) {
1621+
return true;
1622+
}
1623+
count *= m;
15971624
registry->log_count(ptr->key, count, timestamp);
15981625
if (timestamp) {
15991626
std::lock_guard<std::mutex> transport_lock{registry->transport_mu};
@@ -1613,6 +1640,14 @@ class Registry {
16131640
return write_values(&value, 1, 1, timestamp);
16141641
}
16151642
bool write_values(const double *values, size_t values_count, double count = 0, uint32_t timestamp = 0) const {
1643+
const double m = registry->metric_sample_prob_mult(ptr->key);
1644+
if (m <= 0) {
1645+
return true;
1646+
}
1647+
if (count == 0) {
1648+
count = 1;
1649+
}
1650+
count *= m;
16161651
registry->log_values(ptr->key, values, values_count, count, timestamp);
16171652
auto sampling = count != 0 && count != values_count;
16181653
if (sampling || timestamp || values_count >= registry->max_bucket_size.load(std::memory_order_relaxed)) {
@@ -1633,6 +1668,11 @@ class Registry {
16331668
return write_unique(&value, 1, 1, timestamp);
16341669
}
16351670
bool write_unique(const uint64_t *values, size_t values_count, double count, uint32_t timestamp = 0) const {
1671+
const double m = registry->metric_sample_prob_mult(ptr->key);
1672+
if (m <= 0) {
1673+
return true;
1674+
}
1675+
count *= m;
16361676
registry->log_unique(ptr->key, values, values_count, count, timestamp);
16371677
auto sampling = count != 0 && count != values_count;
16381678
if (sampling || timestamp || values_count >= registry->max_bucket_size.load(std::memory_order_relaxed)) {
@@ -1699,6 +1739,11 @@ class Registry {
16991739
return WaterlevelMetricRef{registry, key};
17001740
}
17011741
bool write_count(double count, uint32_t timestamp = 0) const {
1742+
const double m = registry->metric_sample_prob_mult(key);
1743+
if (m <= 0) {
1744+
return true;
1745+
}
1746+
count *= m;
17021747
registry->log_count(key, count, timestamp);
17031748
if (timestamp) {
17041749
std::lock_guard<std::mutex> transport_lock{registry->transport_mu};
@@ -1711,6 +1756,14 @@ class Registry {
17111756
return write_values(&value, 1, 1, timestamp);
17121757
}
17131758
bool write_values(const double *values, size_t values_count, double count = 0, uint32_t timestamp = 0) const {
1759+
const double m = registry->metric_sample_prob_mult(key);
1760+
if (m <= 0) {
1761+
return true;
1762+
}
1763+
if (count == 0) {
1764+
count = 1;
1765+
}
1766+
count *= m;
17141767
registry->log_values(key, values, values_count, count, timestamp);
17151768
auto sampling = count != 0 && count != values_count;
17161769
if (sampling || timestamp || values_count >= registry->max_bucket_size.load(std::memory_order_relaxed)) {
@@ -1724,6 +1777,11 @@ class Registry {
17241777
return write_unique(&value, 1, 1, timestamp);
17251778
}
17261779
bool write_unique(const uint64_t *values, size_t values_count, double count, uint32_t timestamp = 0) const {
1780+
const double m = registry->metric_sample_prob_mult(key);
1781+
if (m <= 0) {
1782+
return true;
1783+
}
1784+
count *= m;
17271785
registry->log_unique(key, values, values_count, count, timestamp);
17281786
auto sampling = count != 0 && count != values_count;
17291787
if (sampling || timestamp || values_count >= registry->max_bucket_size.load(std::memory_order_relaxed)) {
@@ -2006,6 +2064,27 @@ class Registry {
20062064
uint32_t t{time_external};
20072065
return t != 0 ? t : static_cast<uint32_t>(std::time(nullptr));
20082066
}
2067+
double metric_sample_prob_mult(const TransportUDPBase::MetricBuilder &key) const {
2068+
if (metric_sample_prob.empty()) {
2069+
return 1;
2070+
}
2071+
const string_view name = key.metric_name();
2072+
const uint64_t h = wyhash::wyhash(name.data(), name.size(), 0, wyhash::_wyp);
2073+
const auto it = metric_sample_prob.find(h);
2074+
if (it == metric_sample_prob.end()) {
2075+
return 1;
2076+
}
2077+
int p = it->second;
2078+
thread_local uint64_t seed = 0;
2079+
if (!seed) {
2080+
seed = static_cast<uint64_t>(std::random_device{}()) | 1ull;
2081+
}
2082+
const int roll = static_cast<int>(wyhash::wy2u0k(wyhash::wyrand(&seed), 100));
2083+
if (roll >= p) {
2084+
return 0;
2085+
}
2086+
return 100.0 / static_cast<double>(p);
2087+
}
20092088
inline void log_count(const TransportUDPBase::MetricBuilder &key, double count, uint32_t timestamp) const {
20102089
if (!metrics_logging_enabled.load(std::memory_order_relaxed)) return;
20112090
log_message_writer w{};
@@ -2146,6 +2225,7 @@ class Registry {
21462225
std::atomic<size_t> freelist_size;
21472226
std::atomic<size_t> bucket_count;
21482227
};
2228+
std::unordered_map<uint64_t, int> metric_sample_prob;
21492229
std::mutex mu;
21502230
mutable std::mutex transport_mu;
21512231
std::atomic<size_t> max_bucket_size;

0 commit comments

Comments
 (0)