Skip to content

feat: wire shared invariant functions into all production check_invariant! calls#6344

Merged
g-talbot merged 5 commits intomainfrom
gtt/production-invariant-checks
Apr 27, 2026
Merged

feat: wire shared invariant functions into all production check_invariant! calls#6344
g-talbot merged 5 commits intomainfrom
gtt/production-invariant-checks

Conversation

@g-talbot
Copy link
Copy Markdown
Contributor

Summary

Ensures production check_invariant! calls use the same shared invariant functions that the Stateright models use for exhaustive verification. Previously, some checks inlined the logic (e.g., 3600 % duration_secs == 0) instead of calling the shared function.

Changes

SS-2 (new production check):

  • Added verify_ss2_null_ordering to the writer's sort verification path
  • Calls quickwit_dst::invariants::sort::compare_with_null_ordering — the same function the Stateright sort_schema model uses
  • Verifies the shared function returns the expected result (null > non-null for both ascending and descending)
  • Then checks actual data: no null-before-non-null in any sort column when earlier columns are equal

TW-2 (refactored to use shared function):

  • All 3 production check_invariant!(TW2, ...) calls now use quickwit_dst::invariants::window::is_valid_window_duration instead of inlining 3600 % duration_secs == 0
  • Locations: sort_fields/window.rs, storage/writer.rs, split/metadata.rs

Invariant coverage summary

Invariant Shared Function Production check_invariant! Stateright Model
SS-1 (lexsort_to_indices) writer.rs sort_schema
SS-2 compare_with_null_ordering writer.rs (new) sort_schema
SS-5 (direct comparison) writer.rs sort_schema
TW-1 window_start_secs metadata.rs time_windowed_compaction
TW-2 is_valid_window_duration window.rs, writer.rs, metadata.rs (refactored) time_windowed_compaction

Test plan

  • 272 quickwit-parquet-engine tests pass
  • 26 quickwit-dst tests pass (including exhaustive Stateright BFS)
  • cargo clippy clean
  • rustfmt clean

🤖 Generated with Claude Code

g-talbot and others added 4 commits April 26, 2026 16:25
The SS-2 invariant incorrectly specified that nulls sort before non-null
for descending columns. The actual production code uses nulls_first=false
everywhere — nulls always sort after non-null values, regardless of
direction. This is required for sorted_series key correctness: null
columns are omitted from the key, and their keys naturally sort after
keys that include those columns.

Fixed in all locations:
- TLA+ SortSchema.tla: CompareValues and SS2_NullOrdering
- Rust invariants/sort.rs: compare_with_null_ordering
- Rust models/sort_schema.rs: SS-2 property check and compare_values test
- Rust invariants/registry.rs: SS-2 description
- ADR-002: SS-2 invariant table
- Phase 1 design doc: null handling descriptions

Stateright exhaustive_sort_schema BFS passes with corrected model.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added two tests to the Stateright sort_schema model:

- ss2_detects_null_before_non_null_in_descending: constructs rows
  with null before non-null in a descending column, verifies both
  row_leq and is_sorted reject this ordering (nulls always last).

- ss2_accepts_non_null_before_null_in_descending: verifies the
  correct ordering (non-null then null) is accepted.

These confirm the model can catch the exact class of bug that the
SS-2 invariant fix addresses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds verify_ss2_null_ordering to the writer's sort verification path.
This calls quickwit_dst::invariants::sort::compare_with_null_ordering —
the same function the Stateright model uses for exhaustive BFS
verification — to confirm that:

1. The shared invariant function returns Greater for (null, non-null)
   regardless of sort direction (debug_assert at function entry)
2. No adjacent row pair in the sorted output has null before non-null
   in any sort column (when earlier columns are equal)

This bridges the gap where SS-2 was verified by the model but never
checked at runtime in production code. The check runs in debug builds
(via check_invariant! / debug_assert!) and reports to the invariant
recorder in all builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All TW-2 (window_duration divides 3600) checks now call the shared
quickwit_dst::invariants::window::is_valid_window_duration instead
of inlining the `3600 % duration_secs == 0` logic. This ensures
production and model execute identical validation:

- sort_fields/window.rs: validate_window_duration + window_start
- storage/writer.rs: build_compaction_key_value_metadata
- split/metadata.rs: ParquetSplitMetadataBuilder::build

The shared functions used by production check_invariant! calls are
now the same code the Stateright models use:
- SS-2: compare_with_null_ordering (sort.rs)
- TW-2: is_valid_window_duration (window.rs)
- TW-1: window_start_secs (window.rs) — already shared

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@g-talbot g-talbot requested review from mattmkim April 26, 2026 23:42
Base automatically changed from gtt/fix-null-sort-invariant to main April 27, 2026 14:54
@g-talbot g-talbot enabled auto-merge (squash) April 27, 2026 14:54
@g-talbot g-talbot merged commit 243960d into main Apr 27, 2026
9 checks passed
@g-talbot g-talbot deleted the gtt/production-invariant-checks branch April 27, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants