From 0cc5be54790cc1dbafbc0c74a1dc2710d1343ff6 Mon Sep 17 00:00:00 2001 From: nexxo Date: Sat, 25 Jul 2026 16:34:02 +0200 Subject: [PATCH] fix(admin): atomic send-time delivery claim so backlog duplicates ship exactly once Co-Authored-By: Claude Opus 4.8 --- app/Providers/AppServiceProvider.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2f8ccdf..43f2575 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -48,20 +48,29 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // Suppress a queued maintenance announcement if its window was cancelled - // before the job runs — otherwise a delayed announcement could arrive for - // an already-cancelled window. Returning false aborts the send. + // Send-time guard for maintenance mail (X-CP-Notification carries the + // ledger id). Two things happen atomically here so exactly one copy ships + // 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) { $header = $event->message->getHeaders()->get('X-CP-Notification'); if ($header === null) { return null; } - $notification = MaintenanceNotification::query()->with('window')->find((int) $header->getBodyAsString()); - if ($notification?->event === 'announcement' && $notification->window?->state === 'cancelled') { + $id = (int) $header->getBodyAsString(); + $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 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