From 5ef33da034829599bfb78e47f55acdbcf64713d0 Mon Sep 17 00:00:00 2001 From: Saju Varghese Date: Fri, 15 Nov 2024 08:18:15 -0800 Subject: [PATCH 1/2] feat: set sample rate to be min of global and metric sample rate --- README.md | 16 +++++++++++----- src/Client.php | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ef5ba13..1b14598 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,11 @@ When establishing the connection to statsd and sending metrics, errors will be s If you run statsd in TCP mode, there is also a `\Domnikl\Statsd\Connection\TcpSocket` adapter that works like the `UdpSocket` except that it throws a `\Domnikl\Statsd\Connection\TcpSocketException` if no connection could be established. Please consider that unlike UDP, TCP is used for reliable networks and therefor exceptions (and errors) will not be suppressed in TCP mode. +### Sampling Rate + +You can set global sampling rate when constructing instance of `Client`. If a metric call passes lower sampling rate than the global one, it would be +used instead. However if a metric call passes sampling rate higher than the global one, that would be ignored in favor of global sampling rate. + ### [Timings](https://github.com/etsy/statsd/blob/master/docs/metric_types.md#timing) ```php @@ -71,9 +76,9 @@ $statsd->memory('foo.memory_peak_usage'); ### [Gauges](https://github.com/etsy/statsd/blob/master/docs/metric_types.md#gauges) -statsd supports gauges, arbitrary values which can be recorded. +statsd supports gauges, arbitrary values which can be recorded. -This method accepts both absolute (3) and delta (+11) values. +This method accepts both absolute (3) and delta (+11) values. *NOTE:* Negative values are treated as delta values, not absolute. @@ -82,9 +87,9 @@ This method accepts both absolute (3) and delta (+11) values. // Absolute value $statsd->gauge('foobar', 3); -// Pass delta values as a string. +// Pass delta values as a string. // Accepts both positive (+11) and negative (-4) delta values. -$statsd->gauge('foobar', '+11'); +$statsd->gauge('foobar', '+11'); ``` ### [Sets](https://github.com/etsy/statsd/blob/master/docs/metric_types.md#sets) @@ -99,7 +104,7 @@ $statsd->set('userId', 1234); ### disabling sending of metrics To disable sending any metrics to the statsd server, you can use the `Domnikl\Statsd\Connection\Blackhole` connection -
class instead of the default socket abstraction. This may be incredibly useful for feature flags. Another options is +class instead of the default socket abstraction. This may be incredibly useful for feature flags. Another options is to use `Domnikl\Statsd\Connection\InMemory` connection class, that will collect your messages but won't actually send them. ## StatsdAwareInterface @@ -121,6 +126,7 @@ services: arguments: $connection: '@app.statsd_connection' $namespace: '' + $sampleRateAllMetrics: '0.1' # set global sample rate of 10% app.statsd_connection: class: Domnikl\Statsd\Connection\UdpSocket diff --git a/src/Client.php b/src/Client.php index 4520c25..2c2b8cb 100644 --- a/src/Client.php +++ b/src/Client.php @@ -255,7 +255,7 @@ private function send(string $key, $value, string $type, float $sampleRate, arra { // override sampleRate if all metrics should be sampled if ($this->sampleRateAllMetrics < 1) { - $sampleRate = $this->sampleRateAllMetrics; + $sampleRate = min($sampleRate, $this->sampleRateAllMetrics); } if ($sampleRate < 1 && mt_rand() / mt_getrandmax() > $sampleRate) { From ec03bd2197c405e36df178767a9e4672a2b89e57 Mon Sep 17 00:00:00 2001 From: Saju Varghese Date: Fri, 15 Nov 2024 08:58:01 -0800 Subject: [PATCH 2/2] chore: update tests around sampling rates --- tests/unit/ClientTest.php | 80 +++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/tests/unit/ClientTest.php b/tests/unit/ClientTest.php index 92bd409..7dbe98c 100644 --- a/tests/unit/ClientTest.php +++ b/tests/unit/ClientTest.php @@ -58,32 +58,42 @@ public function testCountWithFloatValue() ); } + public function sampleRateData() + { + return [ + [0.9, 1, '0.9'], + [0.9, 0.5, '0.5'], + ]; + } + /** + * @dataProvider sampleRateData * @group sampling */ - public function testCountWithSamplingRate() + public function testCountWithSamplingRate(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 9 / 10); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { - $client->count('foo.baz', 100, 1); + $client->count('foo.baz', 100, $sampleRate); } $this->assertEquals( - 'test.foo.baz:100|c|@0.9', + "test.foo.baz:100|c|@{$expectedSampleRate}", $this->connection->getLastMessage() ); } /** + * @dataProvider sampleRateData * @group sampling */ - public function testCountWithSamplingRateAndTags() + public function testCountWithSamplingRateAndTags(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 9 / 10); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { - $client->count('foo.baz', 100, 1, ['tag' => 'value']); + $client->count('foo.baz', 100, $sampleRate, ['tag' => 'value']); } $this->assertEquals( - 'test.foo.baz:100|c|@0.9|#tag:value', + "test.foo.baz:100|c|@{$expectedSampleRate}|#tag:value", $this->connection->getLastMessage() ); } @@ -98,31 +108,33 @@ public function testIncrement() } /** + * @dataProvider sampleRateData * @group sampling */ - public function testIncrementWithSamplingRate() + public function testIncrementWithSamplingRate(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 0.9); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { - $client->increment('foo.baz', 1); + $client->increment('foo.baz', $sampleRate); } $this->assertEquals( - 'test.foo.baz:1|c|@0.9', + "test.foo.baz:1|c|@{$expectedSampleRate}", $this->connection->getLastMessage() ); } /** + * @dataProvider sampleRateData * @group sampling */ - public function testIncrementWithSamplingRateAndTags() + public function testIncrementWithSamplingRateAndTags(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 0.9); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { - $client->increment('foo.baz', 1, ['tag' => 'value']); + $client->increment('foo.baz', $sampleRate, ['tag' => 'value']); } $this->assertEquals( - 'test.foo.baz:1|c|@0.9|#tag:value', + "test.foo.baz:1|c|@{$expectedSampleRate}|#tag:value", $this->connection->getLastMessage() ); } @@ -137,31 +149,33 @@ public function testDecrement() } /** + * @dataProvider sampleRateData * @group sampling */ - public function testDecrementWithSamplingRate() + public function testDecrementWithSamplingRate(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 0.9); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { - $client->decrement('foo.baz', 1); + $client->decrement('foo.baz', $sampleRate); } $this->assertEquals( - 'test.foo.baz:-1|c|@0.9', + "test.foo.baz:-1|c|@{$expectedSampleRate}", $this->connection->getLastMessage() ); } /** + * @dataProvider sampleRateData * @group sampling */ - public function testDecrementWithSamplingRateAndTags() + public function testDecrementWithSamplingRateAndTags(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 0.9); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { - $client->decrement('foo.baz', 1, ['tag' => 'value']); + $client->decrement('foo.baz', $sampleRate, ['tag' => 'value']); } $this->assertEquals( - 'test.foo.baz:-1|c|@0.9|#tag:value', + "test.foo.baz:-1|c|@{$expectedSampleRate}|#tag:value", $this->connection->getLastMessage() ); } @@ -177,16 +191,17 @@ public function testCanMeasureTimingWithClosure() /** + * @dataProvider sampleRateData * @group sampling */ - public function testTimingWithSamplingRate() + public function testTimingWithSamplingRate(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 0.9); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { - $client->timing('foo.baz', 2000, 1); + $client->timing('foo.baz', 2000, $sampleRate); } $this->assertEquals( - 'test.foo.baz:2000|ms|@0.9', + "test.foo.baz:2000|ms|@{$expectedSampleRate}", $this->connection->getLastMessage() ); } @@ -218,20 +233,21 @@ public function testEndTimingReturnsTiming() } /** + * @dataProvider sampleRateData * @group sampling */ - public function testStartEndTimingWithSamplingRate() + public function testStartEndTimingWithSamplingRate(float $globalSampleRate, float $sampleRate, string $expectedSampleRate) { - $client = new Client($this->connection, 'test', 0.9); + $client = new Client($this->connection, 'test', $globalSampleRate); for ($i = 0; $i < 10; $i++) { $client->startTiming('foo.baz'); usleep(10000); - $client->endTiming('foo.baz'); + $client->endTiming('foo.baz', $sampleRate); } // ranges between 1000 and 1001ms $this->assertMatchesRegularExpression( - '/^test\.foo\.baz:1[0-9](.[0-9]+)?\|ms\|@0.9$/', + "/^test\.foo\.baz:1[0-9](.[0-9]+)?\|ms\|@{$expectedSampleRate}$/", $this->connection->getLastMessage() ); }