Skip to content

Sandbox sender: claim daily cap tokens once when SES throttles a send #278

Closed

Description

@Silthus

Map: Map: let teams send a first email before verifying their domain
Found in review of Sandbox sender: daily caps that fail closed, test sends included (draft PR PostHog#111690).

Problem

A sandbox send claims its daily cap tokens right before the SES call. When SES throttles that call, the send is rescheduled, and the retry claims the tokens again. The first claim is never returned. Under SES throttling a project reaches its daily sandbox cap with fewer emails than the cap allows. It never lets a send past the cap.

Wanted

A throttled sandbox send spends its cap tokens once. Either return the claim when SES throttles, or keep the claim with the rescheduled send so the retry does not claim again.

Test seam

email.service.test.ts: a sandbox send that SES throttles once, then accepts, spends one token per recipient copy in total.

Activity

  1. self-assigned this
    on Oct 5, 2026
  2. Silthus commented on Oct 5, 2026

    @Silthus
    OwnerAuthor

    Started by ticket worker impl-278-281 (Claude Opus 5.5 lane owner; GPT-6.1 Sol via Codex for review lanes, Claude Sonnet 5.5 for the fresh-eyes lane).

  3. Silthus commented on Oct 5, 2026

    @Silthus
    OwnerAuthor

    Done by impl-278-281. Ships together with #281.

    PR: #112071 (draft, cumulative, stacked on PostHog#111904). Clean head b6e1ba75e0f, review delta d9ef8d19c62..b6e1ba75e0f.

    • Behavior: a sandbox send that SES throttles returns its daily cap claim before it is rescheduled, so the retry spends the tokens once. The return is replay-safe: each grant has a claim ID with a marker, and a return that arrives after a short deadline is dropped. A failed return leaves the tokens spent, which only tightens the cap.
    • Proof: email.service.test.ts, a send throttled once then accepted spends one token per recipient copy, and the next send hits the project cap. rate-limiter.service.test.ts covers a doubled and a late return. Each failed before its fix. Local gate green; fork CI waits for maintainer approval.
    • Calls: returned the claim instead of carrying it with the rescheduled send, which would need a trusted marker on the queued email parameters. Kept a failed return as spent instead of skipping the send, which would drop an email SES only throttled.
    • QA: two panel rounds (GPT-6.1 Sol and Claude Opus 5.5) plus the CodeRabbit bot. The replay findings were fixed; the rest are in the PR's agent context.
    • New settings: none.
    • Follow-up candidate: own-domain SES throttle retries do not restore the email queue parameters (raised by CodeRabbit; the path is the same on master).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions