fix(server): surface Pool capacity exhaustion - #1581
Conversation
Pangjiping
left a comment
There was a problem hiding this comment.
Reviewed the Pool capacity exhaustion change. Overall the approach is solid (conservative capacity classification, additive condition, consistent spec/SDK/CRD/docs updates). A few inline comments below, mostly around requeue gating, condition lifecycle, and status-patch consistency.
|
Hi @Pangjiping, thank you for approving the PR and updating the branch with The refreshed CI is currently blocked by Java E2E (docker bridge), which fails during test compilation because I do not have permission to rerun Actions in the upstream repository. Could you please rerun the failed jobs when convenient? Thanks! |
Summary
PoolAllocationPendingBatchSandbox condition when fixed or automatically selected Pools cannot satisfy the remaining allocation because they are atpoolMax.KUBERNETES::POOL_CAPACITY_EXHAUSTEDandRetry-Afterinstead of the generic Pod readiness timeout.Fixes #1578.
Problem
A create request using
extensions.poolRefcan remain pending for the entire sandbox create timeout when the selected Pool has reachedpoolMax. The Server currently cannot distinguish this allocation back-pressure from an ordinary Pending workload, so the request eventually returns504 KUBERNETES::POD_READY_TIMEOUT.Pool capacity pressure and Pod startup failures require different caller and operator actions. Capacity may recover while the request is waiting, but persistent exhaustion should remain machine-identifiable.
Minimal reproduction
poolMax: 2.2 allocated / 0 available.POST /v1/sandboxesrequest with the sameextensions.poolRef.Before this change, the request waits for the overall create timeout (approximately 180 seconds with the reproduced configuration) and returns
KUBERNETES::POD_READY_TIMEOUT.Root cause
The controller enforces capacity, but neither allocation path exposed a stable status that the lifecycle Server could consume:
poolRefremained Pending while waiting for a Pool Pod;poolRef: "*"returned an untyped no-eligible-Pool error and recorded a Kubernetes Event;_wait_for_sandbox_readymapped every remaining Pending state to the generic readiness timeout.A Server-side preflight check would be racy because concurrent creates can consume capacity after the check and before controller allocation.
Change
PoolAllocationPendingwith reasonPoolCapacityExhaustedto the BatchSandbox status contract and generated/Helm CRDs.kubernetes.pool_acquisition_timeout_seconds(default: 30 seconds). It measures continuous capacity blockage and never extendssandbox_create_timeout_seconds.KUBERNETES::POOL_CAPACITY_EXHAUSTED, andRetry-After: 5.The HTTP status is intentionally isolated from the machine-readable error code. I used 429 to represent Pool admission back-pressure, but can switch to 503 if that better matches the project's API convention.
Testing
make helm-lintwas also unavailable because Helm is not installed locally)Completed locally:
cd kubernetes && make testcd kubernetes && make lintcd kubernetes && make buildcd server && uv run ruff checkcd server && uv run pytest(1484 passed)cd docs && pnpm docs:buildThe reproduced fixed-Pool behavior used Server 0.2.2 and Controller 0.2.0. A real-cluster run with the modified images remains outstanding.
Breaking Changes
This is additive: a new CRD condition, configuration field, documented 429 response, and generated response handling. A new Server with an older controller safely falls back to the existing Pending/504 behavior because the condition is absent; an older Server ignores the new condition. Pool-not-found, genuine unschedulable, image failure, and ordinary Pod readiness timeout classifications remain unchanged.
Checklist
No authentication or authorization boundary changes. The 429 response does not expose credentials, tenant data, Pod names, or internal infrastructure details, and the generated SDKs do not automatically replay the non-idempotent create request.