Skip to content

fix(#696): §4.7.1 gap origin = read invocation start, not summary start#714

Merged
FileSystemGuy merged 3 commits into
mainfrom
fix/696-failover-callout-gap
Jul 8, 2026
Merged

fix(#696): §4.7.1 gap origin = read invocation start, not summary start#714
FileSystemGuy merged 3 commits into
mainfrom
fix/696-failover-callout-gap

Conversation

@FileSystemGuy

Copy link
Copy Markdown
Contributor

Summary

  • Fixes the three-way contradiction reported in §4.7.1: mandated write/read split can't meet its own ≤30s gap (read-invocation startup ~46s); submission_checker accepts combined mode — please reconcile #696 between Rules.md §4.7.1 prose, the submission checker's behavior, and physical achievability. The prose demanded a split-mode workflow with a ≤30s gap measured from write.summary.end_time to read.summary.start_time — but the read invocation's own framework startup (Python imports, MPI spawn, CAP env-validation, ~50s in the reporter's evidence) is recorded inside start_time, so the mandated split hard-fails 4.7.1 on a legitimate configuration. Meanwhile the checker also accepted combined mode with no gap check, so a submitter following the prose failed while a submitter following the checker passed.
  • Reframes §4.7.1 around what it was actually meant to police — the submitter's failover callout between the writing and reading clients — not framework startup. Split mode's real purpose is to enable storage architectures that need an external callout (e.g., a submitter script) to complete the client-to-client handoff; the 30-second bound exists to prevent long manual procedures, not to time cache flushing.
  • Emits a new invocation_start_time field in run metadata, captured at mlpstorage main.py entry before any heavy imports. Storage-only change; zero DLIO edits.

Details

Rules.md

  • §4.7.1 rewritten: universal write→read ordering (both modes) up front; combined vs. split described by workload semantics rather than CLI flag names; split-mode's purpose reframed as failover callout (with cache flush as one common in-callout activity, not the point of the split); gap origin defined as read.metadata.invocation_start_time − write.summary.end_time; negative gap treated as NTP clock skew, not a causality violation.
  • Table 3 footnote: "two-invocation cache-flush workflow" → "two-invocation failover workflow".

Code (storage-only)

  • mlpstorage_py/_invocation.py (new): captures INVOCATION_START = time.time() at first import.
  • mlpstorage_py/main.py: imports _invocation before any heavier mlpstorage_py module so the timestamp is recorded before framework startup.
  • mlpstorage_py/benchmarks/base.py: Benchmark.metadata emits invocation_start_time (ISO local naive, matching DLIO's summary.json format) for every run — write, read, and combined.
  • mlpstorage_py/submission_checker/checks/checkpointing_checks.py::cache_flush_validation: reads the gap-start from read_metadata["invocation_start_time"] instead of read_summary["start_time"]. New "missing invocation_start_time" error tells older submissions to regenerate. Negative gap → warn_violation (clock-skew note), not a hard error. The strict no-overlap ordering check remains in checkpoint_invocation_structure unchanged.

Tests

  • mlpstorage_py/tests/conftest.py: fixture always emits invocation_start_time in checkpointing metadata. Existing chkpt_cache_flush_gap_seconds kwarg now controls the read metadata's invocation_start_time relative to write summary end_time; may be negative to model clock skew. New opt-out kwarg chkpt_omit_invocation_start_time exercises the missing-field branch.
  • test_checkpointing_check_phase2.py: "missing timestamps" assertion updated for the new write-side message; new tests for missing-invocation_start_time (regenerate-required) and negative-gap (clock-skew warning).

Empirical impact on the reporter's evidence

Using the exact timestamps from the #696 evidence:

Quantity Old check New check
Origin (read side) read.summary.start_time = 03:57:12.759 read.metadata.invocation_start_time03:56:21.296 (mlpstorage entry, ~60ms after drop_caches ended)
Origin (write side) write.summary.end_time = 03:56:18.199 unchanged
Gap 54.6 s → FAIL ~3.1 s → PASS

The unmeetable ceiling becomes trivially meetable without loosening the semantic that the rule was actually trying to enforce.

Test plan

  • pytest mlpstorage_py/tests tests/unit -q → 3423 passed, 1 skipped, 0 failures.
  • cache_flush_validation covered for: combined mode (silent pass), split with 25s gap (pass), split with 45s gap (fail), missing write end_time (fail w/ regenerate hint on write side), missing read invocation_start_time (fail w/ regenerate hint), negative gap (warning, not violation).
  • Reviewer: verify the Rules.md §4.7.1 prose accurately describes the failover-callout intent for your storage system.

Closes #696.

The §4.7.1 30s gap check measured
read.summary.start_time - write.summary.end_time, which charges the
read invocation's own framework startup (Python imports, MPI spawn,
CAP env-validation — ~50s in the wild) against the failover-callout
budget and makes a legitimate split unmeetable. Rules.md §4.7.1 also
demanded a split that the checker no longer required, leaving
submitters between contradictory instructions.

Rules.md §4.7.1 rewritten to (a) lead with the universal write→read
ordering common to both combined and split modes, (b) reframe split
mode's purpose as enabling an external failover callout (with cache
flush as one common in-callout activity, not the point of the split),
(c) define the gap origin as
read.metadata.invocation_start_time - write.summary.end_time, and
(d) treat a negative gap as NTP clock skew rather than a causality
violation. Table 3 footnote follows suit.

Implementation:
- mlpstorage_py/_invocation.py captures INVOCATION_START at first
  import; main.py imports it before any heavier mlpstorage_py module.
- benchmarks/base.py Benchmark.metadata emits invocation_start_time
  (ISO local naive, matching DLIO summary.json format) for every run.
- checkpointing_checks.py cache_flush_validation reads the read-side
  origin from read_metadata; missing field → regenerate-required
  error; negative gap → warn_violation clock-skew note (not a hard
  violation).
- Fixture and tests updated; new coverage for missing-invocation-time
  and negative-gap paths.

Closes #696.
@FileSystemGuy FileSystemGuy requested a review from a team July 8, 2026 01:03
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

MLCommons CLA bot All contributors have signed the MLCommons CLA ✍️ ✅

FileSystemGuy and others added 2 commits July 7, 2026 18:04
Original PR made cache_flush_validation hard-fail with a "regenerate"
message whenever a split-mode read-phase metadata was missing
invocation_start_time. That's the wrong policy for results directories
produced by an mlpstorage version predating this rule: those runs were
legitimately allowed to produce that shape, and forcing a regenerate on
validate is user-hostile.

New behavior for split-mode pairs whose read metadata has no
invocation_start_time:

* Fall back to read.summary.start_time as the gap origin (the legacy
  measurement).
* If the legacy-origin gap is <= 30s → silent pass (matches what the
  old rule would have accepted).
* If the legacy-origin gap is > 30s → warn_violation, not
  log_violation. Honor the rule that was in force when the run was
  produced by warning rather than failing. Message tells the submitter
  the measurement includes framework startup and is not authoritative;
  regenerating with a current mlpstorage produces the honest number.
* If BOTH origins are missing (metadata AND summary) → hard error
  ("no read-side origin") because there's nothing to measure against.

Combined-mode submissions are still silently accepted (no split pairs
= early return = no gap check), so old combined-mode results
directories continue to validate cleanly.

Tests updated:
- `test_split_mode_missing_invocation_start_time_falls_back_to_legacy_origin_and_passes`
  (replaces the previous "regenerate required" assertion).
- New: `test_split_mode_legacy_origin_gap_over_30_emits_warning_not_error`
  covers the downgraded-severity path.

Fixture docstring for chkpt_omit_invocation_start_time updated to
describe the fallback path it now exercises.
@FileSystemGuy FileSystemGuy merged commit b2dc7d9 into main Jul 8, 2026
4 checks passed
@FileSystemGuy FileSystemGuy deleted the fix/696-failover-callout-gap branch July 8, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant