Skip to content

[storage/qmdb] Support generalized Merkle family in qmdb state sync#3626

Merged
roberto-bayardo merged 8 commits into
mainfrom
mmb-sync-approach-2
Apr 21, 2026
Merged

[storage/qmdb] Support generalized Merkle family in qmdb state sync#3626
roberto-bayardo merged 8 commits into
mainfrom
mmb-sync-approach-2

Conversation

@roberto-bayardo
Copy link
Copy Markdown
Collaborator

@roberto-bayardo roberto-bayardo commented Apr 18, 2026

Generalize sync protocol on merkle family

Generalizes the QMDB sync protocol over merkle families (MMR/MMB) and tightens the current-DB grafting contract so prune and sync targets can't produce state the receiver will fail on.

Motivation

The sync protocol was hardcoded to mmr::Family throughout. current::Db::prune silently clipped prune_loc to an internal "settled" boundary, so callers could believe they had pruned to prune_loc while the ops log actually retained an older start — the kind of mismatch that breaks delayed-merge sync targets.

What changed

  • Family generalization: Target, FetchResult, Requests, EngineError, Error, Database, Resolver, Journal<F> and all their impls/macros now carry a family parameter. immutable/keyless sync impls and their resolver macros no longer hardcode mmr::Family.
  • current::Db grafting contract: new sync_boundary() walks back from the inactivity floor until chunks are settled and absorbed; prune validates prune_loc <= sync_boundary and returns PruneBeyondMinRequired instead of silently clipping; rewind gains a symmetric floor check; inactivity_floor_loc is test-only.
  • Test-module restructure: keyless/sync.rs and immutable/sync.rs converted to sync/{mod,tests}.rs directories, each gaining a SyncTestHarness trait with MMR+MMB instantiations (matches the pattern already in any/sync/ and current/sync/). Per-family harness duplication collapsed into single generic impls plus type aliases.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 18, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
commonware-mcp de3324e Apr 21 2026, 05:07 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 19, 2026

Deploying monorepo with  Cloudflare Pages  Cloudflare Pages

Latest commit: de3324e
Status: ✅  Deploy successful!
Preview URL: https://dfaf6c07.monorepo-eu0.pages.dev
Branch Preview URL: https://mmb-sync-approach-2.monorepo-eu0.pages.dev

View logs

@roberto-bayardo roberto-bayardo force-pushed the mmb-sync-approach-2 branch 10 times, most recently from 43137f8 to 90387dc Compare April 20, 2026 03:02
@roberto-bayardo roberto-bayardo moved this to Ready for Review in Tracker Apr 20, 2026
@roberto-bayardo roberto-bayardo requested a review from Copilot April 20, 2026 04:23
@roberto-bayardo roberto-bayardo added the breaking-format This PR modifies codec and/or storage formats. label Apr 20, 2026
@roberto-bayardo roberto-bayardo marked this pull request as ready for review April 20, 2026 04:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Generalizes QMDB state sync to work across the Merkle “family” abstraction (e.g., MMR and MMB), propagating the family type through sync targets, resolvers, engine types, and database/journal interfaces, and expanding test coverage to exercise multiple families.

Changes:

  • Make sync core types (Target, Resolver, Engine, errors, request tracking) generic over merkle::Family.
  • Update database/journal sync interfaces to use NonEmptyRange<Location<F>> and add Database::Family, plus a DbResolver helper trait.
  • Add/expand sync test harnesses to run suites against both MMR and MMB, and update examples/fuzz targets to the new APIs (including sync_boundary).

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
storage/src/qmdb/sync/target.rs Make Target generic over Merkle family and update codec/tests accordingly.
storage/src/qmdb/sync/resolver.rs Add Resolver::Family and thread family type through fetch/proof types and impl macros.
storage/src/qmdb/sync/requests.rs Parameterize request tracking and future types by Merkle family.
storage/src/qmdb/sync/mod.rs Introduce DbResolver and update sync() to return family-parameterized errors.
storage/src/qmdb/sync/journal.rs Make Journal generic over family and switch to NonEmptyRange<Location<F>>.
storage/src/qmdb/sync/gaps.rs Generalize gap detection over Location<F>.
storage/src/qmdb/sync/error.rs Parameterize sync errors and engine errors by Merkle family.
storage/src/qmdb/sync/engine.rs Thread family through engine state, events, requests, retained roots, and config channels.
storage/src/qmdb/sync/database.rs Add Database::Family and update sync rebuild/target checks to use family + NonEmptyRange.
storage/src/qmdb/keyless/sync/mod.rs Move keyless sync impl into module and generalize over family.
storage/src/qmdb/keyless/sync.rs Remove old MMR-only keyless sync implementation (relocated/generalized).
storage/src/qmdb/keyless/mod.rs Add sync_boundary() helper for keyless DBs.
storage/src/qmdb/keyless/fixed.rs Add an end-to-end sync smoke test for fixed-size keyless DB.
storage/src/qmdb/immutable/sync/tests.rs Add generic sync harness/tests for immutable DBs across MMR and MMB.
storage/src/qmdb/immutable/sync/mod.rs Generalize immutable sync rebuild logic over Merkle family.
storage/src/qmdb/immutable/sync.rs Remove old MMR-only immutable sync implementation (replaced by generalized module).
storage/src/qmdb/immutable/mod.rs Add sync_boundary() helper for immutable DBs.
storage/src/qmdb/current/sync/tests.rs Expand current sync tests to cover both MMR/MMB and add focused MMB pruning/auth regression tests.
storage/src/qmdb/current/sync/mod.rs Generalize current sync rebuild logic over Merkle family and NonEmptyRange.
storage/src/qmdb/current/ordered/fixed.rs Update pruning in tests to use sync_boundary().
storage/src/qmdb/current/mod.rs Replace inactivity-floor pruning usage with sync_boundary() and add boundary-focused tests.
storage/src/qmdb/current/grafting.rs Update docs to reference sync_boundary as the pruning guard.
storage/src/qmdb/current/db.rs Introduce sync_boundary() computation and enforce it as the prune upper bound.
storage/src/qmdb/any/unordered/variable.rs Rename sync test trait assoc type from Mmr to Merkle.
storage/src/qmdb/any/unordered/fixed.rs Rename sync test trait assoc type from Mmr to Merkle.
storage/src/qmdb/any/traits.rs Add DbAny::sync_boundary() and implement via inherent method delegation.
storage/src/qmdb/any/sync/mod.rs Generalize any-db sync helpers over Merkle family and switch to NonEmptyRange.
storage/src/qmdb/any/ordered/variable.rs Rename sync test trait assoc type from Mmr to Merkle.
storage/src/qmdb/any/ordered/fixed.rs Rename sync test trait assoc type from Mmr to Merkle.
storage/src/qmdb/any/db.rs Add sync_boundary() (equal to inactivity floor for any).
storage/src/merkle/mod.rs Require Family::peaks() iterator to be Send.
storage/src/merkle/mmr/journaled.rs Update sync config/tests to use NonEmptyRange.
storage/src/merkle/journaled.rs Change SyncConfig.range to NonEmptyRange and update init/write logic accordingly.
storage/fuzz/fuzz_targets/qmdb_any_variable_sync.rs Switch fuzzing logic from inactivity floor to sync_boundary().
storage/fuzz/fuzz_targets/qmdb_any_fixed_sync.rs Thread family through fuzz target sync types and use sync_boundary().
storage/fuzz/fuzz_targets/merkle_journaled.rs Update fuzz sync ranges to NonEmptyRange.
storage/fuzz/fuzz_targets/current_unordered_operations.rs Use sync_boundary() for pruning/proof bounds in fuzzing.
storage/fuzz/fuzz_targets/current_ordered_operations.rs Use sync_boundary() for pruning/proof bounds in fuzzing.
storage/fuzz/fuzz_targets/current_mmb_prune_grow.rs Use sync_boundary() for pruning helper in fuzzing.
storage/fuzz/fuzz_targets/current_crash_recovery.rs Use sync_boundary() for pruning/recovery verification loops.
storage/conformance.toml Update codec conformance entry for family-parameterized Target.
examples/sync/src/net/wire.rs Update wire types to use Target<mmr::Family, D>.
examples/sync/src/net/resolver.rs Implement Resolver::Family and update return types for family-aware fetch results.
examples/sync/src/databases/mod.rs Rename API from inactivity_floor to sync_boundary in the example DB abstraction.
examples/sync/src/databases/keyless.rs Use sync_boundary() in example keyless DB adapter.
examples/sync/src/databases/immutable.rs Use sync_boundary() in example immutable DB adapter.
examples/sync/src/databases/current.rs Use sync_boundary() in example current DB adapter.
examples/sync/src/databases/any.rs Use sync_boundary() in example any DB adapter.
examples/sync/src/bin/server.rs Serve targets using sync_boundary rather than inactivity floor.
examples/sync/src/bin/client.rs Update target update channel/types to include the family parameter.

Comment thread storage/src/qmdb/sync/mod.rs
Comment thread storage/src/qmdb/any/sync/tests.rs
@roberto-bayardo roberto-bayardo force-pushed the mmb-sync-approach-2 branch 2 times, most recently from bc95804 to 8233b0b Compare April 20, 2026 17:22
Copy link
Copy Markdown
Collaborator

@danlaine danlaine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLM review also pointed out a bunch of mmr -> merkle naming nits:

                          
  Stale "MMR" docs in generalized code                                     
   
  All of these now describe logic that applies to both MMR and MMB — update
   to "merkle tree" / "merkle family" / "ops tree":
                                                                           
  - storage/src/qmdb/current/sync/mod.rs:6-24 — module-level doc: "grafted 
  MMR root", "standard MMR range proofs", "grafted MMR are reconstructed",
  "read directly from the ops MMR", "making the grafted MMR structurally   
  identical to the ops MMR…" 
  - storage/src/qmdb/current/sync/mod.rs:91-92 — build_db doc: "ops MMR",
  "grafted MMR from the bitmap and ops MMR"                                
  - storage/src/qmdb/current/sync/mod.rs:165-195 — inline comments:
  "Extract grafted pinned nodes from the ops MMR", "The grafted MMR's      
  pinned nodes", "Build grafted MMR"
  - storage/src/qmdb/current/sync/mod.rs:314 — "batches against the ops    
  MMR"                                                                     
  - storage/src/qmdb/any/sync/mod.rs:52 — "operations-MMR layout"
  - storage/src/qmdb/sync/engine.rs:169 — "Pinned MMR nodes extracted from 
  proofs"                                                                  
  - storage/src/qmdb/sync/engine.rs:178 — "the MMR is append-only and      
  validate_update rejects unchanged roots"                                 
  - storage/src/qmdb/sync/resolver.rs:41,73 — "Pinned MMR nodes at the
  start location"     

also:

Stale README:
examples/sync/README.md:120. Still says "sync target (inactivity floor,
size, and root digest)". Server was changed to send sync_boundary()
instead; these differ for MMB.

/// known to be inactive. Operations before this point can be safely pruned.
pub const fn inactivity_floor_loc(&self) -> Location<F> {
#[cfg(any(test, feature = "test-traits"))]
pub(crate) const fn inactivity_floor_loc(&self) -> Location<F> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove this and just use sync_boundary which is already pub?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've already replaced most all uses of this, but there are some tests where it's still useful (e.g. floor raising)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh there were more inappropriate uses of prune(inactivity_floor_loc()) that the bot missed, but hopefully fixed now.

Comment on lines 80 to +76
// Size + root match implies the last CommitFloor op (and therefore the
// committed inactivity floor) matches, per the caller contract above.
// size + root identify a unique state, so if they match the target's we can reuse
// the persisted DB without fetching boundary pins.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: (and is never ) closed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread storage/src/qmdb/any/sync/tests.rs Outdated
pub(crate) type JournalOf<H> = <DbOf<H> as qmdb::sync::Database>::Journal;

/// Type alias for the merkle family used by a harness.
pub(crate) type FamilyOf<H> = <DbOf<H> as qmdb::sync::Database>::Family;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove this and just do H::Family

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@roberto-bayardo roberto-bayardo force-pushed the mmb-sync-approach-2 branch 2 times, most recently from a35f784 to 8d8b6e6 Compare April 20, 2026 19:59
@roberto-bayardo
Copy link
Copy Markdown
Collaborator Author

LLM review also pointed out a bunch of mmr -> merkle naming nits:
...

Should be fixed

Comment thread storage/src/qmdb/any/sync/tests.rs
Comment thread storage/src/qmdb/any/sync/tests.rs
@roberto-bayardo roberto-bayardo force-pushed the mmb-sync-approach-2 branch 2 times, most recently from 3684666 to 93a7705 Compare April 20, 2026 21:13
Comment thread storage/src/qmdb/keyless/sync/tests.rs Outdated
pub(crate) type DbOf<H> = <H as SyncTestHarness>::Db;
pub(crate) type OpOf<H> = <DbOf<H> as qmdb::sync::Database>::Op;
pub(crate) type ConfigOf<H> = <DbOf<H> as qmdb::sync::Database>::Config;
pub(crate) type FamilyOf<H> = <DbOf<H> as qmdb::sync::Database>::Family;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove FamilyOf like we did for qmdb::any

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

pub(crate) type DbOf<H> = <H as SyncTestHarness>::Db;
pub(crate) type OpOf<H> = <DbOf<H> as qmdb::sync::Database>::Op;
pub(crate) type ConfigOf<H> = <DbOf<H> as qmdb::sync::Database>::Config;
pub(crate) type FamilyOf<H> = <DbOf<H> as qmdb::sync::Database>::Family;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove FamilyOf like we did for qmdb::any

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

let client_suffix = context.next_u64().to_string();
let client_config = variable_config::<crate::translator::TwoCap>(&client_suffix, &context);
let target_db = std::sync::Arc::new(target_db);
// Supply the trusted canonical root so `build_db`'s authentication check actually
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually the ops root, not the canonical root, I think

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup good catch, fixed

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is still here -- forgot to push maybe?

Comment thread storage/src/qmdb/current/sync/mod.rs Outdated
let hasher = StandardHasher::<H>::new();
let mmr = mmr::journaled::Mmr::init_sync(
let merkle = Journaled::<F, _, _>::init_sync(
context.with_label("mmr"),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
context.with_label("mmr"),
context.with_label("merkle"),

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit de3324e. Configure here.

Comment thread storage/src/qmdb/any/sync/tests.rs
@roberto-bayardo roberto-bayardo added this pull request to the merge queue Apr 21, 2026
Merged via the queue into main with commit 245a099 Apr 21, 2026
178 of 179 checks passed
@roberto-bayardo roberto-bayardo deleted the mmb-sync-approach-2 branch April 21, 2026 19:36
@github-project-automation github-project-automation Bot moved this from Ready for Review to Done in Tracker Apr 21, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

❌ Patch coverage is 92.11618% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.85%. Comparing base (55af4c1) to head (de3324e).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
storage/src/qmdb/sync/resolver.rs 50.00% 15 Missing ⚠️
storage/src/qmdb/current/sync/mod.rs 80.39% 10 Missing ⚠️
storage/src/qmdb/current/mod.rs 94.38% 3 Missing and 2 partials ⚠️
storage/src/qmdb/immutable/mod.rs 0.00% 3 Missing ⚠️
storage/src/qmdb/keyless/mod.rs 0.00% 3 Missing ⚠️
storage/src/qmdb/sync/journal.rs 92.85% 0 Missing and 1 partial ⚠️
storage/src/qmdb/sync/target.rs 95.23% 0 Missing and 1 partial ⚠️
@@            Coverage Diff             @@
##             main    #3626      +/-   ##
==========================================
- Coverage   95.88%   95.85%   -0.04%     
==========================================
  Files         441      441              
  Lines      172372   171196    -1176     
  Branches     4006     4001       -5     
==========================================
- Hits       165287   164104    -1183     
- Misses       5819     5830      +11     
+ Partials     1266     1262       -4     
Files with missing lines Coverage Δ
storage/src/merkle/journaled.rs 95.05% <100.00%> (-0.01%) ⬇️
storage/src/merkle/mmr/journaled.rs 100.00% <100.00%> (ø)
storage/src/merkle/mod.rs 100.00% <ø> (ø)
storage/src/qmdb/any/db.rs 95.65% <100.00%> (+0.05%) ⬆️
storage/src/qmdb/any/mod.rs 99.17% <100.00%> (ø)
storage/src/qmdb/any/ordered/fixed.rs 98.95% <100.00%> (ø)
storage/src/qmdb/any/ordered/mod.rs 97.81% <100.00%> (-0.02%) ⬇️
storage/src/qmdb/any/ordered/variable.rs 97.44% <100.00%> (ø)
storage/src/qmdb/any/sync/mod.rs 98.98% <100.00%> (+0.02%) ⬆️
storage/src/qmdb/any/traits.rs 100.00% <100.00%> (ø)
... and 22 more

... and 8 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 55af4c1...de3324e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-format This PR modifies codec and/or storage formats.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants