Ship production optimization M1-M12.5A: queues, metrics, HTTP observability, and vector search reliability.

Keep similar-ai from tripping the global circuit on a lone URL 502, clamp Qdrant search to 100, and add Server-Timing plus slow-request logging. Studio shared props, Academy S3 exists caching, heat chunking, and Redis/scheduler hygiene stay in this rollout.
This commit is contained in:
2026-08-25 07:58:47 +02:00
parent f52879edbb
commit 8a80aae21e
114 changed files with 9293 additions and 284 deletions
@@ -0,0 +1,24 @@
<?php
use App\Models\Artwork;
use App\Services\ArtworkStatsService;
use Illuminate\Support\Facades\DB;
it('does not increment download stats when the original file is missing', function () {
$artwork = Artwork::factory()->create([
'is_public' => true,
'is_approved' => true,
'published_at' => now()->subDay(),
'file_name' => 'missing-original.jpg',
'file_ext' => 'jpg',
'hash' => str_repeat('ab', 32),
]);
$this->mock(ArtworkStatsService::class, function ($mock) {
$mock->shouldReceive('incrementDownloads')->never();
});
$this->get('/download/artwork/'.$artwork->id)->assertNotFound();
expect(DB::table('artwork_downloads')->where('artwork_id', $artwork->id)->count())->toBe(0);
});
@@ -0,0 +1,11 @@
<?php
use App\Services\Traffic\OnlineVisitorRepository;
it('does not record presence for RSS feed requests', function () {
$this->mock(OnlineVisitorRepository::class, function ($mock): void {
$mock->shouldReceive('track')->never();
});
$this->get('/rss/latest-uploads.xml')->assertOk();
});
@@ -0,0 +1,113 @@
<?php
use App\Models\Artwork;
use App\Models\User;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
function slowHttpLogPath(): string
{
return storage_path('logs/slow-http-test.log');
}
function configureSlowHttpLog(): void
{
config([
'http_observability.slow_request.enabled' => true,
'http_observability.slow_request.threshold_ms' => 750,
'logging.channels.slow-http.driver' => 'single',
'logging.channels.slow-http.path' => slowHttpLogPath(),
'logging.channels.slow-http.level' => 'info',
]);
Log::forgetChannel('slow-http');
File::delete(slowHttpLogPath());
}
afterEach(function (): void {
File::delete(slowHttpLogPath());
});
it('does not write a slow-http record for a fast request', function () {
configureSlowHttpLog();
config(['http_observability.slow_request.test_duration_ms' => 120]);
$this->get('/leaderboard')->assertOk();
expect(File::exists(slowHttpLogPath()))->toBeFalse();
});
it('writes one structured slow-http record using the route template', function () {
configureSlowHttpLog();
config(['http_observability.slow_request.test_duration_ms' => 900]);
$response = $this->get('/leaderboard');
$response->assertOk();
expect($response->headers->get('Server-Timing'))->toContain('app;desc="Laravel"');
$raw = File::get(slowHttpLogPath());
expect(substr_count(trim($raw), "\n"))->toBe(0);
$line = trim($raw);
preg_match('/\{.*\}\s*$/', $line, $matches);
expect($matches[0] ?? null)->not->toBeNull();
$json = json_decode($matches[0], true, flags: JSON_THROW_ON_ERROR);
expect($json['method'])->toBe('GET')
->and($json['status'])->toBe(200)
->and($json['duration_ms'])->toEqual(900)
->and($json['route_uri'])->not->toContain('?')
->and($json['route_uri'])->not->toContain('leaderboard/')
->and($json)->not->toHaveKey('query')
->and($json)->not->toHaveKey('ip')
->and($json)->not->toHaveKey('email')
->and($json)->not->toHaveKey('user_id')
->and($json['authenticated'])->toBeFalse();
});
it('does not log when slow-request logging is disabled', function () {
configureSlowHttpLog();
config([
'http_observability.slow_request.enabled' => false,
'http_observability.slow_request.test_duration_ms' => 5000,
]);
$this->get('/leaderboard')->assertOk()->assertHeader('Server-Timing');
expect(File::exists(slowHttpLogPath()))->toBeFalse();
});
it('does not break a missing-file download response and still sends Server-Timing', function () {
configureSlowHttpLog();
config(['http_observability.slow_request.test_duration_ms' => 100]);
$artwork = Artwork::factory()->create([
'is_public' => true,
'is_approved' => true,
'published_at' => now()->subDay(),
'file_ext' => 'jpg',
'hash' => str_repeat('cd', 32),
]);
$this->get('/download/artwork/'.$artwork->id)
->assertNotFound()
->assertHeader('Server-Timing');
});
it('does not log query strings or user identifiers on an authenticated slow request', function () {
configureSlowHttpLog();
config(['http_observability.slow_request.test_duration_ms' => 800]);
$user = User::factory()->create([
'email' => '[email protected]',
'username' => 'slowlogsecret',
]);
$this->actingAs($user)->get('/leaderboard?period=weekly&debug=1')->assertOk();
$raw = File::get(slowHttpLogPath());
expect($raw)->not->toContain('[email protected]')
->and($raw)->not->toContain('period=weekly')
->and($raw)->not->toContain('debug=1')
->and($raw)->not->toContain('"user_id"')
->and($raw)->not->toContain($user->email);
});
@@ -0,0 +1,26 @@
<?php
use App\Models\Group;
use App\Models\User;
use Illuminate\Support\Facades\DB;
it('does not load studio group memberships on non-studio Inertia pages', function () {
$user = User::factory()->create();
Group::factory()->create([
'owner_user_id' => $user->id,
'status' => Group::LIFECYCLE_ACTIVE,
]);
$groupQueries = 0;
DB::listen(function ($query) use (&$groupQueries): void {
$sql = strtolower($query->sql);
if (str_contains($sql, 'from `groups`') || str_contains($sql, 'from groups') || str_contains($sql, 'group_members')) {
$groupQueries++;
}
});
$this->actingAs($user)->get('/leaderboard')->assertOk();
expect($groupQueries)->toBe(0);
});