Commit 243960d
feat: wire shared invariant functions into all production check_invariant! calls (#6344)
* fix: SS-2 invariant — nulls always sort last, regardless of direction
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>
* test: verify model detects null ordering violations
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>
* feat: add SS-2 production invariant check using shared model function
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>
* refactor: use shared invariant functions for all TW-2 checks
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>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 9f49c8d commit 243960d
3 files changed
Lines changed: 117 additions & 4 deletions
Lines changed: 3 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
51 | | - | |
| 51 | + | |
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
90 | | - | |
| 90 | + | |
| 91 | + | |
91 | 92 | | |
92 | 93 | | |
93 | 94 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
532 | 532 | | |
533 | 533 | | |
534 | 534 | | |
535 | | - | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
536 | 539 | | |
537 | 540 | | |
538 | 541 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
73 | 76 | | |
74 | 77 | | |
75 | 78 | | |
| |||
308 | 311 | | |
309 | 312 | | |
310 | 313 | | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
311 | 325 | | |
312 | 326 | | |
313 | 327 | | |
| |||
552 | 566 | | |
553 | 567 | | |
554 | 568 | | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
555 | 664 | | |
556 | 665 | | |
557 | 666 | | |
| |||
0 commit comments