Add Range Index data type based on Bf-Tree (single instance)#1613
Merged
Add Range Index data type based on Bf-Tree (single instance)#1613
Conversation
Contributor
kevin-montrose
left a comment
There was a problem hiding this comment.
Generally looks good to me - couple questions to poke at, but no blockers that I see.
320af88 to
90fbe55
Compare
8f93b4c to
c5afb94
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new RangeIndex data type to Garnet, backed by the native bf-tree library, including command surface (RI.*), storage/session plumbing, lifecycle (flush/evict/checkpoint/recovery) hooks, and supporting metadata/docs/tests/benchmarks. This integrates RangeIndex into the main store with type-safety enforcement and adds a Rust cdylib + C# interop layer for BfTree.
Changes:
- Introduces RangeIndex runtime support:
RI.CREATE/SET/GET/DEL/SCAN/RANGE, RangeIndexManager, and session ops that execute against a BfTree native pointer stored in a Tsavorite value stub. - Extends Tsavorite trigger surface for checkpoint/recovery callbacks and wires Garnet record triggers to snapshot/evict/restore BfTrees.
- Adds command metadata/docs updates plus new interop tests and benchmarks; updates CI/pipelines to install Rust and build/sign/package the native library.
Reviewed changes
Copilot reviewed 61 out of 66 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| test/Garnet.test/TestUtils.cs | Adds server option flag for enabling RangeIndex preview in tests. |
| test/Garnet.test/RespCommandTests.cs | Marks internal RI RMW commands as “no metadata required”. |
| test/Garnet.test/Resp/ACL/RespCommandTests.cs | Extends ACL coverage logic and adds RI.* ACL tests. |
| test/BfTreeInterop.test/BfTreeInteropTests.cs | New integration tests for C# ↔ native BfTree interop. |
| test/BfTreeInterop.test/BfTreeInterop.test.csproj | New test project for BfTree interop layer. |
| playground/CommandInfoUpdater/SupportedCommand.cs | Adds RI.* commands to command info updater list. |
| playground/CommandInfoUpdater/GarnetCommandsInfo.json | Adds command-info entries for RI.*. |
| playground/CommandInfoUpdater/GarnetCommandsDocs.json | Adds docs entries for RI.* and updates quoting/encoding. |
| libs/storage/Tsavorite/cs/src/core/Index/StoreFunctions/StoreFunctions.cs | Adds trigger passthrough for recovery/checkpoint callbacks. |
| libs/storage/Tsavorite/cs/src/core/Index/StoreFunctions/IStoreFunctions.cs | Extends store functions interface for new callbacks. |
| libs/storage/Tsavorite/cs/src/core/Index/StoreFunctions/IRecordTriggers.cs | Adds recovery and checkpoint trigger APIs to record triggers. |
| libs/storage/Tsavorite/cs/src/core/Index/StoreFunctions/CheckpointTrigger.cs | New enum for checkpoint trigger points. |
| libs/storage/Tsavorite/cs/src/core/Index/Recovery/Recovery.cs | Calls recovery triggers and distinguishes snapshot recovery path. |
| libs/storage/Tsavorite/cs/src/core/Index/Checkpointing/HybridLogCheckpointSMTask.cs | Invokes checkpoint triggers at version-shift/flush-begin. |
| libs/storage/Tsavorite/cs/src/core/Allocator/ObjectAllocatorImpl.cs | Clarifies flush trigger comment for external-resource snapshotting. |
| libs/server/StoreWrapper.cs | Plumbs shared RangeIndexManager through StoreWrapper lifecycle. |
| libs/server/Storage/Session/MainStore/RangeIndexOps.cs | Implements StorageSession RangeIndex operations and RESP writes. |
| libs/server/Storage/Functions/UnifiedStore/DeleteMethods.cs | Minor formatting change. |
| libs/server/Storage/Functions/MainStore/VarLenInputMethods.cs | Adds RI* sizing support for RMW value allocation. |
| libs/server/Storage/Functions/MainStore/ReadMethods.cs | Enforces type-safety for RI keys on read paths. |
| libs/server/Storage/Functions/MainStore/RMWMethods.cs | Adds RI* RMW handling for create/promote/restore stubs. |
| libs/server/Storage/Functions/MainStore/PrivateMethods.cs | Ensures RI.* commands copy output without RESP header. |
| libs/server/Storage/Functions/GarnetRecordTriggers.cs | Adds BfTree lifecycle handling via record triggers. |
| libs/server/Storage/Functions/FunctionsState.cs | Exposes RangeIndexManager via FunctionsState. |
| libs/server/Servers/GarnetServerOptions.cs | Adds EnableRangeIndexPreview server option. |
| libs/server/Resp/RespServerSession.cs | Routes RI.* commands to dedicated handlers. |
| libs/server/Resp/RangeIndex/RespServerSessionRangeIndex.cs | New RESP handlers for RI.* parsing/execution. |
| libs/server/Resp/RangeIndex/RangeIndexResult.cs | New result enum for RangeIndex operations. |
| libs/server/Resp/RangeIndex/RangeIndexManager.cs | New manager for live BfTrees + flush/checkpoint snapshot orchestration. |
| libs/server/Resp/RangeIndex/RangeIndexManager.Locking.cs | Adds RI shared/exclusive locking + lazy restore/promote paths. |
| libs/server/Resp/RangeIndex/RangeIndexManager.Index.cs | Defines stub layout and stub mutation helpers. |
| libs/server/Resp/Parser/RespCommand.cs | Adds RI commands, parsing fast-paths, and RangeIndex legality checks. |
| libs/server/Resp/CmdStrings.cs | Adds RI.* command string constants. |
| libs/server/GarnetDatabase.cs | Stores RangeIndexManager per DB; plumbs through DB construction. |
| libs/server/Garnet.server.csproj | Adds project reference to BfTreeInterop. |
| libs/server/API/IGarnetApi.cs | Adds RangeIndex API surface methods. |
| libs/server/API/GarnetApi.cs | Implements RangeIndex API calls via StorageSession. |
| libs/server/ACL/ACLParser.cs | Supports dot-commands (e.g., RI.CREATE) parsing for ACLs. |
| libs/resources/RespCommandsInfo.json | Adds RI.* command info entries to shipped resources. |
| libs/native/bftree-garnet/src/lib.rs | New Rust FFI wrapper over bf-tree with point ops, scans, snapshot/recovery. |
| libs/native/bftree-garnet/rust-toolchain.toml | Pins Rust toolchain channel to stable. |
| libs/native/bftree-garnet/examples/bench.rs | Adds standalone Rust benchmark example. |
| libs/native/bftree-garnet/NativeBfTreeMethods.cs | Adds P/Invoke declarations for native bftree_garnet. |
| libs/native/bftree-garnet/Cargo.toml | New Rust crate manifest for cdylib build. |
| libs/native/bftree-garnet/Cargo.lock | Locks Rust dependencies (bf-tree, transitive). |
| libs/native/bftree-garnet/BfTreeService.cs | Managed wrapper for BfTree native API and scan helpers. |
| libs/native/bftree-garnet/BfTreeInterop.csproj | Builds/copies native library and packages runtime assets. |
| libs/native/bftree-garnet/.gitignore | Ignores Rust build artifacts. |
| libs/host/defaults.conf | Adds default config for RangeIndex preview flag. |
| libs/host/GarnetServer.cs | Constructs RangeIndexManager and wires triggers into store creation. |
| libs/host/Configuration/Options.cs | Adds CLI option --enable-range-index-preview. |
| benchmark/BDN.benchmark/Operations/RangeIndexOperations.cs | Adds BDN benchmarks for RI.* operations. |
| benchmark/BDN.benchmark/BfTree/BfTreeOperations.cs | Adds BDN benchmarks for BfTree point + scan operations. |
| benchmark/BDN.benchmark/BDN.benchmark.csproj | References BfTreeInterop from benchmarks. |
| Garnet.slnx | Adds new project/folders to solution layout. |
| .github/workflows/ci.yml | Installs Rust toolchain for GitHub Actions CI. |
| .azure/pipelines/createbinaries.ps1 | Copies bftree native binary into publish output. |
| .azure/pipelines/azure-pipelines.yml | Installs Rust toolchain on Windows/Linux builds. |
| .azure/pipelines/azure-pipelines-internal-release.yml | Builds/downloads bftree native and signs additional artifacts. |
| .azure/pipelines/azure-pipelines-external-release.yml | Builds/downloads bftree native and includes in release signing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c207468 to
9ac1c64
Compare
9ac1c64 to
3e67835
Compare
89856a2 to
d602ae0
Compare
a0d9ef2 to
4f60a0b
Compare
Integrates 4 dev commits (#1716, #1712, #1711, #1710). Key change: #1712 internalized heap-size tracking into Tsavorite and refactored IRecordDisposer → IRecordTriggers with OnEvict(EvictionSource). Resolution: - IRecordTriggers: take dev's version (EvictionSource enum, OnEvict with EvictionSource param, OnDisposeDiskRecord, removed OnDisposeValueObject), add bf-tree checkpoint hooks (OnRecovery, OnCheckpoint, OnRecoverySnapshotRead) - GarnetRecordTriggers: update OnEvict to accept EvictionSource; remove OnDisposeValueObject and heap-tracking from OnDispose (now internal to Tsavorite); keep bf-tree dispatch (OnFlush, OnEvict, OnDiskRead, OnDispose, OnCheckpoint, OnRecovery) - GarnetServer: keep cacheSizeTracker + rangeIndexManager constructor - RMWMethods PostCopyUpdater: keep ClearTreeHandle for RIPROMOTE - Fix unnecessary using in ReadMethods.cs All tests pass: 55 RangeIndex (3 runs), 345 RespTests, 4 CacheSizeTracker. Format clean (Garnet + Tsavorite). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4f60a0b to
516bd88
Compare
MEMORY-mode BfTree indexes do not support scan operations in the native library (bf-tree 0.4.9 panics in range_scan.rs:508, crashing the process). Add StorageBackend check in RangeIndexScan/RangeIndexRange that returns MemoryModeNotSupported before calling the native scan FFI. Network handlers return a clear error: 'ERR RI.SCAN/RI.RANGE is not supported for MEMORY-mode indexes'. Server remains responsive after the rejected command. Also fix shutdown ordering: dispose RangeIndexManager before DatabaseManager so BfTree native resources are freed before the Tsavorite store closes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace generic 'ERR invalid key or value size' with descriptive message showing the configured MINRECORD, MAXRECORD, MAXKEYLEN constraints and the actual sizes that were rejected. Example: ERR key+value size must be between 16 and 1024 bytes (got 4), max key length 128 (got 2) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7056ba7 to
5388195
Compare
Pin rust-toolchain.toml to channel 1.94 (was 'stable') for reproducible builds across developers and CI, following DiskANN's pattern. Add --locked flag to all cargo build invocations so Cargo.lock is used exactly and the build fails if it's out of date, rather than silently updating dependencies. Files changed: - libs/native/bftree-garnet/rust-toolchain.toml - libs/native/bftree-garnet/BfTreeInterop.csproj - .azure/pipelines/azure-pipelines-external-release.yml - .azure/pipelines/azure-pipelines-internal-release.yml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
BfTreeService.Dispose: use Interlocked.Exchange on _disposed flag to prevent double-free in concurrent scenarios. RI.CREATE: change numeric option variables from unsigned to signed long, check <= 0 (was == 0) in validation block. Negative values previously wrapped to huge unsigned values, potentially causing massive allocations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
01749bc to
0ee0ba3
Compare
The BfTreeInterop.csproj cargo build target ran for each TFM in parallel, causing concurrent rustup toolchain installs to race on download file renames. Fix: gate the cargo build target on IsCrossTargetingBuild so it runs once in the outer MSBuild orchestration before per-TFM inner builds start. The 'or TargetFrameworks empty' fallback handles single-TFM builds (e.g. dotnet build -f net10.0). Also pin rust-toolchain.toml to exact version 1.94.0 (was 1.94) to match what setup-rust-toolchain@v1 pre-installs in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0ee0ba3 to
2707408
Compare
Cross-compiled via cargo-zigbuild for x86_64-apple-darwin and
aarch64-apple-darwin. Enables building Garnet on macOS without
Rust installed — .NET RID probing finds the prebuilt dylib at
runtimes/osx-{x64,arm64}/native/.
Also add osx-arm64 ContentWithTargetPath to BfTreeInterop.csproj.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Garnet.server.csproj now references BfTreeInterop.csproj, so the restore layer needs the csproj copied for dotnet restore to resolve the project graph. Verified: Docker build succeeds, RI commands (CREATE, SET, GET, SCAN, TYPE) all work correctly in the production container using prebuilt native library from runtimes/linux-x64/native/. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
9c8344e to
f3bd428
Compare
tiagonapoli
reviewed
Apr 21, 2026
tiagonapoli
reviewed
Apr 21, 2026
tiagonapoli
reviewed
Apr 21, 2026
tiagonapoli
reviewed
Apr 21, 2026
tiagonapoli
reviewed
Apr 21, 2026
Change all stubSpan.Length checks from < to != for exact size matching. A mismatched stub size (too short OR too long) signals corruption. In HandleRangeIndexCreateReplay, throw GarnetException instead of silently returning — corrupt AOF entries during recovery should fail loudly rather than silently losing data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Log error on BfTree creation failure during AOF replay instead of silently swallowing the exception (RangeIndexManager.cs:536). 2. Remove unused DisposeTreeIfOwned method — all callers use DisposeTreeUnderLock which acquires the exclusive lock (RangeIndexManager.Index.cs). 3. Change SetFlushedFlag and ClearTreeHandle parameter from ReadOnlySpan<byte> to Span<byte> — these methods mutate the span via Unsafe.As, so the type should reflect the mutation (RangeIndexManager.Index.cs). 4. Throw GarnetException instead of returning NOTFOUND when ReadRangeIndex gets an unexpected stub size — this indicates store corruption and should fail loudly (RangeIndexManager.Locking.cs). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Description of Change
Adds Range Index as a new Garnet data type, backed by Bf-Tree — a high-performance B-tree for ordered key-value storage with range scan support. Gated behind
--enable-range-index-preview.Commands implemented (9):
RI.CREATE— Create index with DISK or MEMORY backendRI.SET/RI.GET/RI.DEL— Point operations on entriesRI.SCAN/RI.RANGE— Range queries (DISK backend only)RI.EXISTS/RI.CONFIG/RI.METRICS— Utility commandsTYPEreturns"rangeindex"for RI keysDEL/UNLINKfrees the underlying BfTreeLifecycle infrastructure (Tsavorite IRecordTriggers):
OnFlush— Snapshot BfTree to flush.bftree, set FlagFlushed for lazy promoteOnEvict— Free BfTree under exclusive lockOnDiskRead— Zero stale TreeHandleOnCheckpoint— VersionShift (barrier), FlushBegin (snapshot), CheckpointCompleted (cleanup)OnRecovery/OnRecoverySnapshotRead— Set recovered token / FlagRecoveredCheckpoint consistency:
SnapshotPending=1at barrier time are snapshotted; v+1 trees are skippedAOF logging:
WRONGTYPE safety:
55 tests covering CRUD, scan, range, lifecycle (flush/evict/promote/restore), checkpoint recovery, AOF replay, AOF-only recovery, WRONGTYPE,
concurrent stress (4 threads + blocking SAVE with strict prefix verification), and utility commands.
Documentation:
website/docs/commands/range-index.md)Not in this PR (deferred)