spike(imap): prototype flag-gated connection reuse for folder-open #131

Merged
JMR-dev merged 4 commits from spike-imap-connection-reuse into main 2026-07-02 20:07:06 +00:00
JMR-dev commented 2026-07-02 14:30:41 +00:00 (Migrated from github.com)

Spike: flag-gated IMAP connection reuse for folder-open

A time-boxed spike for #125, prototyping the per-account connection reuse the investigation (#126) recommended and deferred. This references #125 but does not close it — the feature needs real-device / real-account validation before the default flips on.

The folder-open network path currently re-establishes a full, freshly-authenticated IMAP connection on every operation (CONNECT + TLS + LOGIN … LOGOUT) — no pooling/keep-alive. Only EXAMINE + FETCH is intrinsic to opening a folder; the setup group is the majority of the round-trips and is avoidable on the 2nd+ operation if a connection is reused.

What's here

  • ImapConnectionCache — keeps one authenticated Store alive per account, guarded by a per-account mutex, keyed by connection identity (host/port/user/security/mechanism — not the rotating secret), with lazy catch-and-retry-once stale handling. closeReusedConnections() is the only eviction today.
  • ImapClient flag — a reuseConnections boolean, OFF by default via the @Inject no-arg constructor. With it off, withStore is byte-for-byte the previous connect + LOGOUT-per-call and the cache is never allocated, so it cannot destabilize main. Only the harness opts in.
  • Harness proof — ImapFolderOpenLatencyTest flips the flag on and asserts the collapse against real in-process IMAP (CountingImapProxy + GreenMail). Localhost is ~0 RTT, so this proves the connection is reused (structure), not wall-clock latency — no numbers are fabricated.
  • docs/perf/issue-125-connection-reuse-spike.md — prototype design, the flag-off-vs-on proof, per-decision trade-offs, feasibility verdict, and the refined real-device validation plan.

Deterministic proof (flag off vs on), for N = 3 folder-opens

Scenario TCP conns LOGIN EXAMINE LOGOUT
Flag OFF — N folder-opens N (3) N (3) N (3) N (3)
Flag ON — N folder-opens 1 1 N (3) 1
Flag OFF — open folder + read msg 2 2 1E+1S 2
Flag ON — open folder + read msg 1 1 1E+1S 1

Setup drops from once per operation to once per account; the intrinsic per-folder EXAMINE is unchanged. All 6 cases pass on the JVM fast gate.

Deliberately left open (needs measurement, not a guess)

Single connection vs. bounded pool (head-of-line blocking), idle-eviction timeout, stale-probe strategy (NOOP vs retry), IDLE per-account connection budget (#90 — reuse is an additional socket to IDLE), low-battery eviction (#88/#89/#90), and mutation-retry idempotency. See the doc for the per-decision stance + the 6-step real-device validation plan.

Scope

IMAP client / connection lifecycle + the GreenMail harness + the design doc only. No DB/keystore (#118) or mailbox UI (#130) changes. IDLE is untouched (keeps its own dedicated connection).

🤖 Generated with Claude Code

## Spike: flag-gated IMAP connection reuse for folder-open A time-boxed **spike** for #125, prototyping the per-account connection reuse the investigation (#126) recommended and deferred. This **references #125 but does not close it** — the feature needs real-device / real-account validation before the default flips on. The folder-open network path currently re-establishes a full, freshly-authenticated IMAP connection on every operation (`CONNECT + TLS + LOGIN … LOGOUT`) — no pooling/keep-alive. Only `EXAMINE + FETCH` is intrinsic to opening a folder; the setup group is the majority of the round-trips and is avoidable on the 2nd+ operation if a connection is reused. ### What's here - **`ImapConnectionCache`** — keeps one authenticated `Store` alive per account, guarded by a per-account mutex, keyed by connection identity (host/port/user/security/mechanism — **not** the rotating secret), with lazy catch-and-retry-once stale handling. `closeReusedConnections()` is the only eviction today. - **`ImapClient` flag** — a `reuseConnections` boolean, **OFF by default** via the `@Inject` no-arg constructor. With it off, `withStore` is byte-for-byte the previous connect + `LOGOUT`-per-call and the cache is never allocated, so it cannot destabilize `main`. Only the harness opts in. - **Harness proof** — `ImapFolderOpenLatencyTest` flips the flag on and asserts the collapse against real in-process IMAP (`CountingImapProxy` + GreenMail). Localhost is ~0 RTT, so this proves the connection is **reused** (structure), not wall-clock latency — no numbers are fabricated. - **`docs/perf/issue-125-connection-reuse-spike.md`** — prototype design, the flag-off-vs-on proof, per-decision trade-offs, feasibility verdict, and the refined real-device validation plan. ### Deterministic proof (flag off vs on), for N = 3 folder-opens | Scenario | TCP conns | LOGIN | EXAMINE | LOGOUT | |----------|-----------|-------|---------|--------| | Flag OFF — N folder-opens | N (3) | N (3) | N (3) | N (3) | | **Flag ON — N folder-opens** | **1** | **1** | N (3) | **1** | | Flag OFF — open folder + read msg | 2 | 2 | 1E+1S | 2 | | **Flag ON — open folder + read msg** | **1** | **1** | 1E+1S | **1** | Setup drops from *once per operation* to *once per account*; the intrinsic per-folder `EXAMINE` is unchanged. All 6 cases pass on the JVM fast gate. ### Deliberately left open (needs measurement, not a guess) Single connection vs. bounded pool (head-of-line blocking), idle-eviction timeout, stale-probe strategy (NOOP vs retry), IDLE per-account connection budget (#90 — reuse is an *additional* socket to IDLE), low-battery eviction (#88/#89/#90), and mutation-retry idempotency. See the doc for the per-decision stance + the 6-step real-device validation plan. ### Scope IMAP client / connection lifecycle + the GreenMail harness + the design doc only. No DB/keystore (#118) or mailbox UI (#130) changes. IDLE is untouched (keeps its own dedicated connection). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.