fix(supplier): RouteSupplierLeadJob терминален при отсутствии лида (стоп retry-шторм)
findOrFail -> find + ранний выход при null: 'лид удалён/не существует' — терминальная, не транзиентная ошибка. Раньше ModelNotFoundException -> queue->failed() писал в failed_webhook_jobs -> RetryFailedSupplierJobsCommand бесконечно перезапускал (инцидент 21-22.05: 25k+ записей по удалённому лиду №1). +тест RED->GREEN. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -91,7 +91,20 @@ class RouteSupplierLeadJob implements ShouldQueue
|
||||
LeadDistributor $distributor,
|
||||
RegionTagResolver $tagResolver,
|
||||
): void {
|
||||
$lead = SupplierLead::findOrFail($this->supplierLeadId);
|
||||
$lead = SupplierLead::find($this->supplierLeadId);
|
||||
|
||||
// Терминальный случай: лид удалён/не существует — это НЕ транзиентная ошибка,
|
||||
// повтор бессмыслен. НЕ бросаем ModelNotFoundException: иначе queue->failed()
|
||||
// пишет строку в failed_webhook_jobs, а RetryFailedSupplierJobsCommand
|
||||
// бесконечно перезапускает job (retry-шторм, инцидент 21-22.05.2026 —
|
||||
// 25k+ записей по удалённому лиду №1).
|
||||
if ($lead === null) {
|
||||
Log::warning('supplier_lead.not_found_terminal', [
|
||||
'supplier_lead_id' => $this->supplierLeadId,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Idempotency guard для retry-сценария ($tries = 3).
|
||||
// Если лид уже обработан — выходим, не создаём ghost duplicate'ы deal'ов.
|
||||
|
||||
@@ -48,6 +48,21 @@ function runRouteJob(int $supplierLeadId): void
|
||||
|
||||
// `linkProjectToSupplier` helper now lives in tests/Pest.php — single source.
|
||||
|
||||
it('is terminal (does not throw / re-queue) when the supplier lead does not exist', function (): void {
|
||||
// Регрессия retry-шторма 21-22.05.2026: RouteSupplierLeadJob для удалённого лида №1
|
||||
// бросал ModelNotFoundException -> queue->failed() писал в failed_webhook_jobs ->
|
||||
// RetryFailedSupplierJobsCommand бесконечно перезапускал (25k+ записей).
|
||||
// «Лид не найден» — терминальная (не транзиентная) ошибка: повтор бессмыслен.
|
||||
$missingId = 999999;
|
||||
expect(SupplierLead::find($missingId))->toBeNull();
|
||||
|
||||
// Не должно бросать исключение (иначе сработает failed() -> retry-цикл).
|
||||
runRouteJob($missingId);
|
||||
|
||||
// Никаких побочных эффектов.
|
||||
expect(Deal::count())->toBe(0);
|
||||
});
|
||||
|
||||
it('routes 1 lead to N tenants — creates N deal copies (sharing-model)', function (): void {
|
||||
$supplier = SupplierProject::factory()->create([
|
||||
'platform' => 'B1',
|
||||
|
||||
Reference in New Issue
Block a user