f1ab608444
Масштаб (лимиты внешних сервисов — на КЛЮЧ, общий на всех клиентов): - EXA: параллельный пул findSites (concurrency=5) вместо 40 запросов по одному. - Агрегатор: батч вместо ~90 запросов по одному + чанкинг по 40 и длинный таймаут 90с (большой список gpt-4o-mini не успевал в 30с). - Очередь autopodbor + WithoutOverlapping на всех 3 джобах — глобальный потолок = число воркеров; assertNoInFlight держит один подбор на клиента. Чистота выдачи (универсально, без зашитых ниш): - Анализатор: узкие рубрики, запрет зонтичных слов («финансовые услуги» тащила юрфирмы/банки). - Дедуп: ключ имени минус слова из СЛОВАРЯ рубрик прогона («Яричъ Ломбард»→«Яричъ»); слово-категория из данных, не из списка. Гард: склейка только если остаётся ровно один слово-токен (генерики вроде «займы под залог» не трогаем). TDD, 233/233 (unit+feature) зелёные. НЕ прод. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
41 lines
1.8 KiB
PHP
41 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Jobs\Autopodbor\RunAutopodborResolveJob;
|
|
use App\Jobs\Autopodbor\RunAutopodborSearchJob;
|
|
use App\Jobs\Autopodbor\RunAutopodborStudyJob;
|
|
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
// Масштаб: лимиты внешних сервисов — на КЛЮЧ (общий на всех клиентов). Джобы автоподбора идут на
|
|
// ВЫДЕЛЕННУЮ очередь autopodbor — число её воркеров = глобальный потолок одновременных подборов.
|
|
// Плюс защита от нахлёста (WithoutOverlapping) на случай ретрая/повторного dispatch.
|
|
|
|
it('поиск уходит на выделенную очередь autopodbor', function () {
|
|
Queue::fake();
|
|
|
|
RunAutopodborSearchJob::dispatch(123);
|
|
|
|
Queue::assertPushedOn('autopodbor', RunAutopodborSearchJob::class);
|
|
});
|
|
|
|
it('поиск защищён от нахлёста — middleware WithoutOverlapping по runId', function () {
|
|
$mw = (new RunAutopodborSearchJob(123))->middleware();
|
|
|
|
expect($mw)->toHaveCount(1)
|
|
->and($mw[0])->toBeInstanceOf(WithoutOverlapping::class);
|
|
});
|
|
|
|
it('шаг 2 (изучение) и резолв — на той же очереди и защищены от нахлёста', function () {
|
|
Queue::fake();
|
|
RunAutopodborStudyJob::dispatch(7);
|
|
RunAutopodborResolveJob::dispatch(8);
|
|
|
|
Queue::assertPushedOn('autopodbor', RunAutopodborStudyJob::class);
|
|
Queue::assertPushedOn('autopodbor', RunAutopodborResolveJob::class);
|
|
|
|
expect((new RunAutopodborStudyJob(7))->middleware()[0])->toBeInstanceOf(WithoutOverlapping::class)
|
|
->and((new RunAutopodborResolveJob(8))->middleware()[0])->toBeInstanceOf(WithoutOverlapping::class);
|
|
});
|