Mailglass.RateLimiter (Mailglass v0.1.0)

Copy Markdown View Source

Per-{tenant_id, recipient_domain} ETS token bucket (SEND-02).

Hot path is :ets.update_counter/4 — no GenServer mailbox serialization. The TableOwner GenServer exists only to own the table (see D-22). ≈1-3μs on OTP 27 with decentralized_counters: true plus write_concurrency: :auto.

Invariants

  • :transactional bypass (D-24): check/3 with stream == :transactional returns :ok BEFORE any ETS read. Password-reset / magic-link / verify-email MUST NOT be throttled because a marketing campaign saturated the bucket. Documented as a reserved invariant in docs/api_stability.md; this is NOT a tunable.
  • Leaky-bucket continuous refill (D-23): capacity tokens refill at capacity / 60_000 tokens/ms. Default: 100 tokens @ 100/min.

Configuration

config :mailglass, :rate_limit,
  default: [capacity: 100, per_minute: 100],
  overrides: [
    {{"premium-tenant", "gmail.com"}, [capacity: 500, per_minute: 500]}
  ]

Missing :rate_limit key uses built-in defaults.

Telemetry

Single-emit [:mailglass, :outbound, :rate_limit, :stop] with:

  • Measurements: %{duration_us: integer()}
  • Metadata: %{allowed: boolean(), tenant_id: String.t()}

No PII — recipient_domain is NOT emitted (domain is less sensitive than full address, but to stay inside the Phase 1 D-31 whitelist we omit it; operators who need it can add a domain-aware handler that reads context from other sources).

Summary

Functions

Returns :ok when the delivery is allowed, or {:error, %RateLimitError{}} when the bucket is depleted. :transactional stream always returns :ok.

Functions

check(tenant_id, domain, arg3)

(since 0.1.0)
@spec check(String.t(), String.t(), atom()) ::
  :ok | {:error, Mailglass.RateLimitError.t()}

Returns :ok when the delivery is allowed, or {:error, %RateLimitError{}} when the bucket is depleted. :transactional stream always returns :ok.

Arguments

  • tenant_id — binary tenant id
  • recipient_domain — binary domain (e.g. "gmail.com")
  • stream:transactional | :operational | :bulk