Skip to content

fix: model V4 gated stored-element reads in batch estimated costs - #789

Merged
QuantumExplorer merged 1 commit into
developfrom
fix/v4-estimated-cost-parity
Aug 3, 2026
Merged

fix: model V4 gated stored-element reads in batch estimated costs#789
QuantumExplorer merged 1 commit into
developfrom
fix/v4-estimated-cost-parity

Conversation

@QuantumExplorer

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

GROVE_V4 gates apply_batch.delete_tree_cleanup_type_source and apply_batch.overwrite_indexed_cleanup_inspection, each charging one extra stored-element read per DeleteTree / overwrite-capable op in the applied batch path. The batch estimated-cost paths (grovedb/src/batch/estimated_costs/{average,worst}_case_costs.rs) did not model this read, so estimated costs undershot applied costs by exactly that read on affected ops at grove version >= 4. Surfaced by Dash Platform's identity-op fee tests at protocol v14 (grove v4): applied processing fees came out exactly one read above the v3-era values while estimated-mode paths still produced v3-era numbers.

What was done?

Both estimators now charge the gated read, keyed off the same two version slots so V1..V3 estimates are byte-for-byte unchanged:

  • Overwrite gate (InsertOrReplace / Replace / Patch): one average-/worst-case Merk node read of the layer's estimated element. Skipped when tree-override validation is on, for bare-reference elements, and for layers known to be empty — the same conditions under which the applied path skips the read or gets it for free (a get on an empty Merk costs nothing), which keeps the existing fresh-insert estimated==applied tests exact.
  • Delete-tree gate: the parent storage-context prefix hash plus one seek loading the layered element cost — the exact storage_loaded_bytes shape Element::get_from_storage reports for tree elements. This read is deterministic (raw storage get, no Merk caching), so the estimated V4−V3 delta equals the applied V4−V3 delta field for field.

New tests (per gate, average and worst case):

  • test_average_case_delete_tree_type_source_read_estimated_vs_applied_parity — runs the same batch applied and estimated on V3 and V4 and asserts the two deltas match exactly (seek, loaded bytes, storage cost, context-prefix hash calls).
  • test_average_case_overwrite_inspection_read_gated_v4_vs_v3 / test_worst_case_overwrite_inspection_read_gated_v4_vs_v3 — pin the estimated delta to exactly one modeled node read, and pin the suppression conditions (validation on, bare reference) to a zero delta.
  • test_worst_case_delete_tree_type_source_read_gated_v4_vs_v3 — pins the worst-case delta to context hash + one worst-case layered load.

Existing exact-cost expectations that run at latest() (= V4) were updated by the modeled read, with comments documenting the delta; the estimated == applied seek assertion in test_batch_root_one_tree_insert_op_under_element_average_case_costs became applied + 1 == estimated because the applied path gets that particular read for free from the cached root node.

Also made grovedb/src/estimated_costs/worst_case_costs pub(crate) so the batch estimator can reuse WORST_CASE_FLAGS_LEN.

How Has This Been Tested?

  • cargo test -p grovedb — full crate suite green (2544 tests), including the 6 new/updated estimator tests.
  • cargo check -p grovedb --no-default-features --features minimal and --features verify — feature-gated builds unaffected.
  • cargo clippy -p grovedb --all-targets — no new lints.

Breaking Changes

None on released versions: both charges are gated on the V4 feature slots, so V1..V3 estimated outputs are unchanged. V4 estimated outputs grow by exactly the read the V4 applied path already charges.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests

🤖 Generated with Claude Code

The GROVE_V4 gates delete_tree_cleanup_type_source and
overwrite_indexed_cleanup_inspection each charge one extra stored-element
read per DeleteTree / overwrite-capable op in the applied batch path, but
the average- and worst-case batch estimators did not model that read, so
estimated costs undershot applied costs by exactly one read per affected
op at grove version >= 4 (surfaced by Dash Platform identity-op fee tests
at protocol v14).

Charge the read in both estimators, keyed off the same two version slots
so V1..V3 estimates are unchanged:

- overwrite gate: one average/worst-case Merk node read of the layer's
  estimated element, skipped when tree-override validation is on, for
  bare-reference elements, and for layers known to be empty — the same
  conditions under which the applied path skips or gets the read for free.
- delete-tree gate: the parent context prefix hash plus one seek loading
  the layered element cost, matching Element::get_from_storage exactly;
  the average-case estimated delta therefore equals the applied delta
  field for field, which the new parity test pins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@QuantumExplorer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b027385-976e-4ec0-b1d9-5a6a69329d2a

📥 Commits

Reviewing files that changed from the base of the PR and between d473818 and 6bb43f7.

📒 Files selected for processing (3)
  • grovedb/src/batch/estimated_costs/average_case_costs.rs
  • grovedb/src/batch/estimated_costs/worst_case_costs.rs
  • grovedb/src/estimated_costs/mod.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.05495% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.13%. Comparing base (d473818) to head (6bb43f7).

Files with missing lines Patch % Lines
...db/src/batch/estimated_costs/average_case_costs.rs 94.78% 12 Missing ⚠️
...vedb/src/batch/estimated_costs/worst_case_costs.rs 95.52% 6 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #789      +/-   ##
===========================================
+ Coverage    92.11%   92.13%   +0.01%     
===========================================
  Files          257      257              
  Lines        77936    78297     +361     
===========================================
+ Hits         71794    72137     +343     
- Misses        6142     6160      +18     
Components Coverage Δ
grovedb-core 90.41% <95.05%> (+0.04%) ⬆️
merk 92.89% <ø> (ø)
storage 87.00% <ø> (ø)
commitment-tree 96.05% <ø> (ø)
mmr 96.79% <ø> (ø)
bulk-append-tree 89.82% <ø> (ø)
element 97.95% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@QuantumExplorer QuantumExplorer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This will work

@QuantumExplorer
QuantumExplorer merged commit d56d314 into develop Aug 3, 2026
11 checks passed
@QuantumExplorer
QuantumExplorer deleted the fix/v4-estimated-cost-parity branch August 3, 2026 01:14
QuantumExplorer added a commit that referenced this pull request Aug 3, 2026
…elements (#790)

* fix: make the V4 batch gates zero-cost by reusing already-loaded old elements

The GROVE_V4 gates overwrite_indexed_cleanup_inspection and
delete_tree_cleanup_type_source each bought their correctness with a
dedicated stored-element read per op: every overwrite-capable op and every
DeleteTree in a batch paid +1 seek and the loaded element bytes at V4, even
when no indexed tree was anywhere near the batch (surfaced by Dash Platform
identity-op fee tests at protocol v14 as +2000 seeks + 20 bytes per op unit).

Derive the old element from data the apply already loads instead:

- merk: apply_unchecked_with_old_value_observer threads an infallible
  (key, old_value, disposition) observer through the batch walk. The walker
  fetches an existing node before rewriting or deleting it, so surfacing its
  stored value is free — no storage read, no tracked cost.
- overwrite gate: execute_ops_on_path registers overwrite-capable keys and
  classifies the displaced element in the observer. Bare Reference
  overwrites are now included (there is no longer a cost argument for
  leaving a reference over an indexed tree unswept), which closes #776 and
  un-ignores its regression test. Non-empty indexed replacements are still
  refused — normally by the earlier ungated empty-at-batch-insertion guard,
  with the classifier's NotSupported kept as defense in depth.
- delete-tree gate: cleanup-namespace classification moves after apply_body,
  driven by the ACTUAL stored types captured by the observer (declared vs
  stored mismatches involving an indexed tree are rejected there). The
  pre-apply Error/Skip emptiness checks read the stored element once up
  front — the same single read V1..V3 pay directly or inside the child-merk
  open — and hand it to a new open-with-parent-element helper so nothing is
  read twice. The V1..V3 pre-scan path is byte-for-byte the released
  behaviour, now shared by both apply_batch entry points via
  scan_delete_tree_ops.
- estimated costs: revert the batch estimator modeling of the gated reads
  (#789) in both the average- and worst-case models — with the applied reads
  gone the estimators were overshooting by exactly one read per gated op.

A new cost-parity test pins a batch of plain overwrites plus a plain
DeleteTree to an identical CostResult under GROVE_V3 and GROVE_V4, and the
V4 refresh-reference cost test now asserts the same constants as its
_v3_keeps_live_costs companion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: cover the pre-scan branches and observer paths the gate rework added

Raises patch coverage over the 90% bar (codecov/patch was at 87.76%):

- grovedb: four DeleteTree pre-scan tests — missing-key emptiness read,
  same-batch child-delete carve-out (with a plain Delete elsewhere and a
  Skip-policy sibling), the V1..V3 declared-type-at-face-value shapes on
  GROVE_V3, and a two-mismatch batch exercising the observer's
  short-circuit after the first gate rejection.
- merk: consolidate the six duplicated inert-callback bundles in the ops
  test module into one apply_to_plain helper (whose no-op observer now
  executes via the update tests), and add a build-with-mid-delete test
  covering build's delete arm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs+test: address review — capture-struct return doc, tighter overwrite asserts

- Document apply_batch_structure's BatchApplyCaptures return (both fields)
  instead of the stale two-tuple description.
- Pin the non-empty indexed batch-insertion refusal to the exact
  InvalidBatchOperation message instead of a three-variant alternation.
- Require the indexed-to-Item safe-subset overwrite to succeed instead of
  tolerating either outcome.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant