test(odb): establish dynamic store correctness and performance baselines - #2853
Draft
Sebastian Thiel (Byron) wants to merge 10 commits into
Draft
test(odb): establish dynamic store correctness and performance baselines#2853Sebastian Thiel (Byron) wants to merge 10 commits into
Sebastian Thiel (Byron) wants to merge 10 commits into
Conversation
Sebastian Thiel (Byron)
force-pushed
the
odb-parallelism
branch
6 times, most recently
from
July 30, 2026 20:18
c6a213f to
8047243
Compare
Add a gix-testtools-generated SHA-1/SHA-256 ODB catalogue and a shared mutation helper for publishing and removing pack components, rewriting MIDX files, changing alternates, and constructing maintenance-like intermediate states. Exercise deterministic single-threaded interactions across multiple handles, malformed-index recovery, changing packs and alternates, MIDX transitions, and slot over-commitment. This establishes the ordinary regression vocabulary used by the subsequent implementation changes, benchmarks, and fuzz model.
Sebastian Thiel (Byron)
force-pushed
the
odb-parallelism
branch
from
July 31, 2026 07:12
52c92f1 to
d106dfd
Compare
Use the shared deterministic ODB fixture to measure startup scanning, cold and warm packed lookups, repeated misses, prefix disambiguation, pack publication, fixed-slot pressure, concurrent access, and CPU over-commitment before changing the dynamic ODB implementation. Compare the original strict and never-refresh policies, and use Store metrics outside timed paths to verify that each workload reaches its intended refresh behavior. These common benchmark groups remain available after the implementation changes for direct Criterion comparisons.
Make disk reconciliation atomic when slots are exhausted or reused, retry changed indices after a cached load failure, and remember unchanged pack-load failures without repeatedly reopening the same file. Record an attempted lazy initialization for never-refresh handles so misses do not rescan forever, and recover unexpectedly vacant recorded slots through normal reassignment instead of assuming they are unreachable. Existing handles retain their last usable state throughout failed or partial maintenance transitions.
The fixed-size slot mode is a hard upper bound: once all slots are occupied, new disk state yields InsufficientSlots. Calling it Given obscures that behavior, especially now that a separate mode will be able to grow. Rename the public variant and update all workspace callers and documentation. This commit contains only the breaking vocabulary change so downstream adaptation is easy to identify.
Opening a store should not require listing every pack merely to size a fixed slot map, and later maintenance should not fail only because the repository outgrew its opening-time estimate. Add a Growable slot mode whose initial allocation performs no disk scan. Grow by publishing copy-on-write snapshots of stable slot pointers under the existing structural lock, so readers remain lock-free and all prior slot IDs stay valid. AsNeededByDiskState also gains growth after its initial estimate, while Limit retains the former hard-cap behavior. Ordinary tests cover growth past the initial allocation with shared and stable handles.
Publish the slot index and its backing resource nodes through one catalog so handles cannot combine refresh generations or lazily load a pack through a reassigned slot. Snapshots and stable locations retain the exact index and pack resources they observed, without Store-local PackId encoding or artificial slot/member bit limits. Recover republished indices and packs only when their on-disk state actually changed, preserve loaded MIDX packs across redundant loads, and replace rewritten MIDX resources at their logical slot even at a configured limit. Add test-only synchronization hooks and make contending index loads, pack loads, and refreshes share their success or error outcome. This prevents duplicate I/O and keeps failed maintenance transitions from partially replacing usable state.
Add RefreshMode::AfterDuration as a shared gix-odb freshness window. Repeated misses avoid rescanning packs and alternates until the last successful post-initialization refresh reaches the configured age, while failed refreshes, known missing packs, and explicit stale notification remain immediately retryable. Configure gix object handles with a one-second default and expose gitoxide.objects.refreshAfter in milliseconds. Fetch invalidates freshness after publishing a pack, configuration reloads update active handles, and zero restores strict refresh-on-every-miss behavior. Use the test-support clock for deterministic deadline and contention coverage, and keep feature-unified workspace builds and Repository size guards valid with the added configuration.
Add one deterministic single-threaded opcode VM for multiple handles, component-wise pack publication and removal, MIDX replacement, alternates, malformed resources, loose-object lifecycle, slot pressure, stable locations, iteration, prefix operations, and refresh policies. Give every opcode an explicit reproducible outcome and validate queries, shared-store checkpoints, retained locations, refresh counts, and fixture convergence against the generated disk model. Deterministic time covers strict, duration-based, and never-refresh behavior without wall-clock races. Keep readable corpus seeds for interrupted maintenance, load recovery, stale snapshots, cloned handles, MIDX transitions, growable stores, and minimized regressions.
Extend the pre-change dynamic-store baseline with scan-free growable opening, first-growth cost, fresh and expired duration policies, and post-publication discovery through an expired freshness window. Retain the common strict and never-refresh benchmark groups unchanged so Criterion results remain directly comparable across the implementation boundary, while the additional cases measure behavior that did not exist in the baseline API.
The minimal Linux test job does not install ripgrep. Its inverted pipeline therefore succeeded because rg was missing, silently skipping the assertion that a no-default-features gix build excludes gix-imara-diff. Use POSIX grep with extended regular expressions, which is already required by neighboring checks and preserves the exact dependency assertion on every CI image.
Sebastian Thiel (Byron)
force-pushed
the
odb-parallelism
branch
from
July 31, 2026 11:15
d106dfd to
af4b335
Compare
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.
Tasks
Codex inserts after this line
Purpose
This PR establishes the correctness and performance baseline for upcoming work on the multi-handle, multi-threaded dynamic object database. It intentionally changes test infrastructure rather than production ODB behavior: the aim is to make filesystem transitions, refresh behavior, slot pressure, and handle interactions reproducible before changing the implementation.
The infrastructure is intended to support this sequence of follow-up work:
Refresh::Never.What this adds
Shared ODB scenarios
A
gix-testtoolsfixture script builds a deterministic catalog of valid SHA-1 and SHA-256 packs and indices. A utility module undergix-odb/tests/copies and mutates that catalog for tests, benchmarks, and fuzzing without introducing another support crate.The shared vocabulary can:
This keeps all three kinds of coverage grounded in the same well-known repositories and object IDs.
Deterministic correctness coverage
The integration tests exercise multiple live handles in deliberately interleaved, single-threaded sequences while:
The existing threaded packed-object smoke test also now always uses at least two workers and verifies that every enumerated object is actually found.
Benchmarks
Criterion benchmarks provide baselines for:
Refresh::Never;These should make regressions and improvements in refresh suppression, prefix lookup, startup behavior, and slot over-commitment visible.
Deterministic fuzz VM
A single-threaded bytecode VM interleaves operations across several handles with disk mutations. It covers lookup, header access, iteration, prefix queries, refresh modes, pack-component transitions, MIDX transitions, alternates, malformed indices, and slot pressure.
The seed corpus includes standalone packs, MIDX use, alternates, malformed recovery, slot pressure, pack replacement, MIDX expiration, and interrupted maintenance. Maintenance states can expose markers or new components before old packs are removed, remove packs while a MIDX still references them, and abandon or reverse transitions.
The VM checks returned object hashes, header/data agreement, fresh-store equivalence at valid checkpoints, and existing handle metrics. Store statistics can be used or extended further where they help characterize later correctness or performance changes.
Verification
cargo test -p gix-odb --all-features --lib --test odbcargo clippy -p gix-odb --all-features --tests --benches -- -D warnings