Skip to content

ST: the gRPC transport has no persistent fast channel — file ops pay a round trip and a process each #245

Description

@pdettori

Problem

k8sSandboxExtension deliberately runs a two-tier transport (extension.ts:30-34):

  • fastTransportpersistentExecInPod, ONE long-lived bash with nonce-framed commands, serving the small request/response ops (read/write/edit/ls/find).
  • streamTransport — per-call exec, for the ops that need onData / abort / long-running semantics (bash, grep, user !).

But opts.transport overrides both (extension.ts:38, :49-51), and run-leaf.ts:786 passes exactly that for a leased gRPC presence record:

k8sSandboxExtension({ config: selected?.config ?? null, transport: selected?.transport })

So on the gRPC path the fast tier does not exist. Every file op becomes a full SandboxExec.Exec round trip through the relay, ending in a fresh bash -c in the sandbox. On the kubectl path the same op is one framed line on an already-running bash.

This is a silent asymmetry between transports of the kind ADR-0024 and transport.ts's conformance battery exist to prevent — the battery pins the output cap and the exec-timeout ceiling across all three implementations, but nothing pins whether the fast tier is available, so a caller above the seam cannot see or control the difference.

Why it matters more now

File ops are the highest-frequency tool calls in a coding agent. Under P6 (#244) a single host runs W workers × S multiplexed sessions against K sandbox containers, so per-op process churn concentrates: what is one extra round trip at 1:1 becomes W×S round trips and W×S bash spawns against K containers.

E6/E7 measured per-sandbox duty at 2–8% of leaf wall-clock, and E7 validated mixed-ref converge correctness at 6 concurrent refs on one pod. P6's rungs go well past that, so whether duty holds under this load is an open question rather than a settled one. P6 §5.2 records per-file-op latency and sandbox-container CPU precisely so a sandbox-bound run is not misreported as a harness density limit.

Why this is not P6's to fix

The fix lifts both substrates equally. Folding it into P6 would improve the VM arm and the Knative arm at the same time, while adding transport surface to a slice whose job is to measure the deployment tier. P6 instead stays correct without it by pinning both E9 arms to the relay + gRPC transport (§5.3), so the comparison varies the deployment tier and nothing else.

Shape of the fix

Two independent pieces; the first is small.

1. Let the extension take the two tiers separately. Today opts.transport means "both". Either add an optional fast override, or accept { fastTransport?, streamTransport? } with transport kept as the both-tiers shorthand for back-compat. Purely a signature change; no protocol involved.

2. Give the remote path a persistent channel. persistent-exec.ts is transport-agnostic almost everywhere — framing (framing.ts, wrapCommand, nonce matching), producer-side cap, transparent fallback and timeout handling are all generic. It is kubectl-specific in exactly two places:

  • the binary name at :84spawnFn('kubectl', …)
  • buildPersistentKubectlArgs at :12exec -i -n <ns> <pod> -- bash

So a container-exec variant (docker exec -i <container> bash, or podman) is a parameterized argv rather than a new protocol, and it declares the same producer-side-cap truncation mechanism the conformance battery already knows.

A relay-mediated persistent channel is the larger alternative: it needs a long-lived shell concept on the wire (sandbox.proto), since Exec is currently one command per RPC. Worth considering only if the local-container variant proves insufficient — it is the option that would also serve a genuinely remote worker.

Suggested acceptance

  • The extension can be given a fast tier and a stream tier independently.
  • A container-exec persistent channel exists and passes the shared conformance battery (test/conformance.ts) declaring producer-side-cap.
  • A test pins fast-tier availability per transport, so a fourth transport cannot silently ship without one — the same discipline transport.ts:24-32 applies to truncated.
  • Measured: per-file-op latency before/after on the gRPC path, at a P6-style concurrency rung.

References


🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions