perf(gmail): respect Gmail IMAP connection & bandwidth limits in sync/backfill #469

Merged
JMR-dev merged 1 commits from feat-361-gmail-imap-limits into main 2026-07-09 01:15:22 +00:00
1 Commits
Author SHA1 Message Date
JMR-dev b1a7931dfa perf(gmail): respect Gmail IMAP connection & bandwidth limits in sync/backfill
Add Gmail's documented IMAP caps (15 max simultaneous connections, 2,500 MB/day
download, 500 MB/day upload, 10,000 messages/labels per limit) as provider-scoped
config/policy that feeds the existing #360/#356 pacing machinery instead of
reinventing it:

- GmailSyncLimits: pure constants + `appliesTo(account)` provider detection via the
  existing MailProvider.forImapHost lookup (no changes to MailProvider itself).
- GmailBandwidthTracker: a new, per-account/per-day download-byte tracker (mirrors
  AccountThrottleGate's shape: ConcurrentHashMap state, injectable clock, PII-free
  once-per-crossing AppLog breadcrumb). Proactive and orthogonal to the #360
  AccountThrottleGate (which only reacts to a provider-issued throttle) and #356's
  BackfillPacer (which paces slice cadence, not bytes) - same relationship
  InteractiveImapGate already documents having to AccountThrottleGate.

Wiring: MailRepositoryImpl.prefetchMessage (the single funnel both MailBackfiller
and MailSyncer's background prefetch already share) records bytes actually pulled
over the network for Gmail accounts; MailBackfiller/MailSyncer's prefetchIfEnabled
consult isOverDailyBudget once per account before starting a batch and defer
body/attachment prefetch for the rest of the day once Gmail's budget is reached -
header paging/sync is never gated, and interactive fetches (open, attachment tap,
inline images) are never gated either, matching the existing interactive-priority
principle (#355/#360).

The 15-connection cap is already satisfied by the existing architecture
(ImapConnectionCache keeps one reused connection per account plus one dedicated
IDLE connection - 2 total, well under the cap); GmailSyncLimitsTest pins that
invariant against the documented ceiling so a future change that grows per-account
concurrency trips a test before it could approach Gmail's real limit. The
10k-messages-per-label figure is captured as a documented constant only - it is
deliberately NOT wired into a backfill stop condition, since issue #12's full
history backfill is intentional and a large real mailbox can exceed 10k messages.

AccountThrottleGate, BackfillPacer, ThrottleClassifier/ThrottleSignal/ThrottleBackoff,
InteractiveImapGate, and MailProvider are all untouched.

Closes #361
2026-07-08 19:23:25 -05:00