fix(admin): atomic send-time delivery claim so backlog duplicates ship exactly once

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/portal-design
nexxo 2026-07-25 16:34:02 +02:00
parent 5b85806605
commit 0cc5be5479
1 changed files with 15 additions and 6 deletions

View File

@ -48,20 +48,29 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
// Suppress a queued maintenance announcement if its window was cancelled // Send-time guard for maintenance mail (X-CP-Notification carries the
// before the job runs — otherwise a delayed announcement could arrive for // ledger id). Two things happen atomically here so exactly one copy ships
// an already-cancelled window. Returning false aborts the send. // even if a backlog caused a duplicate job to be queued:
// 1) suppress an announcement whose window was cancelled meanwhile;
// 2) claim delivery with a conditional update — the first send wins,
// any duplicate finds sent_at already set and is aborted.
Event::listen(MessageSending::class, function (MessageSending $event) { Event::listen(MessageSending::class, function (MessageSending $event) {
$header = $event->message->getHeaders()->get('X-CP-Notification'); $header = $event->message->getHeaders()->get('X-CP-Notification');
if ($header === null) { if ($header === null) {
return null; return null;
} }
$notification = MaintenanceNotification::query()->with('window')->find((int) $header->getBodyAsString()); $id = (int) $header->getBodyAsString();
if ($notification?->event === 'announcement' && $notification->window?->state === 'cancelled') { $notification = MaintenanceNotification::query()->with('window')->find($id);
if ($notification === null) {
return null;
}
if ($notification->event === 'announcement' && $notification->window?->state === 'cancelled') {
return false; // window cancelled meanwhile — do not deliver return false; // window cancelled meanwhile — do not deliver
} }
return null; $claimed = MaintenanceNotification::query()->whereKey($id)->whereNull('sent_at')->update(['sent_at' => now()]);
return $claimed === 0 ? false : null; // 0 → already delivered, suppress duplicate
}); });
// Stamp a maintenance-notification ledger row as delivered only once the // Stamp a maintenance-notification ledger row as delivered only once the