prevent cross-architectural leakeage in matrix_job_re - #76
Conversation
…by_arch # Conflicts: # scripts/receive_therock/tests/therock_update_status_json_test.py # scripts/receive_therock/therock_update_status_json.py
HereThereBeDragons
left a comment
There was a problem hiding this comment.
branch needs to be merged into develop
| variants = _derive_variants(workflow_run, arch=arch) | ||
| status = _run_status(workflow_run) | ||
| if arch is not None and variants: | ||
| status = Variant.rollup_status(variants, status) |
There was a problem hiding this comment.
i think here is the same question like in the other pr: _run_status should be part of the variants status, not the fallback?
There was a problem hiding this comment.
arch here is only ever set when the run reports multiple architectures, so _run_status (workflow_run) is a whole-run aggregate across all of them. Folding it into the rollup would broadcast one shared conclusion onto every architecture, and that's what we're trying to fix. added a comment to clarify
| statuses.append(leaf.status) | ||
| candidate = leaf.model_copy( | ||
| update={ | ||
| "status": rollup_statuses(statuses, leaf.status), |
There was a problem hiding this comment.
leaf.status does decide here, just earlier and conditionally "if single_arch: statuses.append(leaf.status)", which is intentional. that's what keeps a whole-run failure from being masked when there's only one architecture, without broadcasting it when there are several.
| # Extracts the arch a job's own "Test | <arch>" segment names, if any, so | ||
| # jobs from different architectures nested under the same cell are never | ||
| # grouped together as if they were one architecture's result. | ||
| _TEST_ARCH_JOB_RE = re.compile( |
There was a problem hiding this comment.
coudl use _GPU_FAMILY_RE from therock_classify.py
There was a problem hiding this comment.
Can't reuse _GPU_FAMILY_RE, it's unanchored, and job names in this pipeline commonly carry a runner-label segment that embeds its own gfx-shaped substring alongside the real "Test | <arch_something>" segment. Scanning unanchored would pick up both as separate "architectures" for the same job and reintroduce the leakage we're trying to fix. I did pull the token shape out into a shared GPU_FAMILY_TOKEN constant in therock_classify.py so the two regexes can't drift independently.
| # derived from the run's full, arch-blind job list -- into every target | ||
| # arch's slot. Re-derive a leaf scoped to each arch so one architecture's | ||
| # result can never be attributed to another's. | ||
| multi_arch = len(targets) > 1 and cls.pipeline_type in _VARIANT_AXIS_KEY |
There was a problem hiding this comment.
right now we should not hit this branch as we currently only have: each test has a single gpu, not multiple different gpus.
as we might have in the future such test, claude suggest to add the following regression test:
def test_tmp_multi_arch_test_event_does_not_leak_or_alias_across_architectures() -> None:
# TEMP (review): pins the multi_arch branch in _merge_run_into_document.
run = _variant_run(
pipeline_type="pytorch",
pipeline_phase="test",
architectures=["gfx942", "gfx1101"],
run_id=902,
conclusion="failure",
jobs=[
_job("py 3.12 | torch release/2.10 / Test | gfx942"),
_job(
"py 3.12 | torch release/2.10 / Test | gfx1101",
conclusion="failure",
),
],
)
doc = StatusDocument()
tusj._merge_run_into_document(doc, run, tusj._create_leaf(run))
gfx942 = doc.pipelines.pytorch.test["linux"]["gfx942"]
gfx1101 = doc.pipelines.pytorch.test["linux"]["gfx1101"]
assert gfx942.status is Status.success
assert gfx1101.status is Status.failure
assert gfx942 is not gfx1101
assert gfx942.variants is not gfx1101.variants
HereThereBeDragons
left a comment
There was a problem hiding this comment.
see the one comment otherwise lgtm
| # test jobs get refreshed into already-existing per-arch test leaves): | ||
| # this pins _merge_run_into_document's own multi_arch branch, for a | ||
| # single *test*-phase event that itself reports more than one | ||
| # architecture in cls.architectures. No dispatcher today fans a single |
There was a problem hiding this comment.
the "no dispatcher" part probably gets stale. either remove or otherwise at least add some date to it so one knows how old the statement is
|
(maybe double check pr description is up to date) |
Motivation
status.json's matrix-cell key for PyTorch/JAX fan-out builds is parsed purely from job names as(py, ref)— it carries no GPU architecture. When a single build cell (e.g.py 3.12 | torch release/2.10) nests test jobs for multiple architectures (e.g.gfx942andgfx1101), all of those jobs collapse into one shared, worst-of variant.That conflated variant then gets broadcast onto every architecture's test leaf by
_refresh_same_run_fanout_tests, so one architecture's failure silently drags down another architecture's otherwise-passing result (and vice versa) instatus.json, even though the two GPUs' actual test outcomes are independent.closes #75
Technical Details
_TEST_ARCH_JOB_RE/_job_matches_archto detect which architecture (if any) a job names in its own"Test | <arch>"segment, so a job can be classified as either arch-agnostic (the cell's shared build step) or scoped to one specific architecture._variants_from_jobs,_derive_variants, and_create_leafnow accept an optionalarchparameter that filters the job list down to that architecture's own jobs plus any arch-agnostic jobs before deriving matrix-cell variants._refresh_same_run_fanout_testsnow re-derives each existing test leaf's variants scoped to its own architecture, instead of reusing the build leaf's arch-blind variant list wholesale. It also rolls the leaf'sstatusup from those scoped variants (previously it copied the raw run conclusion directly, which doesn't reflect the arch-specific outcome either)._merge_run_into_documentnow re-derives a per-architecture leaf when a single event legitimately reports more than one target architecture, closing the same conflation risk (and a related object-aliasing hazard) on the primary write path, not just the same-run refresh path.Test Plan
test_completed_fanout_build_does_not_leak_status_across_architectures, which sets up two architectures (gfx942passing,gfx1101failing) nested under the same(py, torch)build cell and asserts each architecture's test leaf reflects only its own outcome. also added test_multi_arch_test_event_does_not_leak_or_alias_across_architectures.gfx942's leaf incorrectly flips tofailure) and passes with the fix applied.scripts/receive_therocktest suite and checked for regressions againstdevelop.Test Result
pytest scripts/receive_therock/tests/therock_update_status_json_test.py: 99 passedpytest scripts/receive_therock/tests/: 401 passedSubmission Checklist