reservation) { return true; } // One account on this website. Changing the API key does not grant extra quota. $usage = Cache::remember('mailflow:resend:usage', 15, fn () => $this->client->usage()); foreach (['daily', 'monthly'] as $period) { if (!isset($usage[$period]['used'], $usage[$period]['resets_at']) || !is_numeric($usage[$period]['used'])) { throw new \RuntimeException('resend_usage_invalid'); } } return DB::transaction(function () use ($usage, $message) { $buckets = []; foreach (['daily', 'monthly'] as $period) { $reset = Carbon::parse($usage[$period]['resets_at'])->setTimezone(config('app.timezone')); if ($reset->isPast()) { Cache::forget('mailflow:resend:usage'); throw new \RuntimeException('resend_usage_stale'); } $limit = min((int) Settings::get($period . '_limit'), (int) ($usage[$period]['limit'] ?? Settings::get($period . '_limit'))); $key = $period . ':' . $reset->timestamp; DB::table('v2_mail_quota_buckets')->insertOrIgnore([ 'period_key' => $key, 'period' => $period, 'resets_at' => $reset, 'limit' => $limit, 'used' => 0, 'reserved' => 0, 'created_at' => now(), 'updated_at' => now(), ]); $bucket = MailQuotaBucket::where('period_key', $key)->lockForUpdate()->firstOrFail(); $bucket->used = max($bucket->used, (int) $usage[$period]['used']); $bucket->limit = $limit; $bucket->synced_at = now(); $bucket->save(); $buckets[] = $bucket; } foreach ($buckets as $bucket) { if ($bucket->used + $bucket->reserved >= $bucket->limit) { return false; } } foreach ($buckets as $bucket) { $bucket->increment('reserved'); } $message->reservation = array_map(fn ($bucket) => $bucket->id, $buckets); $message->save(); return true; }, 3); } // Call inside the same transaction that records the message result. public function settle(MailMessage $message, bool $accepted): void { foreach ($message->reservation ?? [] as $id) { $bucket = MailQuotaBucket::whereKey($id)->lockForUpdate()->firstOrFail(); $bucket->reserved = max(0, $bucket->reserved - 1); if ($accepted) { $bucket->used++; } $bucket->save(); } $message->reservation = null; $message->save(); } public function exhausted(string $period): void { Cache::forget('mailflow:resend:usage'); MailQuotaBucket::where('period', $period)->where('resets_at', '>', now()) ->update(['used' => DB::raw('`limit`')]); } }