seconds = $seconds ?? RuntimeConfig::get('notification_cooldown_seconds'); } /** Atomically reserve a sender's notification slot and queue the email. */ public function run(string $sender, callable $queue): bool { $key = hash('sha256', strtolower(trim($sender))); $now = time(); return $this->connection->transactional(function () use ($key, $now, $queue): bool { try { $this->connection->insert('mailgateway_notification', ['sender_key' => $key, 'last_notified_at' => 0]); } catch (UniqueConstraintViolationException) { // Existing sender; the conditional update decides who owns the slot. } $claimed = $this->connection->executeStatement( 'UPDATE mailgateway_notification SET last_notified_at = ? WHERE sender_key = ? AND last_notified_at <= ?', [$now, $key, $now - $this->seconds], ); if ($claimed !== 1) { return false; } if ($queue() === false) { // Disabled templates do not consume the sender's notification slot. $this->connection->delete('mailgateway_notification', ['sender_key' => $key]); return false; } return true; }); } }