Maintenance
The maintainer is the normal way to run ZoneTree maintenance in a service. Keep one maintainer alive for each active tree so merge work and inactive cache cleanup happen in the background.
using var zoneTree = new ZoneTreeFactory<int, string>()
.SetDataDirectory("data/app")
.OpenOrCreate();
using var maintainer = zoneTree.CreateMaintainer(); CreateMaintainer() attaches to the tree's maintenance events. After writes move the mutable segment forward, the maintainer decides whether to start a merge. It also starts the periodic cleanup job for disk-read caches.
For most applications, this is the whole maintenance setup.
Merge Triggers
The default maintainer starts a normal merge after the mutable segment moves forward and either trigger is reached:
| Setting | Default | Meaning |
|---|---|---|
ThresholdForMergeOperationStart | 0 records | merge when read-only records exceed this count |
MaximumReadOnlySegmentCount | 64 | merge when read-only segment count exceeds this count |
With the default threshold of 0, any non-empty read-only layer can start a merge after segment movement.
maintainer.ThresholdForMergeOperationStart = 500_000;
maintainer.MaximumReadOnlySegmentCount = 32; Use a higher record threshold when you want larger merge batches. Use a lower read-only segment count limit when memory pressure should trigger merge work sooner.
Evict Current Data
EvictToDisk() moves the current mutable segment forward and starts a normal merge.
maintainer.EvictToDisk();
maintainer.WaitForBackgroundThreads(); Use it before controlled shutdowns, exports, or explicit maintenance points when you want the current in-memory records to enter the merge pipeline immediately. Call WaitForBackgroundThreads() when the caller needs the merge to finish before continuing.
Waiting And Cancellation
The maintainer tracks the merge threads it starts.
maintainer.WaitForBackgroundThreads(); For async callers:
await maintainer.WaitForBackgroundThreadsAsync(); To request a faster shutdown:
maintainer.TryCancelBackgroundThreads();
maintainer.WaitForBackgroundThreads(); TryCancelBackgroundThreads() asks active normal and bottom-segment merges to cancel. The merge threads finish when they observe the cancellation request.
Cache Cleanup
The default maintainer starts inactive cache cleanup automatically.
| Setting | Default |
|---|---|
EnableJobForCleaningInactiveCaches | true |
BlockCacheLifeTime | 1 minute |
InactiveBlockCacheCleanupInterval | 30 seconds |
maintainer.BlockCacheLifeTime = TimeSpan.FromMinutes(2);
maintainer.InactiveBlockCacheCleanupInterval = TimeSpan.FromSeconds(30); Longer cache lifetime can help repeated disk reads. Shorter cache lifetime reduces retained read-cache memory. The cleanup job releases inactive decompressed blocks and expired circular key/value cache records.
For read-cache details, see read-path caching.
Bottom Segment Merge
Bottom segment merge is an explicit operation. Run it when your service wants to compact a range of bottom segments.
maintainer.StartBottomSegmentsMerge();
maintainer.WaitForBackgroundThreads(); To merge a selected range:
maintainer.StartBottomSegmentsMerge(fromIndex: 0, toIndex: 4);
maintainer.WaitForBackgroundThreads(); The range uses the current bottom segment order. A broad range such as 0..int.MaxValue asks ZoneTree to merge as much of the bottom layer as possible.
Direct Maintenance API
zoneTree.Maintenance exposes the lower-level operations used by the maintainer. Use it when your application owns its own scheduler.
zoneTree.Maintenance.MoveMutableSegmentForward();
var thread = zoneTree.Maintenance.StartMergeOperation();
thread?.Join(); Useful direct operations:
| Operation | Purpose |
|---|---|
MoveMutableSegmentForward() | move the current mutable segment into the read-only layer |
StartMergeOperation() | start a normal merge thread |
StartBottomSegmentsMergeOperation(fromIndex, toIndex) | start a bottom-segment merge thread |
TryCancelMergeOperation() | request cancellation for the active normal merge |
TryCancelBottomSegmentsMergeOperation() | request cancellation for the active bottom-segment merge |
SaveMetaData() | refresh the JSON metadata file and clear pending metadata records |
ReleaseReadBuffers(ticks) | release inactive decompressed disk blocks |
ReleaseCircularKeyCacheRecords() | release expired key-cache records |
ReleaseCircularValueCacheRecords() | release expired value-cache records |
StartMergeOperation() and StartBottomSegmentsMergeOperation(...) return the created thread, or null when a merge of the same kind is already active.
Merge Results
Merge completion is reported through maintenance events.
zoneTree.Maintenance.OnMergeOperationEnded += (_, result) =>
{
Console.WriteLine(result);
}; | Result | Meaning |
|---|---|
SUCCESS | merge completed |
NOTHING_TO_MERGE | no eligible read-only segments were available |
ANOTHER_MERGE_IS_RUNNING | a merge of the same kind was already active |
RETRY_READONLY_SEGMENTS_ARE_NOT_READY | read-only segments were still preparing |
CANCELLED_BY_USER | cancellation was requested |
FAILURE | an exception occurred; inspect the logger |
The default maintainer retries RETRY_READONLY_SEGMENTS_ARE_NOT_READY. If it sees ANOTHER_MERGE_IS_RUNNING, it starts another merge after the active merge finishes.
Counters
Maintenance counters are useful for dashboards and health checks.
| Counter | Meaning |
|---|---|
MutableSegmentRecordCount | records in the current mutable segment |
ReadOnlySegmentsCount | read-only in-memory segment count |
ReadOnlySegmentsRecordCount | records across read-only in-memory segments |
InMemoryRecordCount | mutable plus read-only record count |
TotalRecordCount | physical records across memory and disk layers |
IsMerging | normal merge is active |
IsBottomSegmentsMerging | bottom-segment merge is active |
TotalRecordCount is a physical storage counter. Use Count() or CountFullScan() for live-record counts.
Events
Use events for monitoring, scheduling, and cleanup reporting.
| Event | Use |
|---|---|
OnMutableSegmentMovedForward | observe mutable segment movement |
OnMergeOperationStarted | observe normal merge start |
OnMergeOperationEnded | observe normal merge result |
OnBottomSegmentsMergeOperationStarted | observe bottom merge start |
OnBottomSegmentsMergeOperationEnded | observe bottom merge result |
OnDiskSegmentCreated | observe created disk segment files |
OnDiskSegmentActivated | observe the active disk segment change |
OnCanNotDropReadOnlySegment | report cleanup failure for read-only segment files |
OnCanNotDropDiskSegment | report cleanup failure for disk segment files |
OnCanNotDropDiskSegmentCreator | report cleanup failure for unfinished merge output |
Failed drop events mean obsolete files or temporary output stayed behind after a cleanup attempt failed. Log the exception and investigate the file-system or provider error.
Iterator Lifetime
Dispose iterators as soon as scans finish. Long-lived iterators can keep segments alive, which delays cleanup of old segment files and read buffers.
Snapshot iterators move the mutable segment forward when they are created. Heavy snapshot-iterator usage under write load can increase read-only segment pressure.
Practical Patterns
For a normal service:
using var zoneTree = new ZoneTreeFactory<int, string>()
.SetDataDirectory("data/app")
.OpenOrCreate();
using var maintainer = zoneTree.CreateMaintainer();
// run application
maintainer.WaitForBackgroundThreads(); For a controlled checkpoint:
maintainer.EvictToDisk();
maintainer.WaitForBackgroundThreads();
zoneTree.Maintenance.SaveMetaData(); For a custom maintenance window:
maintainer.StartMerge();
maintainer.StartBottomSegmentsMerge();
maintainer.WaitForBackgroundThreads(); For related tuning, see memory usage, disk segment tuning, and read-path caching.