Merge lp:~lucio.torre/txstatsd/use-uniform-sampling into lp:txstatsd

Proposed by Lucio Torre
Status: Merged
Approved by: Sidnei da Silva
Approved revision: 91
Merged at revision: 91
Proposed branch: lp:~lucio.torre/txstatsd/use-uniform-sampling
Merge into: lp:txstatsd
Diff against target: 278 lines (+71/-79)
4 files modified
txstatsd/metrics/timermetric.py (+22/-29)
txstatsd/server/configurableprocessor.py (+2/-1)
txstatsd/tests/metrics/test_timermetric.py (+6/-22)
txstatsd/tests/test_configurableprocessor.py (+41/-27)
To merge this branch: bzr merge lp:~lucio.torre/txstatsd/use-uniform-sampling
Reviewer Review Type Date Requested Status
Sidnei da Silva Approve
Review via email: mp+106880@code.launchpad.net

Commit message

Drop exponential sampling for uniform sampling.

Description of the change

drop exponential sampling for uniform sampling.
Between each report() call we create a bucket where we sample uniformly from the values seen in the period.
This will produce more spiky metrics but with more information and an more exact rate, but also means dropping Xmin_rate.

also, cheaper on the cpu.

To post a comment you must log in.
Revision history for this message
Sidnei da Silva (sidnei) wrote :

Looks good, works great. Not much improvement in CPU, but meh. +1!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'txstatsd/metrics/timermetric.py'
2--- txstatsd/metrics/timermetric.py 2012-02-09 02:02:42 +0000
3+++ txstatsd/metrics/timermetric.py 2012-05-22 19:16:23 +0000
4@@ -6,8 +6,8 @@
5 from txstatsd.metrics.histogrammetric import HistogramMetricReporter
6 from txstatsd.metrics.metermetric import MeterMetricReporter
7 from txstatsd.metrics.metric import Metric
8-from txstatsd.stats.exponentiallydecayingsample \
9- import ExponentiallyDecayingSample
10+from txstatsd.stats.uniformsample \
11+ import UniformSample
12
13
14 class TimerMetric(Metric):
15@@ -69,30 +69,25 @@
16 prefix += "."
17 self.prefix = prefix
18
19- sample = ExponentiallyDecayingSample(1028, 0.015)
20+ sample = UniformSample(1028)
21 self.histogram = HistogramMetricReporter(sample)
22- self.meter = MeterMetricReporter(
23- "calls", wall_time_func=self.wall_time_func)
24+ # total number of values seen
25+ self.count = 0
26 self.clear()
27
28- def clear(self):
29+ def clear(self, timestamp=None):
30 """Clears all recorded durations."""
31 self.histogram.clear()
32-
33- def count(self):
34- return self.histogram.count
35-
36- def fifteen_minute_rate(self):
37- return self.meter.fifteen_minute_rate()
38-
39- def five_minute_rate(self):
40- return self.meter.five_minute_rate()
41-
42- def mean_rate(self):
43- return self.meter.mean_rate()
44-
45- def one_minute_rate(self):
46- return self.meter.one_minute_rate()
47+ if timestamp is None:
48+ timestamp = self.wall_time_func()
49+ self.last_time = float(timestamp)
50+
51+ def rate(self, timestamp):
52+ """The number of values seen since last clear."""
53+ dt = (timestamp - self.last_time)
54+ if dt == 0:
55+ return 0
56+ return self.histogram.count / dt
57
58 def max(self):
59 """Returns the longest recorded duration."""
60@@ -132,13 +127,12 @@
61
62 @param duration: The length of the duration in seconds.
63 """
64+ self.count += 1
65 if duration >= 0:
66 self.histogram.update(duration)
67- self.meter.mark()
68
69 def tick(self):
70- """Updates the moving averages."""
71- self.meter.tick()
72+ pass
73
74 def report(self, timestamp):
75 # median, 75, 95, 98, 99, 99.9 percentile
76@@ -150,12 +144,11 @@
77 ".stddev": self.std_dev(),
78 ".99percentile": percentiles[4],
79 ".999percentile": percentiles[5],
80- ".count": self.meter.count,
81- ".1min_rate": self.meter.one_minute_rate(),
82- ".5min_rate": self.meter.five_minute_rate(),
83- ".15min_rate": self.meter.fifteen_minute_rate()}
84-
85+ ".count": self.count,
86+ ".rate": self.rate(timestamp),
87+ }
88 for item, value in items.iteritems():
89 metrics.append((self.prefix + self.name + item,
90 round(value, 6), timestamp))
91+ self.clear(timestamp)
92 return metrics
93
94=== modified file 'txstatsd/server/configurableprocessor.py'
95--- txstatsd/server/configurableprocessor.py 2012-02-07 21:11:42 +0000
96+++ txstatsd/server/configurableprocessor.py 2012-05-22 19:16:23 +0000
97@@ -40,7 +40,8 @@
98
99 def compose_timer_metric(self, key, duration):
100 if not key in self.timer_metrics:
101- metric = TimerMetricReporter(key, prefix=self.message_prefix)
102+ metric = TimerMetricReporter(key,
103+ wall_time_func=self.time_function, prefix=self.message_prefix)
104 self.timer_metrics[key] = metric
105 self.timer_metrics[key].update(duration)
106
107
108=== modified file 'txstatsd/tests/metrics/test_timermetric.py'
109--- txstatsd/tests/metrics/test_timermetric.py 2011-09-14 12:01:10 +0000
110+++ txstatsd/tests/metrics/test_timermetric.py 2012-05-22 19:16:23 +0000
111@@ -1,5 +1,6 @@
112
113 import math
114+import time
115
116 from twisted.trial.unittest import TestCase
117
118@@ -9,7 +10,6 @@
119 class TestBlankTimerMetric(TestCase):
120 def setUp(self):
121 self.timer = TimerMetricReporter('test')
122- self.timer.tick()
123
124 def test_max(self):
125 self.assertEqual(
126@@ -28,7 +28,7 @@
127
128 def test_count(self):
129 self.assertEqual(
130- self.timer.count(), 0,
131+ self.timer.count, 0,
132 'Should have a count of zero')
133
134 def test_std_dev(self):
135@@ -54,26 +54,11 @@
136 percentiles[4], 0,
137 'Should have p99.9 of zero')
138
139- def test_mean_rate(self):
140- self.assertEqual(
141- self.timer.mean_rate(), 0,
142- 'Should have a mean rate of zero')
143-
144- def test_one_minute_rate(self):
145- self.assertEqual(
146- self.timer.one_minute_rate(), 0,
147+ def test_rate(self):
148+ self.assertEqual(
149+ self.timer.rate(time.time()), 0,
150 'Should have a one-minute rate of zero`')
151
152- def test_five_minute_rate(self):
153- self.assertEqual(
154- self.timer.five_minute_rate(), 0,
155- 'Should have a five-minute rate of zero')
156-
157- def test_fifteen_minute_rate(self):
158- self.assertEqual(
159- self.timer.fifteen_minute_rate(), 0,
160- 'Should have a fifteen-minute rate of zero')
161-
162 def test_no_values(self):
163 self.assertEqual(
164 len(self.timer.get_values()), 0,
165@@ -83,7 +68,6 @@
166 class TestTimingSeriesEvents(TestCase):
167 def setUp(self):
168 self.timer = TimerMetricReporter('test')
169- self.timer.tick()
170 self.timer.update(10)
171 self.timer.update(20)
172 self.timer.update(20)
173@@ -92,7 +76,7 @@
174
175 def test_count(self):
176 self.assertEqual(
177- self.timer.count(), 5,
178+ self.timer.count, 5,
179 'Should record the count')
180
181 def test_min(self):
182
183=== modified file 'txstatsd/tests/test_configurableprocessor.py'
184--- txstatsd/tests/test_configurableprocessor.py 2012-05-14 19:18:27 +0000
185+++ txstatsd/tests/test_configurableprocessor.py 2012-05-22 19:16:23 +0000
186@@ -64,29 +64,40 @@
187 If a single timer with a single data point is present, all
188 percentiles will be set to the same value.
189 """
190+
191+ _now = 40
192+
193 configurable_processor = ConfigurableMessageProcessor(
194- time_function=lambda: 42)
195+ time_function=lambda: _now)
196
197 configurable_processor.process("glork:24|ms")
198+ _now = 42
199+
200 messages = configurable_processor.flush()
201-
202- self.assertEqual(('glork.15min_rate', 0.0, 42), messages[0])
203- self.assertEqual(('glork.1min_rate', 0.0, 42), messages[1])
204- self.assertEqual(('glork.5min_rate', 0.0, 42), messages[2])
205- self.assertEqual(("glork.999percentile", 24.0, 42), messages[3])
206- self.assertEqual(("glork.99percentile", 24.0, 42), messages[4])
207- self.assertEqual(("glork.count", 1., 42), messages[5])
208- self.assertEqual(("glork.max", 24.0, 42), messages[6])
209- self.assertEqual(("glork.mean", 24.0, 42), messages[7])
210- self.assertEqual(("glork.min", 24.0, 42), messages[8])
211- self.assertEqual(("glork.stddev", 0.0, 42), messages[9])
212+ messages.sort()
213+
214+ expected = [
215+ ("glork.999percentile", 24.0, 42),
216+ ("glork.99percentile", 24.0, 42),
217+ ('glork.count', 1.0, 42),
218+ ("glork.max", 24.0, 42),
219+ ("glork.mean", 24.0, 42),
220+ ("glork.min", 24.0, 42),
221+ ('glork.rate', 0.5, 42),
222+ ("glork.stddev", 0.0, 42),
223+ ]
224+ expected.sort()
225+
226+ for e, f in zip(expected, messages):
227+ self.assertEqual(e, f)
228
229 def test_flush_single_timer_multiple_times(self):
230 """
231 Test reporting of multiple timer metric samples.
232 """
233+ _now = 40
234 configurable_processor = ConfigurableMessageProcessor(
235- time_function=lambda: 42)
236+ time_function=lambda: _now)
237
238 configurable_processor.process("glork:4|ms")
239 configurable_processor.update_metrics()
240@@ -101,21 +112,24 @@
241 configurable_processor.process("glork:42|ms")
242 configurable_processor.update_metrics()
243
244+ _now = 42
245 messages = configurable_processor.flush()
246-
247- self.assertEqual(('glork.15min_rate', 0.20000000000000001, 42),
248- messages[0])
249- self.assertEqual(('glork.1min_rate', 0.20000000000000001, 42),
250- messages[1])
251- self.assertEqual(('glork.5min_rate', 0.20000000000000001, 42),
252- messages[2])
253- self.assertEqual(("glork.999percentile", 42.0, 42), messages[3])
254- self.assertEqual(("glork.99percentile", 42.0, 42), messages[4])
255- self.assertEqual(('glork.count', 6.0, 42), messages[5])
256- self.assertEqual(("glork.max", 42.0, 42), messages[6])
257- self.assertEqual(("glork.mean", 18.0, 42), messages[7])
258- self.assertEqual(("glork.min", 4.0, 42), messages[8])
259- self.assertEqual(("glork.stddev", 13.490738, 42), messages[9])
260+ messages.sort()
261+
262+ expected = [
263+ ("glork.999percentile", 42.0, 42),
264+ ("glork.99percentile", 42.0, 42),
265+ ('glork.count', 6.0, 42),
266+ ("glork.max", 42.0, 42),
267+ ("glork.mean", 18.0, 42),
268+ ("glork.min", 4.0, 42),
269+ ('glork.rate', 3, 42),
270+ ("glork.stddev", 13.490738, 42),
271+ ]
272+ expected.sort()
273+
274+ for e, f in zip(expected, messages):
275+ self.assertEqual(e, f)
276
277
278 class FlushMeterMetricMessagesTest(TestCase):

Subscribers

People subscribed via source and target branches