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,55 @@
<?php
use App\Jobs\IndexArtworkJob;
use App\Jobs\IndexUserJob;
use App\Jobs\RankBuildScopeListsJob;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
use Illuminate\Support\Facades\Queue;
it('assigns artwork and user search index jobs to the search queue', function () {
expect((new IndexArtworkJob(15))->queue)->toBe('search')
->and((new IndexUserJob(22))->queue)->toBe('search');
Queue::fake();
IndexUserJob::dispatch(22);
Queue::assertPushedOn('search', IndexUserJob::class);
});
it('collapses duplicate index jobs with unique ids', function () {
$artwork = new IndexArtworkJob(15);
$user = new IndexUserJob(22);
expect($artwork)->toBeInstanceOf(ShouldBeUniqueUntilProcessing::class)
->and($artwork->uniqueId())->toBe('15')
->and($artwork->uniqueFor)->toBe(120)
->and($user)->toBeInstanceOf(ShouldBeUniqueUntilProcessing::class)
->and($user->uniqueId())->toBe('22')
->and($user->uniqueFor)->toBe(60);
});
it('uniques ranking scope rebuilds so hourly fan-out cannot stack the same scope', function () {
$job = new RankBuildScopeListsJob('category', 44);
expect($job)->toBeInstanceOf(ShouldBeUnique::class)
->and($job->uniqueId())->toBe('category:44')
->and($job->uniqueFor)->toBe(21600)
->and($job->uniqueFor)->toBeGreaterThan(360)
->and((new RankBuildScopeListsJob('content_type', 3))->uniqueId())->toBe('content_type:3')
->and((new RankBuildScopeListsJob('category', 44))->uniqueId())
->not->toBe((new RankBuildScopeListsJob('category', 45))->uniqueId());
});
it('does not enqueue the same ranking scope again after 360s while the unique lock remains', function () {
Queue::fake();
$this->freezeTime();
RankBuildScopeListsJob::dispatch('category', 88);
$this->travel(361)->seconds();
RankBuildScopeListsJob::dispatch('category', 88);
RankBuildScopeListsJob::dispatch('category', 89);
Queue::assertPushed(RankBuildScopeListsJob::class, 2);
Queue::assertPushed(RankBuildScopeListsJob::class, fn (RankBuildScopeListsJob $job): bool => $job->uniqueId() === 'category:88');
Queue::assertPushed(RankBuildScopeListsJob::class, fn (RankBuildScopeListsJob $job): bool => $job->uniqueId() === 'category:89');
});
@@ -0,0 +1,56 @@
<?php
use App\Jobs\IndexUserJob;
use App\Jobs\RecComputeSimilarByTagsJob;
use App\Support\Queues\QueuedJobClassMatcher;
use Illuminate\Support\Facades\Redis;
it('matches only RecComputeSimilarByTagsJob payloads', function () {
$tags = json_encode([
'displayName' => RecComputeSimilarByTagsJob::class,
'data' => ['commandName' => RecComputeSimilarByTagsJob::class],
]);
$other = json_encode([
'displayName' => IndexUserJob::class,
'data' => ['commandName' => IndexUserJob::class],
]);
expect(QueuedJobClassMatcher::isClass($tags, RecComputeSimilarByTagsJob::class))->toBeTrue()
->and(QueuedJobClassMatcher::isClass($other, RecComputeSimilarByTagsJob::class))->toBeFalse();
});
it('dry-run counts stale tags jobs and does not remove them', function () {
$tags = json_encode(['displayName' => RecComputeSimilarByTagsJob::class, 'uuid' => 'aaa']);
$other = json_encode(['displayName' => IndexUserJob::class, 'uuid' => 'bbb']);
$redis = Mockery::mock();
$redis->shouldReceive('llen')->once()->with('queues:default')->andReturn(2);
$redis->shouldReceive('lrange')->once()->andReturn([$other, $tags]);
$redis->shouldReceive('lrem')->never();
Redis::partialMock()->shouldReceive('connection')->andReturn($redis);
$this->artisan('skinbase:purge-queued-rec-tags --dry-run')
->expectsOutputToContain('matched=1')
->assertSuccessful();
});
it('execute removes only tags jobs and preserves unrelated payloads', function () {
$tagsA = json_encode(['displayName' => RecComputeSimilarByTagsJob::class, 'uuid' => 't1']);
$tagsB = json_encode(['displayName' => RecComputeSimilarByTagsJob::class, 'uuid' => 't2']);
$other = json_encode(['displayName' => IndexUserJob::class, 'uuid' => 'keep']);
$redis = Mockery::mock();
$redis->shouldReceive('llen')->once()->with('queues:default')->andReturn(3);
$redis->shouldReceive('lrange')->once()->andReturn([$other, $tagsA, $tagsB]);
$redis->shouldReceive('lrem')->twice()->andReturn(1);
Redis::partialMock()->shouldReceive('connection')->andReturn($redis);
$this->artisan('skinbase:purge-queued-rec-tags --execute')
->expectsOutputToContain('matched=2')
->assertSuccessful();
});
it('refuses to run without dry-run or execute', function () {
$this->artisan('skinbase:purge-queued-rec-tags')
->assertFailed();
});