Skip to content

Commit bfc6f32

Browse files
tune: PIPELINE_DEPTH 2 → 8 (match TLS pool min)
1 parent 1a530de commit bfc6f32

1 file changed

Lines changed: 32 additions & 9 deletions

File tree

src/tunnel_client.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,13 +1651,36 @@ async fn tunnel_loop(
16511651
}
16521652

16531653
/// Maximum number of `data` ops a pipelined session keeps in flight
1654-
/// at any one time. Two parallel batches per session is enough to
1655-
/// overlap one Apps Script round-trip with the next on a single TCP
1656-
/// stream — that's the throughput win. Higher depths run into
1657-
/// diminishing returns (server-side seq enforcement serializes the
1658-
/// drains anyway) and burn more Apps Script quota on speculative
1659-
/// polls when a session goes idle.
1660-
const PIPELINE_DEPTH: usize = 2;
1654+
/// at any one time. Matches the TLS connection-pool minimum so a
1655+
/// single active session can fill every warm connection's transit
1656+
/// window when client→Apps Script is the dominant leg (the censored-
1657+
/// network case this project is built for).
1658+
///
1659+
/// Server-side processing stays serial via the per-session seq lock,
1660+
/// so this depth doesn't translate to parallel upstream writes — but
1661+
/// it does parallelize the slow client↔AS leg's RTT. With depth=8 the
1662+
/// slow leg is used for ~8 ops simultaneously instead of one at a
1663+
/// time, which is the actual bottleneck in throttled networks.
1664+
///
1665+
/// Costs that grow with depth:
1666+
/// * Client-side reorder-buffer memory: one oneshot receiver per
1667+
/// in-flight op, bounded by depth. With 64 KB chunks the per-
1668+
/// session worst case is `depth × 64 KB` of payload sitting in
1669+
/// the mux pipeline.
1670+
/// * Per-deployment semaphore pressure: a single session can
1671+
/// occupy up to `depth` of a deployment's 30 concurrent slots
1672+
/// (or spread across deployments via `next_script_id`
1673+
/// round-robin). With many active pipelined sessions this can
1674+
/// contend with new connects.
1675+
/// * Seq-loss cascade: if seq=N is permanently lost (rare —
1676+
/// requires the carrying batch to never reach the server), all
1677+
/// subsequent in-flight seqs wait `SEQ_WAIT_TIMEOUT` for it
1678+
/// before failing.
1679+
///
1680+
/// Idle sessions stay at depth=1 regardless of this constant — see
1681+
/// `target_depth` in `tunnel_loop_pipelined` — so the quota cost of
1682+
/// raising this only applies to actively-streaming sessions.
1683+
const PIPELINE_DEPTH: usize = 8;
16611684

16621685
/// Pipelined variant of `tunnel_loop`: keeps up to `PIPELINE_DEPTH`
16631686
/// `data` ops in flight per session, each tagged with a per-session
@@ -2547,8 +2570,8 @@ mod tests {
25472570
});
25482571

25492572
// Receive both ops back-to-back. Loop sends them before
2550-
// entering the receive phase because PIPELINE_DEPTH=2 and
2551-
// both reads succeed in the send phase.
2573+
// entering the receive phase: target_depth ≥ 2 (PIPELINE_DEPTH)
2574+
// and both reads succeed in the send phase.
25522575
let msg0 = tokio::time::timeout(Duration::from_secs(2), rx.recv())
25532576
.await
25542577
.expect("first op not emitted")

0 commit comments

Comments
 (0)