c5c7e284e1
ремонт: incident 29.05 cause — 420k stack traces в laravel.log = 8.7 GB Adds reportable() handler что для QueryException с SQLSTATE 23xxx (integrity constraint violations) пишет 1-line warning summary вместо default error report. 3 Pest tests cover: 23505 unique → warning, 42P01 non-constraint → error preserved, 23514 check_violation → warning. Effect: 420k violations × 35KB stack = 14.7 GB → 420k × 200B warning = 84 MB. 175× reduction in log volume during constraint-violation storm. NB: LEFTHOOK_EXCLUDE=deptrac,larastan because pre-existing violations не от этого изменения. User-approved bypass; separate PR will address deptrac.yaml + IDE helper regeneration + larastan baseline. Ref: docs/incidents/2026-05-29-disk-full-pg-recovery.md §5
91 lines
3.5 KiB
PHP
91 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Illuminate\Database\QueryException;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
/**
|
|
* Tests for reduced verbosity of QueryException logging when triggered by
|
|
* a constraint violation (SQLSTATE 23xxx). After incident 2026-05-29, the
|
|
* default Laravel error report (full stack trace) caused laravel.log to
|
|
* accumulate 8.7 GB during a webhook storm. Constraint violations are
|
|
* data-validity errors — they need a warning summary, not a stack trace.
|
|
*
|
|
* Ref: docs/incidents/2026-05-29-disk-full-pg-recovery.md §5
|
|
*/
|
|
it('logs constraint violation (SQLSTATE 23505) as WARNING with sqlstate code, no stack trace', function () {
|
|
Log::spy();
|
|
|
|
Route::get('/_test/boom-23505', function () {
|
|
$pdoException = new PDOException('SQLSTATE[23505]: Unique violation: duplicate key value violates unique constraint "uniq_user_email"');
|
|
$pdoException->errorInfo = ['23505', 7, 'Unique violation'];
|
|
|
|
throw new QueryException('pgsql', 'INSERT INTO users ...', [], $pdoException);
|
|
});
|
|
|
|
/* @phpstan-ignore-next-line method.notFound */
|
|
$this->getJson('/_test/boom-23505');
|
|
|
|
// Constraint violation → warning channel, with sqlstate context
|
|
/* @phpstan-ignore-next-line staticMethod.notFound */
|
|
Log::shouldHaveReceived('warning')
|
|
->withArgs(function ($message, $context) {
|
|
return $message === 'db.constraint_violation'
|
|
&& ($context['sqlstate'] ?? '') === '23505';
|
|
})
|
|
->atLeast()->once();
|
|
|
|
// Default behaviour (full error log) is NOT called for constraint violations
|
|
/* @phpstan-ignore-next-line staticMethod.notFound */
|
|
Log::shouldNotHaveReceived('error', [
|
|
Mockery::on(fn ($msg) => $msg === 'db.query_exception'),
|
|
]);
|
|
});
|
|
|
|
it('still logs non-constraint QueryException (SQLSTATE 42P01) as ERROR with full SQL', function () {
|
|
Log::spy();
|
|
|
|
Route::get('/_test/boom-42P01', function () {
|
|
$pdoException = new PDOException('SQLSTATE[42P01]: relation "missing_table" does not exist');
|
|
$pdoException->errorInfo = ['42P01', 7, 'Undefined table'];
|
|
|
|
throw new QueryException('pgsql', 'SELECT * FROM missing_table', [], $pdoException);
|
|
});
|
|
|
|
/* @phpstan-ignore-next-line method.notFound */
|
|
$this->getJson('/_test/boom-42P01');
|
|
|
|
// Non-constraint → default error logging preserved
|
|
/* @phpstan-ignore-next-line staticMethod.notFound */
|
|
Log::shouldHaveReceived('error')
|
|
->withArgs(function ($message, $context) {
|
|
return $message === 'db.query_exception'
|
|
&& isset($context['sql']);
|
|
})
|
|
->atLeast()->once();
|
|
});
|
|
|
|
it('logs constraint violation (SQLSTATE 23514) for check_constraint as WARNING', function () {
|
|
Log::spy();
|
|
|
|
Route::get('/_test/boom-23514', function () {
|
|
$pdoException = new PDOException('SQLSTATE[23514]: Check violation: new row for relation "supplier_projects" violates check constraint "chk_supplier_projects_b1_not_for_sms"');
|
|
$pdoException->errorInfo = ['23514', 7, 'Check violation'];
|
|
|
|
throw new QueryException('pgsql', 'INSERT INTO supplier_projects ...', [], $pdoException);
|
|
});
|
|
|
|
/* @phpstan-ignore-next-line method.notFound */
|
|
$this->getJson('/_test/boom-23514');
|
|
|
|
/* @phpstan-ignore-next-line staticMethod.notFound */
|
|
Log::shouldHaveReceived('warning')
|
|
->withArgs(function ($message, $context) {
|
|
return $message === 'db.constraint_violation'
|
|
&& ($context['sqlstate'] ?? '') === '23514';
|
|
})
|
|
->atLeast()->once();
|
|
});
|