diff --git a/resources/views/livewire/slow-requests.blade.php b/resources/views/livewire/slow-requests.blade.php index 5daeb89a..c8be509a 100644 --- a/resources/views/livewire/slow-requests.blade.php +++ b/resources/views/livewire/slow-requests.blade.php @@ -8,6 +8,19 @@ + @php + $message = << + + + @@ -31,12 +46,14 @@ + Method Route Count + Average Slowest @@ -66,9 +83,12 @@ @if ($config['sample_rate'] < 1) ~{{ number_format($slowRequest->count * (1 / $config['sample_rate'])) }} @else - {{ number_format($slowRequest->count) }} + {{ number_format($slowRequest->count) }} / {{ number_format($slowRequest->total) }} @endif + + {{ number_format($slowRequest->avg) }} ms + @if ($slowRequest->slowest === null) Unknown diff --git a/src/Livewire/SlowRequests.php b/src/Livewire/SlowRequests.php index 9521f9c2..7c165b58 100644 --- a/src/Livewire/SlowRequests.php +++ b/src/Livewire/SlowRequests.php @@ -21,7 +21,7 @@ class SlowRequests extends Card /** * Ordering. * - * @var 'slowest'|'count' + * @var 'slowest'|'count'|'average'|'total' */ #[Url(as: 'slow-requests')] public string $orderBy = 'slowest'; @@ -34,12 +34,15 @@ public function render(): Renderable [$slowRequests, $time, $runAt] = $this->remember( fn () => $this->aggregate( 'slow_request', - ['max', 'count'], + ['max', 'count', 'avg'], match ($this->orderBy) { 'count' => 'count', + 'average' => 'avg', + 'total' => 'avg_count', default => 'max', }, - )->map(function ($row) { + ) + ->map(function ($row) { [$method, $uri, $action] = json_decode($row->key, flags: JSON_THROW_ON_ERROR); return (object) [ @@ -48,6 +51,8 @@ public function render(): Renderable 'action' => $action, 'count' => $row->count, 'slowest' => $row->max, + 'avg' => $row->avg, + 'total' => $row->avg_count, 'threshold' => $this->threshold($uri, SlowRequestsRecorder::class), ]; }), diff --git a/src/Recorders/SlowRequests.php b/src/Recorders/SlowRequests.php index d7038261..04e59142 100644 --- a/src/Recorders/SlowRequests.php +++ b/src/Recorders/SlowRequests.php @@ -54,19 +54,26 @@ public function record(Carbon $startedAt, Request $request, Response $response): [$path, $via] = $this->resolveRoutePath($request); - if ( - $this->shouldIgnore($path) || - $this->underThreshold($duration = ((int) $startedAt->diffInMilliseconds()), $path) - ) { + if ($this->shouldIgnore($path)) { return; } - $this->pulse->record( + $duration = ((int) $startedAt->diffInMilliseconds()); + + // Record average and max duration for all requests. + $entry = $this->pulse->record( type: 'slow_request', key: json_encode([$request->method(), $path, $via], flags: JSON_THROW_ON_ERROR), value: $duration, timestamp: $startedAt, - )->max()->count(); + )->avg()->max(); + + if ($this->underThreshold($duration, $path)) { + return; + } + + // Record count of slow requests + $entry->count(); if ($userId = $this->pulse->resolveAuthenticatedUserId()) { $this->pulse->record( diff --git a/src/Storage/DatabaseStorage.php b/src/Storage/DatabaseStorage.php index 09b75812..56d3b2f3 100644 --- a/src/Storage/DatabaseStorage.php +++ b/src/Storage/DatabaseStorage.php @@ -540,6 +540,10 @@ public function aggregate( $orderBy ??= $aggregates[0]; + if (in_array('avg', $aggregates)) { + $aggregates[] = 'avg_count'; + } + /** @phpstan-ignore return.type */ return $this->connection() ->query() @@ -561,6 +565,7 @@ public function aggregate( 'max' => "max({$this->wrap('max')})", 'sum' => "sum({$this->wrap('sum')})", 'avg' => "avg({$this->wrap('avg')})", + 'avg_count' => "sum({$this->wrap('avg_count')})", }." as {$this->wrap($aggregate)}"); } @@ -581,6 +586,7 @@ public function aggregate( 'max' => "max({$this->wrap('value')})", 'sum' => "sum({$this->wrap('value')})", 'avg' => "avg({$this->wrap('value')})", + 'avg_count' => 'count(*)', }." as {$this->wrap($aggregate)}"); } @@ -593,6 +599,10 @@ public function aggregate( // Buckets foreach ($aggregates as $currentAggregate) { + if ($currentAggregate === 'avg_count') { + continue; + } + $query->unionAll(function (Builder $query) use ($type, $aggregates, $currentAggregate, $period, $oldestBucket) { $query->select('key_hash'); @@ -605,6 +615,8 @@ public function aggregate( 'sum' => "sum({$this->wrap('value')})", 'avg' => "avg({$this->wrap('value')})", }." as {$this->wrap($aggregate)}"); + } elseif ($aggregate === 'avg_count' && $currentAggregate === 'avg') { + $query->selectRaw("sum({$this->wrap('count')}) as {$this->wrap('avg_count')}"); } else { $query->selectRaw("null as {$this->wrap($aggregate)}"); } diff --git a/tests/Feature/Livewire/CacheTest.php b/tests/Feature/Livewire/CacheTest.php index 80ddbbd2..087bcdf1 100644 --- a/tests/Feature/Livewire/CacheTest.php +++ b/tests/Feature/Livewire/CacheTest.php @@ -58,8 +58,8 @@ Pulse::ingest(); Livewire::test(Cache::class, ['lazy' => false]) - ->assertDontSeeHtml("100.00%\n") - ->assertSeeHtml("99.99%\n"); + ->assertDontSeeHtml("100.00%" . PHP_EOL) + ->assertSeeHtml("99.99%" . PHP_EOL); }); it('does not show decimals for round numbers', function () { @@ -68,6 +68,6 @@ Pulse::ingest(); Livewire::test(Cache::class, ['lazy' => false]) - ->assertDontSeeHtml("50.00%\n") - ->assertSeeHtml("50%\n"); + ->assertDontSeeHtml("50.00%" . PHP_EOL) + ->assertSeeHtml("50%" . PHP_EOL); }); diff --git a/tests/Feature/Livewire/SlowRequestsTest.php b/tests/Feature/Livewire/SlowRequestsTest.php index ee303286..55cc42d7 100644 --- a/tests/Feature/Livewire/SlowRequestsTest.php +++ b/tests/Feature/Livewire/SlowRequestsTest.php @@ -17,26 +17,26 @@ // Add entries outside of the window. Carbon::setTestNow('2000-01-01 12:00:00'); - Pulse::record('slow_request', $request1, 1)->max()->count(); - Pulse::record('slow_request', $request2, 1)->max()->count(); + Pulse::record('slow_request', $request1, 1)->avg()->max()->count(); + Pulse::record('slow_request', $request2, 1)->avg()->max()->count(); // Add entries to the "tail". Carbon::setTestNow('2000-01-01 12:00:01'); - Pulse::record('slow_request', $request1, 1234)->max()->count(); - Pulse::record('slow_request', $request1, 2468)->max()->count(); - Pulse::record('slow_request', $request2, 1234)->max()->count(); + Pulse::record('slow_request', $request1, 1234)->avg()->max()->count(); + Pulse::record('slow_request', $request1, 2468)->avg()->max()->count(); + Pulse::record('slow_request', $request2, 1234)->avg()->max()->count(); // Add entries to the current buckets. Carbon::setTestNow('2000-01-01 13:00:00'); - Pulse::record('slow_request', $request1, 1000)->max()->count(); - Pulse::record('slow_request', $request1, 1000)->max()->count(); - Pulse::record('slow_request', $request2, 1000)->max()->count(); + Pulse::record('slow_request', $request1, 1000)->avg()->max()->count(); + Pulse::record('slow_request', $request1, 1000)->avg()->max()->count(); + Pulse::record('slow_request', $request2, 1000)->avg()->max()->count(); Pulse::ingest(); Livewire::test(SlowRequests::class, ['lazy' => false]) ->assertViewHas('slowRequests', collect([ - (object) ['method' => 'GET', 'uri' => '/users', 'action' => 'FooController@index', 'count' => 4, 'slowest' => 2468, 'threshold' => 1_000], - (object) ['method' => 'GET', 'uri' => '/users/{user}', 'action' => 'Closure', 'count' => 2, 'slowest' => 1234, 'threshold' => 1_000], + (object) ['method' => 'GET', 'uri' => '/users', 'action' => 'FooController@index', 'count' => 4, 'slowest' => 2468, 'threshold' => 1_000, 'avg' => 1425.5, 'total' => 4], + (object) ['method' => 'GET', 'uri' => '/users/{user}', 'action' => 'Closure', 'count' => 2, 'slowest' => 1234, 'threshold' => 1_000, 'avg' => 1117, 'total' => 2], ])); }); diff --git a/tests/Feature/Recorders/SlowRequestsTest.php b/tests/Feature/Recorders/SlowRequestsTest.php index b02afd68..d1e50e8d 100644 --- a/tests/Feature/Recorders/SlowRequestsTest.php +++ b/tests/Feature/Recorders/SlowRequestsTest.php @@ -33,69 +33,107 @@ expect($entries[0]->value)->toBe(4000); $aggregates = Pulse::ignore(fn () => DB::table('pulse_aggregates')->orderBy('type')->orderBy('period')->orderBy('aggregate')->get()); - expect($aggregates)->toHaveCount(8); + expect($aggregates)->toHaveCount(12); expect($aggregates[0]->bucket)->toBe(946782240); expect($aggregates[0]->period)->toBe(60); expect($aggregates[0]->type)->toBe('slow_request'); - expect($aggregates[0]->aggregate)->toBe('count'); + expect($aggregates[0]->aggregate)->toBe('avg'); expect($aggregates[0]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[0]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); - expect($aggregates[0]->value)->toEqual(1); + expect($aggregates[0]->value)->toEqual(4000); + expect($aggregates[0]->count)->toEqual(1); expect($aggregates[1]->bucket)->toBe(946782240); expect($aggregates[1]->period)->toBe(60); expect($aggregates[1]->type)->toBe('slow_request'); - expect($aggregates[1]->aggregate)->toBe('max'); + expect($aggregates[1]->aggregate)->toBe('count'); expect($aggregates[1]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[1]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); - expect($aggregates[1]->value)->toEqual(4000); + expect($aggregates[1]->value)->toEqual(1); - expect($aggregates[2]->bucket)->toBe(946782000); - expect($aggregates[2]->period)->toBe(360); + expect($aggregates[2]->bucket)->toBe(946782240); + expect($aggregates[2]->period)->toBe(60); expect($aggregates[2]->type)->toBe('slow_request'); - expect($aggregates[2]->aggregate)->toBe('count'); + expect($aggregates[2]->aggregate)->toBe('max'); expect($aggregates[2]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[2]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); - expect($aggregates[2]->value)->toEqual(1); + expect($aggregates[2]->value)->toEqual(4000); expect($aggregates[3]->bucket)->toBe(946782000); expect($aggregates[3]->period)->toBe(360); expect($aggregates[3]->type)->toBe('slow_request'); - expect($aggregates[3]->aggregate)->toBe('max'); + expect($aggregates[3]->aggregate)->toBe('avg'); expect($aggregates[3]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[3]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); expect($aggregates[3]->value)->toEqual(4000); + expect($aggregates[3]->count)->toEqual(1); - expect($aggregates[4]->bucket)->toBe(946781280); - expect($aggregates[4]->period)->toBe(1440); + expect($aggregates[4]->bucket)->toBe(946782000); + expect($aggregates[4]->period)->toBe(360); expect($aggregates[4]->type)->toBe('slow_request'); expect($aggregates[4]->aggregate)->toBe('count'); expect($aggregates[4]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[4]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); expect($aggregates[4]->value)->toEqual(1); - expect($aggregates[5]->bucket)->toBe(946781280); - expect($aggregates[5]->period)->toBe(1440); + expect($aggregates[5]->bucket)->toBe(946782000); + expect($aggregates[5]->period)->toBe(360); expect($aggregates[5]->type)->toBe('slow_request'); expect($aggregates[5]->aggregate)->toBe('max'); expect($aggregates[5]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[5]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); expect($aggregates[5]->value)->toEqual(4000); - expect($aggregates[6]->period)->toBe(10080); + expect($aggregates[6]->bucket)->toBe(946781280); + expect($aggregates[6]->period)->toBe(1440); expect($aggregates[6]->type)->toBe('slow_request'); - expect($aggregates[6]->aggregate)->toBe('count'); + expect($aggregates[6]->aggregate)->toBe('avg'); expect($aggregates[6]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[6]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); - expect($aggregates[6]->value)->toEqual(1); + expect($aggregates[6]->value)->toEqual(4000); + expect($aggregates[6]->count)->toEqual(1); - expect($aggregates[7]->period)->toBe(10080); + expect($aggregates[7]->bucket)->toBe(946781280); + expect($aggregates[7]->period)->toBe(1440); expect($aggregates[7]->type)->toBe('slow_request'); - expect($aggregates[7]->aggregate)->toBe('max'); + expect($aggregates[7]->aggregate)->toBe('count'); expect($aggregates[7]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); expect($aggregates[7]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); - expect($aggregates[7]->value)->toEqual(4000); + expect($aggregates[7]->value)->toEqual(1); + + expect($aggregates[8]->bucket)->toBe(946781280); + expect($aggregates[8]->period)->toBe(1440); + expect($aggregates[8]->type)->toBe('slow_request'); + expect($aggregates[8]->aggregate)->toBe('max'); + expect($aggregates[8]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); + expect($aggregates[8]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); + expect($aggregates[8]->value)->toEqual(4000); + + expect($aggregates[9]->bucket)->toBe(946774080); + expect($aggregates[9]->period)->toBe(10080); + expect($aggregates[9]->type)->toBe('slow_request'); + expect($aggregates[9]->aggregate)->toBe('avg'); + expect($aggregates[9]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); + expect($aggregates[9]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); + expect($aggregates[9]->value)->toEqual(4000); + expect($aggregates[9]->count)->toEqual(1); + + expect($aggregates[10]->bucket)->toBe(946774080); + expect($aggregates[10]->period)->toBe(10080); + expect($aggregates[10]->type)->toBe('slow_request'); + expect($aggregates[10]->aggregate)->toBe('count'); + expect($aggregates[10]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); + expect($aggregates[10]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); + expect($aggregates[10]->value)->toEqual(1); + + expect($aggregates[11]->bucket)->toBe(946774080); + expect($aggregates[11]->period)->toBe(10080); + expect($aggregates[11]->type)->toBe('slow_request'); + expect($aggregates[11]->aggregate)->toBe('max'); + expect($aggregates[11]->key)->toBe(json_encode(['GET', '/test-route', 'Closure'])); + expect($aggregates[11]->key_hash)->toBe(keyHash(json_encode(['GET', '/test-route', 'Closure']))); + expect($aggregates[11]->value)->toEqual(4000); Pulse::ignore(fn () => expect(DB::table('pulse_values')->count())->toBe(0)); }); @@ -122,12 +160,23 @@ get('default-threshold')->assertOk(); $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); - expect($entries)->toHaveCount(1); + expect($entries)->toHaveCount(3); + expect($entries[0]->type)->toBe('slow_request'); expect($entries[0]->key)->toBe(json_encode(['GET', '/one-second-threshold', 'Closure'])); expect($entries[0]->key_hash)->toBe(keyHash(json_encode(['GET', '/one-second-threshold', 'Closure']))); expect($entries[0]->value)->toBe(1000); + expect($entries[1]->type)->toBe('slow_request'); + expect($entries[1]->key)->toBe(json_encode(['GET', '/two-second-threshold', 'Closure'])); + expect($entries[1]->key_hash)->toBe(keyHash(json_encode(['GET', '/two-second-threshold', 'Closure']))); + expect($entries[1]->value)->toBe(1000); + + expect($entries[2]->type)->toBe('slow_request'); + expect($entries[2]->key)->toBe(json_encode(['GET', '/default-threshold', 'Closure'])); + expect($entries[2]->key_hash)->toBe(keyHash(json_encode(['GET', '/default-threshold', 'Closure']))); + expect($entries[2]->value)->toBe(0); + DB::table('pulse_entries')->delete(); $sleepSeconds = 2; @@ -136,15 +185,21 @@ get('default-threshold')->assertOk(); $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->orderBy('key')->get()); - expect($entries)->toHaveCount(2); + expect($entries)->toHaveCount(3); expect($entries[0]->type)->toBe('slow_request'); - expect($entries[0]->key)->toBe(json_encode(['GET', '/one-second-threshold', 'Closure'])); - expect($entries[0]->key_hash)->toBe(keyHash(json_encode(['GET', '/one-second-threshold', 'Closure']))); - expect($entries[0]->value)->toBe(2000); + expect($entries[0]->key)->toBe(json_encode(['GET', '/default-threshold', 'Closure'])); + expect($entries[0]->key_hash)->toBe(keyHash(json_encode(['GET', '/default-threshold', 'Closure']))); + expect($entries[0]->value)->toBe(0); + expect($entries[1]->type)->toBe('slow_request'); - expect($entries[1]->key)->toBe(json_encode(['GET', '/two-second-threshold', 'Closure'])); - expect($entries[1]->key_hash)->toBe(keyHash(json_encode(['GET', '/two-second-threshold', 'Closure']))); + expect($entries[1]->key)->toBe(json_encode(['GET', '/one-second-threshold', 'Closure'])); + expect($entries[1]->key_hash)->toBe(keyHash(json_encode(['GET', '/one-second-threshold', 'Closure']))); expect($entries[1]->value)->toBe(2000); + + expect($entries[2]->type)->toBe('slow_request'); + expect($entries[2]->key)->toBe(json_encode(['GET', '/two-second-threshold', 'Closure'])); + expect($entries[2]->key_hash)->toBe(keyHash(json_encode(['GET', '/two-second-threshold', 'Closure']))); + expect($entries[2]->value)->toBe(2000); }); it('captures slow requests per user', function () { @@ -212,7 +267,7 @@ get('test-route'); Pulse::ignore(fn () => expect(DB::table('pulse_entries')->get())->toHaveCount(1)); - Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->get())->toHaveCount(8)); + Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->get())->toHaveCount(12)); Pulse::ignore(fn () => expect(DB::table('pulse_values')->count())->toBe(0)); }); @@ -225,8 +280,8 @@ get('test-route'); - Pulse::ignore(fn () => expect(DB::table('pulse_entries')->count())->toBe(0)); - Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->count())->toBe(0)); + Pulse::ignore(fn () => expect(DB::table('pulse_entries')->count())->toBe(1)); + Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->count())->toBe(8)); Pulse::ignore(fn () => expect(DB::table('pulse_values')->count())->toBe(0)); }); @@ -320,71 +375,107 @@ expect($entries[0]->value)->toBe(4000); $aggregates = Pulse::ignore(fn () => DB::table('pulse_aggregates')->orderBy('type')->orderBy('period')->orderBy('aggregate')->get()); - expect($aggregates)->toHaveCount(8); + expect($aggregates)->toHaveCount(12); expect($aggregates[0]->bucket)->toBe(946782240); expect($aggregates[0]->period)->toBe(60); expect($aggregates[0]->type)->toBe('slow_request'); - expect($aggregates[0]->aggregate)->toBe('count'); + expect($aggregates[0]->aggregate)->toBe('avg'); expect($aggregates[0]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[0]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); - expect($aggregates[0]->value)->toEqual(1); + expect($aggregates[0]->value)->toEqual(4000); + expect($aggregates[0]->count)->toEqual(1); expect($aggregates[1]->bucket)->toBe(946782240); expect($aggregates[1]->period)->toBe(60); expect($aggregates[1]->type)->toBe('slow_request'); - expect($aggregates[1]->aggregate)->toBe('max'); + expect($aggregates[1]->aggregate)->toBe('count'); expect($aggregates[1]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[1]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); - expect($aggregates[1]->value)->toEqual(4000); + expect($aggregates[1]->value)->toEqual(1); - expect($aggregates[2]->bucket)->toBe(946782000); - expect($aggregates[2]->period)->toBe(360); + expect($aggregates[2]->bucket)->toBe(946782240); + expect($aggregates[2]->period)->toBe(60); expect($aggregates[2]->type)->toBe('slow_request'); - expect($aggregates[2]->aggregate)->toBe('count'); + expect($aggregates[2]->aggregate)->toBe('max'); expect($aggregates[2]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[2]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); - expect($aggregates[2]->value)->toEqual(1); + expect($aggregates[2]->value)->toEqual(4000); expect($aggregates[3]->bucket)->toBe(946782000); expect($aggregates[3]->period)->toBe(360); expect($aggregates[3]->type)->toBe('slow_request'); - expect($aggregates[3]->aggregate)->toBe('max'); + expect($aggregates[3]->aggregate)->toBe('avg'); expect($aggregates[3]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[3]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); expect($aggregates[3]->value)->toEqual(4000); + expect($aggregates[3]->count)->toEqual(1); - expect($aggregates[4]->bucket)->toBe(946781280); - expect($aggregates[4]->period)->toBe(1440); + expect($aggregates[4]->bucket)->toBe(946782000); + expect($aggregates[4]->period)->toBe(360); expect($aggregates[4]->type)->toBe('slow_request'); expect($aggregates[4]->aggregate)->toBe('count'); expect($aggregates[4]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[4]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); expect($aggregates[4]->value)->toEqual(1); - expect($aggregates[5]->bucket)->toBe(946781280); - expect($aggregates[5]->period)->toBe(1440); + expect($aggregates[5]->bucket)->toBe(946782000); + expect($aggregates[5]->period)->toBe(360); expect($aggregates[5]->type)->toBe('slow_request'); expect($aggregates[5]->aggregate)->toBe('max'); expect($aggregates[5]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[5]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); expect($aggregates[5]->value)->toEqual(4000); - expect($aggregates[6]->bucket)->toBe(946774080); - expect($aggregates[6]->period)->toBe(10080); + expect($aggregates[6]->bucket)->toBe(946781280); + expect($aggregates[6]->period)->toBe(1440); expect($aggregates[6]->type)->toBe('slow_request'); - expect($aggregates[6]->aggregate)->toBe('count'); + expect($aggregates[6]->aggregate)->toBe('avg'); expect($aggregates[6]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[6]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); - expect($aggregates[6]->value)->toEqual(1); + expect($aggregates[6]->value)->toEqual(4000); + expect($aggregates[6]->count)->toEqual(1); - expect($aggregates[7]->bucket)->toBe(946774080); - expect($aggregates[7]->period)->toBe(10080); + expect($aggregates[7]->bucket)->toBe(946781280); + expect($aggregates[7]->period)->toBe(1440); expect($aggregates[7]->type)->toBe('slow_request'); - expect($aggregates[7]->aggregate)->toBe('max'); + expect($aggregates[7]->aggregate)->toBe('count'); expect($aggregates[7]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); expect($aggregates[7]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); - expect($aggregates[7]->value)->toEqual(4000); + expect($aggregates[7]->value)->toEqual(1); + + expect($aggregates[8]->bucket)->toBe(946781280); + expect($aggregates[8]->period)->toBe(1440); + expect($aggregates[8]->type)->toBe('slow_request'); + expect($aggregates[8]->aggregate)->toBe('max'); + expect($aggregates[8]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); + expect($aggregates[8]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); + expect($aggregates[8]->value)->toEqual(4000); + + expect($aggregates[9]->bucket)->toBe(946774080); + expect($aggregates[9]->period)->toBe(10080); + expect($aggregates[9]->type)->toBe('slow_request'); + expect($aggregates[9]->aggregate)->toBe('avg'); + expect($aggregates[9]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); + expect($aggregates[9]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); + expect($aggregates[9]->value)->toEqual(4000); + expect($aggregates[9]->count)->toEqual(1); + + expect($aggregates[10]->bucket)->toBe(946774080); + expect($aggregates[10]->period)->toBe(10080); + expect($aggregates[10]->type)->toBe('slow_request'); + expect($aggregates[10]->aggregate)->toBe('count'); + expect($aggregates[10]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); + expect($aggregates[10]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); + expect($aggregates[10]->value)->toEqual(1); + + expect($aggregates[11]->bucket)->toBe(946774080); + expect($aggregates[11]->period)->toBe(10080); + expect($aggregates[11]->type)->toBe('slow_request'); + expect($aggregates[11]->aggregate)->toBe('max'); + expect($aggregates[11]->key)->toBe(json_encode(['POST', '/test-route', 'via /livewire/update'])); + expect($aggregates[11]->key_hash)->toBe(keyHash(json_encode(['POST', '/test-route', 'via /livewire/update']))); + expect($aggregates[11]->value)->toEqual(4000); Pulse::ignore(fn () => expect(DB::table('pulse_values')->count())->toBe(0)); }); @@ -414,7 +505,7 @@ expect($entries[0]->key)->toBe(json_encode(['GET', '{account}.example.com/users', 'Closure'])); expect($entries[1]->key)->toBe(json_encode(['GET', '/users', 'Closure'])); - Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->count())->toBe(16)); + Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->count())->toBe(24)); Pulse::ignore(fn () => expect(DB::table('pulse_values')->count())->toBe(0)); }); @@ -484,7 +575,7 @@ get('test-route'); Pulse::ignore(fn () => expect(DB::table('pulse_entries')->count())->toBe(10)); - Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->count())->toBe(8)); + Pulse::ignore(fn () => expect(DB::table('pulse_aggregates')->count())->toBe(12)); Pulse::ignore(fn () => expect(DB::table('pulse_values')->count())->toBe(0)); }); diff --git a/tests/Feature/Recorders/UserRequestsTest.php b/tests/Feature/Recorders/UserRequestsTest.php index 3e4a70e7..1f0febfc 100644 --- a/tests/Feature/Recorders/UserRequestsTest.php +++ b/tests/Feature/Recorders/UserRequestsTest.php @@ -21,7 +21,7 @@ actingAs(User::make(['id' => '567']))->get('users'); - $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->get()); expect($entries)->toHaveCount(1); expect($entries[0])->toHaveProperties([ 'timestamp' => 946782245, @@ -29,7 +29,7 @@ 'key' => '567', 'value' => null, ]); - $aggregates = Pulse::ignore(fn () => DB::table('pulse_aggregates')->orderBy('period')->get()); + $aggregates = Pulse::ignore(fn () => DB::table('pulse_aggregates')->where('type', 'user_request')->orderBy('period')->get()); expect($aggregates)->toHaveCount(4); expect($aggregates)->toContainAggregateForAllPeriods( type: 'user_request', @@ -44,7 +44,7 @@ get('users'); - $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->get()); expect($entries)->toHaveCount(0); }); @@ -53,7 +53,7 @@ post('login'); - $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->get()); expect($entries)->toHaveCount(1); expect($entries[0]->key)->toBe('567'); }); @@ -63,7 +63,7 @@ actingAs(User::make(['id' => '567']))->post('logout'); - $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->get()); expect($entries)->toHaveCount(1); expect($entries[0]->key)->toBe('567'); }); @@ -94,7 +94,7 @@ public function user() get('users'); - $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->get()); expect($entries)->toHaveCount(0); }); @@ -106,7 +106,7 @@ public function user() actingAs(User::make(['id' => '567']))->get('users'); - expect(Pulse::ignore(fn () => DB::table('pulse_entries')->count()))->toBe(0); + expect(Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->count()))->toBe(0); }); it('ignores livewire update requests from an ignored path', function () { @@ -129,7 +129,7 @@ public function user() ], ]); - expect(Pulse::ignore(fn () => DB::table('pulse_entries')->count()))->toBe(0); + expect(Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->count()))->toBe(0); }); it('can sample', function () { @@ -148,7 +148,7 @@ public function user() get('users'); get('users'); - expect(Pulse::ignore(fn () => DB::table('pulse_entries')->count()))->toEqualWithDelta(1, 4); + expect(Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->count()))->toEqualWithDelta(1, 4); }); it('can sample at zero', function () { @@ -167,7 +167,7 @@ public function user() get('users'); get('users'); - expect(Pulse::ignore(fn () => DB::table('pulse_entries')->count()))->toBe(0); + expect(Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->count()))->toBe(0); }); it('can sample at one', function () { @@ -186,5 +186,5 @@ public function user() get('users'); get('users'); - expect(Pulse::ignore(fn () => DB::table('pulse_entries')->count()))->toBe(10); + expect(Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'user_request')->count()))->toBe(10); }); diff --git a/tests/Feature/Storage/DatabaseStorageTest.php b/tests/Feature/Storage/DatabaseStorageTest.php index 99644930..05942e7a 100644 --- a/tests/Feature/Storage/DatabaseStorageTest.php +++ b/tests/Feature/Storage/DatabaseStorageTest.php @@ -328,8 +328,8 @@ $results = Pulse::aggregate('slow_request', ['min', 'max', 'sum', 'avg', 'count'], CarbonInterval::hour()); expect($results->all())->toEqual([ - (object) ['key' => 'GET /bar', 'min' => 200, 'max' => 600, 'sum' => 2400, 'avg' => 400, 'count' => 6], - (object) ['key' => 'GET /foo', 'min' => 100, 'max' => 400, 'sum' => 2000, 'avg' => 250, 'count' => 8], + (object) ['key' => 'GET /bar', 'min' => 200, 'max' => 600, 'sum' => 2400, 'avg' => 400, 'count' => 6, 'avg_count' => 6], + (object) ['key' => 'GET /foo', 'min' => 100, 'max' => 400, 'sum' => 2000, 'avg' => 250, 'count' => 8, 'avg_count' => 8], ]); });