sync: per-account scheduler, session pooling, rate limiting, and jittered backoff #396

Open
opened 2026-08-10 19:28:51 +00:00 by KofTwentyTwo · 0 comments
KofTwentyTwo commented 2026-08-10 19:28:51 +00:00 (Migrated from github.com)

Not the small item the draft assumed — there is no scheduler to wire.

  • sync_interval_secs is stored and validated and nothing reads it. main.rs spawns exactly two timers: the outbox drain (5s) and an opt-in update check (24h). Mail sync happens only when a client calls Mail.Sync.
  • The outbox opens a fresh connection per mutation — mail_backend() rebuilds the engine, and apply_remote_mutation connects and logs in each call. Worst case 50 connect/login cycles per 5s per engine; ×10 engines = 500 authentications per 5s against one account. Providers rate-limit authentications far harder than concurrent connections, and some respond with a temporary lockout.
  • calculate_backoff_ms is dead code.

Scope

  • A real per-account scheduler driven by sync_interval_secs, with jitter.
  • One mutex-guarded connection per (engine, account) with mutation priority. Sharing naively queues a mark-read behind a 300-second per-account sync timeout; not sharing needs 2 per account per engine, which exceeds provider caps at fleet scale. Chunk sync per folder so it yields.
  • Drain the outbox grouped by (account, folder) — one SELECT serving N commands.
  • Budget on bandwidth, not connections. Gmail publishes 15 simultaneous connections and 2,500 MB/day download, per account; on ladder rungs 2–3 the byte cap binds first.
  • A fleet_size setting — cross-engine rate limiting is impossible without the coordination layer we decline to build, so each engine's budget is provider_cap / fleet_size.
  • Honour Retry-After with jitter proportional to fleet_size; ten engines honouring the same deadline retry in the same instant, which is worse than uncoordinated.
  • Politeness primitives currently unused: LIST-STATUS (RFC 5819, Gmail advertises it) probes every folder's change state in one round trip; COMPRESS=DEFLATE (RFC 4978) reduces metered bytes; NOTIFY (RFC 5465) watches many mailboxes on one connection.
  • Isolate poison collections — one folder that always fails must not burn the account's 300s timeout every cycle.

Largely parallelizable, but the outbox-grouping half depends on #391.

Design: ADR 0002 · review evidence (PR #385)

**Not the small item the draft assumed — there is no scheduler to wire.** - `sync_interval_secs` is stored and validated and **nothing reads it**. `main.rs` spawns exactly two timers: the outbox drain (5s) and an opt-in update check (24h). Mail sync happens only when a client calls `Mail.Sync`. - The outbox opens a **fresh connection per mutation** — `mail_backend()` rebuilds the engine, and `apply_remote_mutation` connects and logs in each call. Worst case 50 connect/login cycles per 5s per engine; ×10 engines = 500 authentications per 5s against one account. Providers rate-limit *authentications* far harder than concurrent connections, and some respond with a temporary lockout. - `calculate_backoff_ms` is dead code. ### Scope - A real per-account scheduler driven by `sync_interval_secs`, with jitter. - **One mutex-guarded connection per (engine, account) with mutation priority.** Sharing naively queues a mark-read behind a 300-second per-account sync timeout; not sharing needs 2 per account per engine, which exceeds provider caps at fleet scale. Chunk sync per folder so it yields. - Drain the outbox grouped by (account, folder) — one `SELECT` serving N commands. - **Budget on bandwidth, not connections.** Gmail publishes 15 simultaneous connections *and* 2,500 MB/day download, per account; on ladder rungs 2–3 the byte cap binds first. - A `fleet_size` setting — cross-engine rate limiting is impossible without the coordination layer we decline to build, so each engine's budget is `provider_cap / fleet_size`. - Honour `Retry-After` **with jitter proportional to `fleet_size`**; ten engines honouring the same deadline retry in the same instant, which is worse than uncoordinated. - Politeness primitives currently unused: `LIST-STATUS` (RFC 5819, Gmail advertises it) probes every folder's change state in one round trip; `COMPRESS=DEFLATE` (RFC 4978) reduces metered bytes; `NOTIFY` (RFC 5465) watches many mailboxes on one connection. - Isolate poison collections — one folder that always fails must not burn the account's 300s timeout every cycle. Largely parallelizable, but the outbox-grouping half depends on #391. Design: [ADR 0002](https://github.com/KofTwentyTwo/nuncio/blob/dev/docs/adr/0002-multi-engine-sync-model.md) · [review evidence](https://github.com/KofTwentyTwo/nuncio/blob/dev/docs/reviews/2026-08-10-multi-engine-design-review.md) (PR #385)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/nuncio#396