Repository navigation
api: pin the pooled-setup error arms #273 called unreachable - #326
Merged
Merged
Conversation
The characterization of apply_adopted_per_sandbox_setup called two of its
error arms effectively unreachable through the retrying node-api client.
Both are reachable, and their cases now use production-shaped inputs.
Tests only; no production code changes.
The network-policy step's Unavailable -> 503 arm. A rate-limited
ResourceExhausted with a server retry hint is replayed after exactly that
hint, with no lower bound, and it spends the same retry count as
Unavailable. sbx-mgr's Unix-socket rate limiter answers with a hint of
0 ms. Node-api relays SetSandboxNetworkPolicy statuses unchanged. After
twelve such rejections, sbx-mgr's "requires a live net-gateway"
Unavailable arrives with the client's retries spent and comes back raw,
well inside the 5 s setup budget. A new case,
policy_unavailable_after_rate_limits, scripts that sequence. It expects
503, reclaimable, and exactly thirteen calls.
The metadata update's Cancelled -> 408 arm. When node-api's connection
to sandbox-manager closes while an UpdateSandbox forward is still queued,
hyper cancels it and tonic reports Cancelled("operation was canceled").
Node-api returns that unchanged for a state-writing RPC. Without its
local "transport error" source the API's client does not replay it. The
update_cancelled case now scripts that status instead of "Timeout
expired", which tonic produces only after timers the 5 s budget always
preempts.
The scripted node-api gains ScriptedReply::Sequence, which answers
successive calls to one method with successive replies.
ac-pplx
force-pushed
the
ac/api-remove-unreachable-pooled-arms
branch
from
October 12, 2026 21:14
40549c5 to
b4b5f40
Compare
ac-pplx
marked this pull request as ready for review
October 12, 2026 21:31
Collaborator
Review — 2 findingsMedium
Low
Verified
Verified against |
the-gigi-pplx
approved these changes
Oct 12, 2026
the-gigi-pplx
left a comment
Collaborator
There was a problem hiding this comment.
Approved. 2 non-blocking findings in the review comment above.
This was referenced Oct 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pins the two error arms of
KubernetesSubstrate::apply_adopted_per_sandbox_setup(kubernetes/adapter/pooled.rs) that #273's characterization called effectively unreachable through the retrying node-api client. Both arms are reachable. Their cases now script the statuses production delivers. Tests only: no production code changes, and every status mapping and the reclaim classification stay as they are.The two arms and how they are reached
Network-policy step,
Unavailable→ 503.GrpcClient::callreplays a rate-limitedResourceExhaustedthat carries a server retry hint after exactly that hint, with no lower bound (common/grpc/src/retry.rs,retry_delay_for_status). That replay spends the same retry count asUnavailable.conservative_uds_grpc_rate_limiter_layer, a per-peer GCRA limit of 20,000 requests per second. Its request-rate rejection israte_limited_status(…, Some(retry_after))withretry_afterat most the 50 µs interval, so the hint reads 0 ms. The rejection is not marked refused-before-start.SetSandboxNetworkPolicywithRpcReplaySafety::RequiresProofand relays statuses unchanged.DEFAULT_TCP_GRPC_CONNECT_RETRY_POLICY.max_retries(12) such rejections, sbx-mgr's freshUnavailable("synchronous network policy update requires a live net-gateway") meetsbackoff_delay(12) == None. It comes back raw, well inside the 5 sWARM_POOL_SETUP_RPC_TIMEOUT, and the arm maps it to 503.Metadata update,
Cancelled→ 408.UpdateSandboxover its Unix-socket tonic channel to sandbox-manager. When that connection closes while the forward is still queued in hyper's HTTP/2 dispatch channel, hyper fails it as canceled ("connection closed"). Tonic reportsCancelled("operation was canceled"), with a localtransport errorsource.RequiresProofreturns that status unchanged.is_temporary_tcp_failure, shared byGrpcClient::callandretry_grpc_status_with_context) matches none of its fragments, so the client returns it without a replay, and the arm maps it to 408.Test changes
pooled_adoption_stops_at_a_failed_setup_step::update_cancellednow scriptsCancelled("operation was canceled"), the status node-api relays. It previously scriptedCancelled("Timeout expired"), which tonic produces only from its 150 s channel timer and 120 s server timer, both preempted by the 5 s budget. The expectation is unchanged: 408, not reclaimable.policy_unavailable_after_rate_limits. The scripted node-api answers the network-policy update with 12 rate-limit rejections carrying sbx-mgr's message and a 0 ms hint, then sbx-mgr's live-gatewayUnavailable. The case expects 503, reclaimable bypooled_prepare_error_is_reclaimable, and exactly 13 calls, so every rejection was replayed and the final status was not. The count comes fromDEFAULT_TCP_GRPC_CONNECT_RETRY_POLICY.max_retries, the policyNodeApiClientuses.scripted_test_doubles.rs) gainsScriptedReply::Sequence, which answers the n-th call to a method with the n-th reply and later calls with the last one. Existing replies behave as before.Mutation proofs
Each edit was temporary and was reverted afterwards.
Unavailablearmpolicy_unavailable_after_rate_limitsfails:left: 500, right: 503Cancelledarmupdate_cancelledfails:left: 500, right: 408left: 13, right: 12): one real backoff replays theUnavailableonce moreleft: 500, right: 503: the client replays theUnavailableuntil the budget ends and reportsDeadlineExceededObservation, not fixed here
A 0 ms rate-limit hint uses up the node-api client's 12-retry count with no backoff. Under node-api → sbx-mgr overload, a setup call therefore ends after 13 back-to-back attempts within milliseconds, instead of spreading its replays over the 5 s budget.
Host safety
The tests run in their own re-executed process against a scripted node-api on an ephemeral
127.0.0.1port. They reach no fixed host path, socket, firewall or netns.Overlap with open PRs
No open PR touches
adapter_characterization_tests/pooled_adoption.rsorscripted_test_doubles.rs.Validation
cargo fmt --all -- --checkcargo clippy --workspace --locked --all-targets --all-features -- -D warningscargo nextest run --locked -p pplx-space-api --all-features --retries 0, three consecutive runs withSPACE_CI_REQUIRE_TEST_DEPS=1(Postgres and Redis via the nextest setup scripts)#[ignore]400k-sandbox occupancy benchmark), 0 dependency skipsA Kind run of an earlier revision of this branch saw
local.deferred_btrfs_send_capture_restores_cross_nodefail once and pass on rerun. It is unrelated to this diff and was not reproduced on the base commit. The failure was node-manager's runtime-artifact cleanup (node/mgr/server/src/service/cleanup_helpers.rs,inspect …/instance: Invalid argument (os error 22)), reached from the suspend cleanup. It left the prior-generation podTerminating, so the restore deferred with 503.🤖 Generated with Claude Code