fix(billing): serialize Gmail inbox allowance decisions so racing alias signups can't both keep it
Merged
WA
1d ago+227+227+012345678901234567890123456789-85-85-01234567890123456789
Summary
- Better Auth commits each user row before
user.create.afterruns, so two signups on one Gmail inbox could race: the later account's earlier-account check could run before the earlier row was visible, and both kept the free allowance (follow-up to #9418) handleNewUsernow decides inside one transaction under a per-inbox advisory lock (acquireGmailInboxLock, existingacquireAdvisoryXactLock), inserts the stats row, then withdraws the allowance from any later alias on the inbox that got in first (laterGmailAliasesWithAllowance) — the earliest account keeps it whichever hook runs first- The stats-row fallback keeps an unlocked check: it only runs when a signup's own decision failed, and locking there would hold the inbox lock through the caller's transaction; it never withdraws from later accounts, so long-standing aliases are never stripped retroactively
identityHadFreeAllowancemoves tolib/users/free-allowance.ts; the admin balance grant now uses it, so a stats row it creates applies the Gmail rule too (it previously checked deleted identities only)- Alias lookups are a single self-join on
user_gmail_mailbox_idx; test schema mock gainsgmailMailbox
Type of Change
- Bug fix
Testing
lib/users/gmail-alias.integration.tson real Postgres (INTEGRATION_DB_PROVISION=migrate): 11/11. New guards shown red with the fix disabled: an alias whose signup decided first loses the allowance once the earlier account decides; a held inbox lock blocks a same-inbox signup (pg_blocking_pids); the fallback never withdraws from a later alias. Index test now EXPLAINs the self-join shapelib/billing/core/usage.test.ts,lib/admin/dashboard-credit-grant.test.ts: 30/30bun run lint,check:file-names,check:test-patterns,check:comment-hygiene,check:unused-exports,check:boundaries,check:schema-mock;drizzle-kit generatereports no drift- Full type-check / audits / suites left to CI
Checklist
- Code follows project style guidelines
- Self-reviewed my changes
- Tests added/updated and passing (new tests pass the
test-auditauthoring gate) - No new warnings introduced
- I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)