Repository navigation
fix: carry the skipped PVC bind budget into the data-access pod wait - #1696
Merged
Merged
Conversation
wait_for_pvc correctly refuses to block on a StorageClass with volumeBindingMode=WaitForFirstConsumer (binding needs a consumer pod that has not been applied yet), but it returned a plain success result indistinguishable from "the PVC actually bound". The caller had no way to know its 240s pvc_bind_timeout budget went unspent. In step 05 that meant the 120s harness_data_access_timeout wait had to cover two serialised phases, dynamic volume provisioning (which only starts once the pod schedules) plus container start, on half the budget of the easier job. --pvc-bind-timeout, whose help text documents the slow shared-storage backends this hits (weka, ceph, gpfs), never applied on those backends because they are all WaitForFirstConsumer. First standup on a cold volume failed deterministically and the retry passed because the volume was by then provisioned, so it read as flakiness. Report the skipped wait on CommandResult (wait_skipped) and add the unspent bind budget to the data-access pod wait when it is set. On failure the error now names the skipped bind and the two knobs to raise. The run phase inherits step 05, so both pipelines pick this up. Also register --data-access-timeout on the standup subparser and pass it into the standup ExecutionContext. It was only wired for run and experiment, so standup (the phase this failure is reported from, and the phase the issue's LLMDBENCH_DATA_ACCESS_TIMEOUT=900 workaround targets) rejected the flag outright and silently ignored the env var while still listing it as an active override. The failure hint would otherwise name a knob that phase does not have. wait_skipped reflects that the bind wait was skipped, not that the volume was actually unprovisioned: on an idempotent re-standup the PVC may already be Bound. The widened value is only a ceiling, so this costs nothing on the warm path, and the log and error text say "skipped" rather than claiming a deferral that may not have happened. Checking the phase instead would cost an extra kubectl call per standup to buy nothing. The other two wait_for_pvc callers in step 04 are not affected: their downstream waits are the model download job (2400s) and the serving deploy (1500s/900s), both already larger than pvc_bind_timeout. Fixes llm-d#1694 Signed-off-by: Kush Zingade <[email protected]>
UgaTheDev
requested review from
Vezio,
achandrasekar,
kalantar,
maugustosilva,
mengmeiye and
namasl
as code owners
July 30, 2026 12:01
maugustosilva
approved these changes
Jul 31, 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.
Description
Fixes #1694.
CommandExecutor.wait_for_pvccorrectly refuses to block on a StorageClass withvolumeBindingMode: WaitForFirstConsumer, because binding needs a consumer podthat has not been applied yet. But it returned a plain success result,
indistinguishable from "the PVC actually bound", so the caller had no way to know
its 240s
pvc_bind_timeoutbudget went unspent.In step 05 that meant the 120s
harness_data_access_timeoutwait had to covertwo serialised phases, dynamic volume provisioning (which only starts once the
pod schedules) plus container start, on half the budget of the easier job. And
--pvc-bind-timeout, whose help text is where the project already wrote downthat slow shared-storage backends (weka, ceph, gpfs) need minutes, never applied
on those backends, because they are all
WaitForFirstConsumerin the commoncase. First standup on a cold volume failed deterministically and the immediate
retry passed because the volume was by then provisioned, so it presented as
flakiness.
The change reports the skipped wait on
CommandResult(wait_skipped) and addsthe unspent bind budget to the data-access pod wait when it is set. On failure
the error now names the skipped bind and the two knobs to raise. The run phase
inherits step 05, so both pipelines pick this up.
It also registers
--data-access-timeouton the standup subparser and passes itinto the standup
ExecutionContext. That knob was only wired forrunandexperiment, so standup, which is the phase this failure is reported from andthe phase the issue's
LLMDBENCH_DATA_ACCESS_TIMEOUT=900workaround targets,rejected the flag outright and silently ignored the env var while
cli.py'soverride table still advertised it as active. Without this, the new failure hint
would name a knob that phase does not have.
Two deliberate calls, both also recorded in the commit message:
wait_skippedmeans the bind wait was skipped, not that the volume wasactually unprovisioned. On an idempotent re-standup the PVC may already be
Bound. The widened value is only a ceiling, so it costs nothing on that warmpath, and the log and error text say "skipped" rather than claiming a deferral
that may not have happened. Probing
.status.phaseto tell the two apart wouldcost an extra kubectl call on every standup and every run, and would add a new
question (what does the flag mean when the probe itself errors or returns
empty) to make a diagnostic string marginally more specific.
wait_for_pvccallers, instep_04_model_namespace.py, need noadjustment: their downstream waits are the model download job (2400s) and the
serving deploy (1500s/900s), both already larger than
pvc_bind_timeout. Thedocstring says so, so it does not read as a rule that is violated in-tree.
Not done here: the issue's suggestion (2), distinguishing "pod Pending because
its PVC is still provisioning" from "pod failed to start". That needs the pod
wait to inspect the PVC's own phase and is a separate change to
wait_for_pods. Suggestion (3) is covered by the new hint text.Type of change
How Has This Been Tested?
Adds
tests/test_harness_pvc_bind_deferral.py(5 tests), which drives the realHarnessNamespaceStepagainst a stub executor: the deferred-bind path extendsthe wait to
120 + 240, the non-deferred path does not, the failure messagenames the cause and the knobs, and the real standup subparser accepts
--data-access-timeout 900.The
--ignoreis a pre-existing gap in my environment, not something thischange causes:
tests/test_direct_service_mode.pyneeds the externalllm-d-plannerdependency, and it fails the same way onmainhere.Confirmed the new tests fail without the change, reverting one file at a time in
a throwaway worktree:
step_05_harness_namespace.pyonly:2 failed, 3 passed.test_deferred_bind_extends_data_access_waitfails withassert 120 == (120 + 240), which is issue WaitForFirstConsumer PVCs hand the provisioning wait to a 120s timer, so the first standup on slow RWX storage always fails #1694 reproduced numerically, andtest_deferred_bind_failure_names_the_causefails withassert 'WaitForFirstConsumer' in 'Data access pod not ready: Timed out after 120s waiting for pods',which is
main's exact unhelpful message.interface/standup.pyandcli.py:3 failed, 2 passed,the new flag test failing with
error: unrecognized arguments: --data-access-timeout 900.executor/command.py:5 failed, allTypeError: CommandResult.__init__() got an unexpected keyword argument 'wait_skipped'.Test Configuration
standup/run/teardown sequence was not run, and the
WaitForFirstConsumerbehaviour itself is exercised through a stub executor rather than against real
storage. The arithmetic and the message are covered; a real cold-volume
standup on a slow RWX backend is not something I can verify here, and I would
rather flag that than imply otherwise.
Checklist
./setup/standup.sh->run.sh->./setup/teardown.shsequence completed successfullypre-commit runwas run and all checks passedllmdbenchmark/executor/README.md,llmdbenchmark/interface/README.md)On the two unchecked boxes: no cluster here, and
pre-commit'srender-validation hook needs a populated
.venvplushelm, which thisenvironment does not have. I ran
ruff check,ruff format --checkand thefull pytest suite directly instead. This diff touches no rendering code.