-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[AMLII-2019] Max samples per context for Histogram, Distribution and …
…Timing metrics (Experimental Feature) (#863) This experimental feature allows the user to limit the number of samples per context for histogram, distribution, and timing metrics. This can be enabled with the statsd_max_samples_per_context flag. When enabled up to n samples will be kept in per context for Histogram, Distribution and Timing metrics when Aggregation is enabled. The default value is 0 which means no limit.
- Loading branch information
1 parent
5482cf4
commit 830c7b3
Showing
8 changed files
with
337 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import random | ||
from datadog.dogstatsd.metric_types import MetricType | ||
from datadog.dogstatsd.metrics import MetricAggregator | ||
from threading import Lock | ||
|
||
|
||
class MaxSampleMetric(object): | ||
def __init__(self, name, tags, metric_type, specified_rate=1.0, max_metric_samples=0): | ||
self.name = name | ||
self.tags = tags | ||
self.lock = Lock() | ||
self.metric_type = metric_type | ||
self.max_metric_samples = max_metric_samples | ||
self.specified_rate = specified_rate | ||
self.data = [None] * max_metric_samples if max_metric_samples > 0 else [] | ||
self.stored_metric_samples = 0 | ||
self.total_metric_samples = 0 | ||
|
||
def sample(self, value): | ||
if self.max_metric_samples == 0: | ||
self.data.append(value) | ||
else: | ||
self.data[self.stored_metric_samples] = value | ||
self.stored_metric_samples += 1 | ||
self.total_metric_samples += 1 | ||
|
||
def maybe_keep_sample_work_unsafe(self, value): | ||
if self.max_metric_samples > 0: | ||
self.total_metric_samples += 1 | ||
if self.stored_metric_samples < self.max_metric_samples: | ||
self.data[self.stored_metric_samples] = value | ||
self.stored_metric_samples += 1 | ||
else: | ||
i = random.randint(0, self.total_metric_samples - 1) | ||
if i < self.max_metric_samples: | ||
self.data[i] = value | ||
else: | ||
self.sample(value) | ||
|
||
def skip_sample(self): | ||
self.total_metric_samples += 1 | ||
|
||
def flush(self): | ||
rate = self.stored_metric_samples / self.total_metric_samples | ||
with self.lock: | ||
return [ | ||
MetricAggregator(self.name, self.tags, rate, self.metric_type, self.data[i]) | ||
for i in range(self.stored_metric_samples) | ||
] | ||
|
||
|
||
class HistogramMetric(MaxSampleMetric): | ||
def __init__(self, name, tags, rate=1.0, max_metric_samples=0): | ||
super(HistogramMetric, self).__init__(name, tags, MetricType.HISTOGRAM, rate, max_metric_samples) | ||
|
||
|
||
class DistributionMetric(MaxSampleMetric): | ||
def __init__(self, name, tags, rate=1.0, max_metric_samples=0): | ||
super(DistributionMetric, self).__init__(name, tags, MetricType.DISTRIBUTION, rate, max_metric_samples) | ||
|
||
|
||
class TimingMetric(MaxSampleMetric): | ||
def __init__(self, name, tags, rate=1.0, max_metric_samples=0): | ||
super(TimingMetric, self).__init__(name, tags, MetricType.TIMING, rate, max_metric_samples) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from threading import Lock | ||
import random | ||
|
||
|
||
class MaxSampleMetricContexts: | ||
def __init__(self, max_sample_metric_type): | ||
self.lock = Lock() | ||
self.values = {} | ||
self.max_sample_metric_type = max_sample_metric_type | ||
|
||
def flush(self): | ||
metrics = [] | ||
"""Flush the metrics and reset the stored values.""" | ||
with self.lock: | ||
temp = self.values | ||
self.values = {} | ||
for _, metric in temp.items(): | ||
metrics.append(metric.flush()) | ||
return metrics | ||
|
||
def sample(self, name, value, tags, rate, context_key, max_samples_per_context): | ||
"""Sample a metric and store it if it meets the criteria.""" | ||
keeping_sample = self.should_sample(rate) | ||
with self.lock: | ||
if context_key not in self.values: | ||
# Create a new metric if it doesn't exist | ||
self.values[context_key] = self.max_sample_metric_type(name, tags, max_samples_per_context) | ||
metric = self.values[context_key] | ||
metric.lock.acquire() | ||
if keeping_sample: | ||
metric.maybe_keep_sample_work_unsafe(value) | ||
else: | ||
metric.skip_sample() | ||
metric.lock.release() | ||
|
||
def should_sample(self, rate): | ||
"""Determine if a sample should be kept based on the specified rate.""" | ||
if rate >= 1: | ||
return True | ||
return random.random() < rate |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,6 @@ class MetricType: | |
COUNT = "c" | ||
GAUGE = "g" | ||
SET = "s" | ||
HISTOGRAM = "h" | ||
DISTRIBUTION = "d" | ||
TIMING = "ms" |
Oops, something went wrong.