Skip to content

[MOD-13911] Add GC logic for NumericRangeTree#8293

Merged
LukeMathWalker merged 4 commits intomasterfrom
numeric-range-tree-1-c-gc-impl
Feb 18, 2026
Merged

[MOD-13911] Add GC logic for NumericRangeTree#8293
LukeMathWalker merged 4 commits intomasterfrom
numeric-range-tree-1-c-gc-impl

Conversation

@LukeMathWalker
Copy link
Collaborator

@LukeMathWalker LukeMathWalker commented Feb 6, 2026

Describe the changes in the pull request

Prepare NumericRangeTree for GC integration.
This PR adds logic to:

  • Scan a range to determine the GC delta (to be invoked in the child process of fork GC)
  • Apply the GC delta to a given node (to be invoked in the parent process of fork GC)

The GC relies on the index of a node inside the slab as the node identifier. This differs from the existing C implementation, which relies on the memory address of the node as its identity (i.e. it passes a pointer over from the child to the parent GC process).

Stacked on top of #8292.

Which additional issues this PR fixes

  1. MOD-...
  2. #...

Main objects this PR modified

  1. ...

Mark if applicable

  • This PR introduces API changes
  • This PR introduces serialization changes

Release Notes

  • This PR requires release notes
  • This PR does not require release notes

If a release note is required (bug fix / new feature / enhancement), describe the user impact of this PR in the title.


Note

Medium Risk
Touches core GC and tree-mutation paths (stats accounting, node removal, slab compaction, pointer remapping), which could impact correctness or memory usage if edge cases are missed, though it is heavily covered by new tests.

Overview
Adds garbage-collection support for NumericRangeTree. The tree can now accept per-node GC deltas (NodeGcDelta) and apply them via apply_gc_to_node, updating entry/memory stats, tracking newly-empty leaves, and repairing HLL cardinality (including post-fork writes and ignored-last-block scenarios).

Introduces maintenance operations to reclaim memory: trim_empty_leaves prunes empty subtrees (freeing internal retained ranges when applicable) and compact_if_sparse compacts the node slab when many leaves are empty, remapping node indices safely.

Includes related robustness fixes: GcApplyInfo is now Copy/Clone/Default, InvertedIndex::apply_gc correctly sets ignored_last_block even when no last-block delta exists, numeric index exposes scan_gc/apply_gc helpers, and extensive integration/property tests were added to cover GC, trimming, compaction, and last-block edge cases.

Written by Cursor Bugbot for commit 92713bf. This will update automatically on new commits. Configure here.

@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from e31551b to 3afb760 Compare February 6, 2026 09:22
@codecov
Copy link

codecov bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 92.33449% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.64%. Comparing base (606bdab) to head (92713bf).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
...rc/redisearch_rs/numeric_range_tree/src/tree/gc.rs 92.26% 8 Missing and 6 partials ⚠️
...edisearch_rs/numeric_range_tree/src/tree/insert.rs 69.56% 7 Missing ⚠️
src/redisearch_rs/numeric_range_tree/src/range.rs 96.77% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8293      +/-   ##
==========================================
+ Coverage   82.53%   82.64%   +0.11%     
==========================================
  Files         417      418       +1     
  Lines       59759    60029     +270     
  Branches    17815    18085     +270     
==========================================
+ Hits        49320    49611     +291     
+ Misses      10256    10232      -24     
- Partials      183      186       +3     
Flag Coverage Δ
flow 84.22% <ø> (-0.03%) ⬇️
unit 51.49% <92.33%> (+0.28%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch from 1c84fcd to 9db43f7 Compare February 6, 2026 11:26
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch 2 times, most recently from 716ca3d to 2762c4d Compare February 6, 2026 11:40
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch 2 times, most recently from 1b9cd2f to 0aef10b Compare February 6, 2026 11:42
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from 2762c4d to 130f854 Compare February 6, 2026 11:42
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch from 0aef10b to ad6b3fe Compare February 6, 2026 15:33
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from 130f854 to 0be66c9 Compare February 6, 2026 15:33
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch from ad6b3fe to 8bef85f Compare February 6, 2026 16:07
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from 0be66c9 to ce4e0bc Compare February 6, 2026 16:07
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch from 8bef85f to a099cbf Compare February 6, 2026 16:31
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from ce4e0bc to 1b5c31b Compare February 6, 2026 16:31
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch from a099cbf to 67a358b Compare February 7, 2026 13:23
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch 2 times, most recently from 90c6ed2 to e84db9a Compare February 7, 2026 13:26
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch from 67a358b to 937e999 Compare February 7, 2026 13:26
@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 7, 2026

@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch 2 times, most recently from 84f9171 to 2bfad4a Compare February 8, 2026 09:26
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch 2 times, most recently from 010f7d0 to e7bee69 Compare February 8, 2026 10:02
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from 2bfad4a to 21e0009 Compare February 8, 2026 10:02
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-b-debug-repr branch from e7bee69 to f685066 Compare February 8, 2026 11:11
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from 21e0009 to b6fbd83 Compare February 8, 2026 11:11
@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from 76cfb77 to b4bfcfb Compare February 12, 2026 13:05
Copy link
Collaborator

@meiravgri meiravgri left a comment

Choose a reason for hiding this comment

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

Nice ! :)

/// Apply garbage collection deltas to this index.
///
/// Consumes the `delta` and returns information about what changed.
pub fn apply_gc(&mut self, delta: inverted_index::GcScanDelta) -> inverted_index::GcApplyInfo {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a specific reason not to put apply_gc and scan_gc in gc.rs?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I kept them in index since they just forward to the underlying methods with the same name. I've tried to avoid littering the rest of the crate with match statements over the compressed/uncompressed variants.

Copy link
Collaborator

Choose a reason for hiding this comment

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

In practice we could have moved this into tree/gc.rs right?

impl NumericIndex {
    // apply_gc
   // scan_gc
}
``

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, that would be possible since both enum variants are public.

NumericRangeNode::Leaf(leaf) => {
rv.num_leaves_delta -= 1;
rv.size_delta -= leaf.range.memory_usage() as i64;
rv.num_records_delta -= leaf.range.num_entries() as i32;
Copy link
Collaborator

Choose a reason for hiding this comment

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

is rv.num_records_delta used by the caller?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It isn't used on the GC path.
We could remove it, by creating a distinct stat-tracking output type for trim_empty_leaves rather than reusing AddResult.

Copy link
Collaborator

Choose a reason for hiding this comment

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

possible

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done in 54d2f47

@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from b4bfcfb to 747de96 Compare February 16, 2026 10:10
#[cfg(all(feature = "unittest", not(miri)))]
mod invariants;

pub use gc::{NodeGcDelta, SingleNodeGcResult};
Copy link

Choose a reason for hiding this comment

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

CompactIfSparseResult not re-exported from crate

Low Severity

CompactIfSparseResult is the return type of the public method compact_if_sparse() on NumericRangeTree, but it's not re-exported from tree/mod.rs or lib.rs. Meanwhile, NodeGcDelta and SingleNodeGcResult (return types of other public GC methods) are properly re-exported. External callers can use the returned value via type inference but cannot name the type, which is inconsistent with the other GC types.

Additional Locations (1)

Fix in Cursor Fix in Web

@jit-ci
Copy link

jit-ci bot commented Feb 16, 2026

❌ Jit Scanner failed - Our team is investigating

Jit Scanner failed - Our team has been notified and is working to resolve the issue. Please contact support if you have any questions.


💡 Need to bypass this check? Comment @sera bypass to override.

@LukeMathWalker
Copy link
Collaborator Author

I've switched all stats operations to use checked_* operations @meiravgri, which will panic on underflow/overflow.

@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch 2 times, most recently from 95e3628 to 772285d Compare February 16, 2026 13:27
meiravgri
meiravgri previously approved these changes Feb 16, 2026
/// Apply garbage collection deltas to this index.
///
/// Consumes the `delta` and returns information about what changed.
pub fn apply_gc(&mut self, delta: inverted_index::GcScanDelta) -> inverted_index::GcApplyInfo {
Copy link
Collaborator

Choose a reason for hiding this comment

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

In practice we could have moved this into tree/gc.rs right?

impl NumericIndex {
    // apply_gc
   // scan_gc
}
``

NumericRangeNode::Leaf(leaf) => {
rv.num_leaves_delta -= 1;
rv.size_delta -= leaf.range.memory_usage() as i64;
rv.num_records_delta -= leaf.range.num_entries() as i32;
Copy link
Collaborator

Choose a reason for hiding this comment

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

possible

Copy link

@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.

/// The change in the tree's node memory usage, in bytes.
/// Positive values indicate growth, negative values indicate shrinkage.
pub node_size_delta: i64,
}
Copy link

Choose a reason for hiding this comment

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

CompactIfSparseResult not re-exported from crate

Low Severity

CompactIfSparseResult is the return type of pub fn compact_if_sparse on NumericRangeTree, but it's not re-exported from tree/mod.rs or lib.rs. The gc module is private, so external callers can invoke the method and access fields via type inference, but cannot name the type explicitly. The other GC result types (NodeGcDelta, SingleNodeGcResult, TrimEmptyLeavesResult) are all properly re-exported — this one appears to be an oversight.

Additional Locations (2)

Fix in Cursor Fix in Web

@LukeMathWalker LukeMathWalker force-pushed the numeric-range-tree-1-c-gc-impl branch from 54d2f47 to 6b3d750 Compare February 17, 2026 09:50
meiravgri
meiravgri previously approved these changes Feb 17, 2026
@LukeMathWalker LukeMathWalker added this pull request to the merge queue Feb 17, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 17, 2026
@sonarqubecloud
Copy link

@LukeMathWalker LukeMathWalker added this pull request to the merge queue Feb 18, 2026
Merged via the queue into master with commit fdf7d79 Feb 18, 2026
50 checks passed
@LukeMathWalker LukeMathWalker deleted the numeric-range-tree-1-c-gc-impl branch February 18, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants