From 8280d7e22f7ff891d1fbc98ee3611f9e7edaf5ee Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Thu, 20 Aug 2026 23:22:54 +0800 Subject: [PATCH] fix(sentry): stop flushing metrics on every request --- src/sentry/publish/sentry.php | 8 ++ .../src/Metrics/Aspect/CounterAspect.php | 2 - .../src/Metrics/Aspect/HistogramAspect.php | 2 +- .../src/Metrics/Listener/RequestWatcher.php | 2 +- tests/Sentry/Metrics/RequestWatcherTest.php | 75 +++++++++++++++++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 tests/Sentry/Metrics/RequestWatcherTest.php diff --git a/src/sentry/publish/sentry.php b/src/sentry/publish/sentry.php index fb99e720a..6a5b65d01 100644 --- a/src/sentry/publish/sentry.php +++ b/src/sentry/publish/sentry.php @@ -56,6 +56,14 @@ // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_metrics 'enable_metrics' => env('SENTRY_ENABLE_METRICS', false), + // Metrics are buffered in memory and are no longer flushed on every request + // or annotation call. They are reported when: + // - the SDK `metric_flush_threshold` is reached (automatic flush), or + // - the request runtime context ends (endContext flush), or + // - the periodic flush kicks in as a fallback (`metrics_interval`). + // Forcing a flush per request/call amplifies transport channel pressure and + // increases the risk of memory exhaustion under high traffic. + // @see: https://docs.sentry.io/platforms/php/configuration/options/#before_send_metric // 'before_send_metric' => function (Sentry\Metrics\Types\Metric $metric): ?Sentry\Metrics\Types\Metric { // return $metric; diff --git a/src/sentry/src/Metrics/Aspect/CounterAspect.php b/src/sentry/src/Metrics/Aspect/CounterAspect.php index 6991903f9..5e9d81fac 100644 --- a/src/sentry/src/Metrics/Aspect/CounterAspect.php +++ b/src/sentry/src/Metrics/Aspect/CounterAspect.php @@ -49,8 +49,6 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint): mixed 'class' => $proceedingJoinPoint->className, 'method' => $proceedingJoinPoint->methodName, ]); - - metrics()->flush(); } return $proceedingJoinPoint->process(); diff --git a/src/sentry/src/Metrics/Aspect/HistogramAspect.php b/src/sentry/src/Metrics/Aspect/HistogramAspect.php index 50845cf19..91575e1d8 100644 --- a/src/sentry/src/Metrics/Aspect/HistogramAspect.php +++ b/src/sentry/src/Metrics/Aspect/HistogramAspect.php @@ -54,7 +54,7 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint): mixed ]); return tap($proceedingJoinPoint->process(), function () use ($timer) { - defer(fn () => $timer->end(true)); + defer(fn () => $timer->end()); }); } diff --git a/src/sentry/src/Metrics/Listener/RequestWatcher.php b/src/sentry/src/Metrics/Listener/RequestWatcher.php index e779034a6..555ac1933 100644 --- a/src/sentry/src/Metrics/Listener/RequestWatcher.php +++ b/src/sentry/src/Metrics/Listener/RequestWatcher.php @@ -62,7 +62,7 @@ public function process(object $event): void ++$this->stats->response_count; --$this->stats->connection_num; - $timer->end(true); + $timer->end(); unset($timer); }); diff --git a/tests/Sentry/Metrics/RequestWatcherTest.php b/tests/Sentry/Metrics/RequestWatcherTest.php new file mode 100644 index 000000000..8b5795ec5 --- /dev/null +++ b/tests/Sentry/Metrics/RequestWatcherTest.php @@ -0,0 +1,75 @@ +shouldReceive('isMetricsEnabled')->andReturn(true); + + $watcher = new RequestWatcher($stats, $feature); + $request = new Request('GET', 'http://127.0.0.1:9501/health'); + + $snapshot = null; + $cid = \Swoole\Coroutine::create(function () use ($watcher, $request, $stats, &$snapshot) { + $watcher->process(new RequestReceived($request, null)); + $snapshot = [ + 'accept_count' => $stats->accept_count, + 'request_count' => $stats->request_count, + 'connection_num' => $stats->connection_num, + ]; + }); + + waitForCoroutine($cid); + + expect(\Swoole\Coroutine::exists($cid))->toBeFalse() + ->and($snapshot['accept_count'])->toBe(1) + ->and($snapshot['request_count'])->toBe(1) + ->and($snapshot['connection_num'])->toBe(1); +}); + +test('defer closes the request counters after the coroutine ends', function () { + $stats = new CoroutineServerStats(); + $feature = m::mock(Feature::class); + $feature->shouldReceive('isMetricsEnabled')->andReturn(true); + + $watcher = new RequestWatcher($stats, $feature); + $request = new Request('GET', 'http://127.0.0.1:9501/health'); + + $cid = \Swoole\Coroutine::create(function () use ($watcher, $request) { + $watcher->process(new RequestReceived($request, null)); + }); + + waitForCoroutine($cid); + + expect($stats->close_count)->toBe(1) + ->and($stats->response_count)->toBe(1) + ->and($stats->connection_num)->toBe(0); +});