ZoneTree 1.0.3

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package ZoneTree --version 1.0.3                
NuGet\Install-Package ZoneTree -Version 1.0.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ZoneTree" Version="1.0.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ZoneTree --version 1.0.3                
#r "nuget: ZoneTree, 1.0.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ZoneTree as a Cake Addin
#addin nuget:?package=ZoneTree&version=1.0.3

// Install ZoneTree as a Cake Tool
#tool nuget:?package=ZoneTree&version=1.0.3                

ZoneTree

ZoneTree is a persistent, high-performance key-value database for .NET. It can operate in memory or on disk. (Optimized for SSDs)

Download

ZoneTree is a fast and high-performance LSM Tree for .NET.

LSM Tree (Log-structured merge-tree) is the most popular data structure and it is being used by many popular databases internally.

Why ZoneTree?

  1. It is pure C#. Easy to maintain, easy to develop new features.
  2. It is faster than using C/C++ based key-value stores like RocksDB. Because ZoneTree does not need to transfer bytes to the native external libraries (Zero Marshaling).
  3. .NET EcoSystem does not have any feature-complete and thread-safe LSM Tree that operates both in memory and on disk.

How fast is it?

2 Million int key and int value inserts in 7 seconds. (Config: 1M mutable segment size, 2M readonly segments merge-threshold)

20 Million int key and int value inserts in 73 seconds. (Config: 1M mutable segment size, 2M readonly segments merge-threshold)

20 Million int key and int value reads in 16 seconds. (Config: 1M mutable segment size, 2M readonly segments merge-threshold)

Doing database benchmark is tough. A proper and fair performance analysis requires a lot of work.

For now, we are confident that ZoneTree is fast enough to be used in production.

How to use ZoneTree?

The following sample demonstrates creating a database.

  var dataPath = "data/mydatabase";
  var walPath = "data/mydatabase/wal";
  using var zoneTree = new ZoneTreeFactory<int, string>()
    .SetComparer(new IntegerComparerAscending())
    .SetDataDirectory(dataPath)
    .SetWriteAheadLogDirectory(walPath)
    .SetKeySerializer(new Int32Serializer())
    .SetValueSerializer(new Utf8StringSerializer())
    .OpenOrCreate();
    
    // atomic (thread-safe) on single mutable-segment.
    zoneTree.Upsert(39, "Hello Zone Tree!");
    
    // atomic across all segments
    zoneTree.TryAtomicAddOrUpdate(39, "a", (x) => x + "b");

How to maintain LSM Tree?

Big LSM Trees require maintenance tasks. ZoneTree provides the IZoneTreeMaintenance interface to give you full power on maintenance tasks. It also comes with a default maintainer to let you focus on your business logic without wasting time with LSM details. You can start using the default maintainer like in the following sample code. Note: For small data you don't need a maintainer.

  var dataPath = "data/mydatabase";
  var walPath = "data/mydatabase/wal";

  // 1. Create your ZoneTree
  using var zoneTree = new ZoneTreeFactory<int, string>()
    .SetComparer(new IntegerComparerAscending())
    .SetDataDirectory(dataPath)
    .SetWriteAheadLogDirectory(walPath)
    .SetKeySerializer(new Int32Serializer())
    .SetValueSerializer(new Utf8StringSerializer())
    .OpenOrCreate();
 
  using var maintainer = new BasicZoneTreeMaintainer<int, string>(zoneTree);

  // 2. Read/Write data
  zoneTree.Upsert(39, "Hello ZoneTree!");

  // 3. Complete maintainer running tasks.
  maintainer.CompleteRunningTasks().AsTask().Wait();

How to delete keys?

In LSM trees, the deletions are handled by upserting key/value with deleted flag. Later on, during the compaction stage, the actual deletion happens. ZoneTree does not implement this flag format by default. It lets the user to define the suitable deletion flag themselves. For example, the deletion flag might be defined by user as -1 for int values. If user wants to use any int value as a valid record, then the value-type should be changed. For example, one can define the following struct and use this type as a value-type.

[StructLayout(LayoutKind.Sequential)]
struct MyDeletableValueType {
   int Number; 
   bool IsDeleted; 
}

ZoneTree gives flexibility to micro-manage the the tree size. The following sample shows how to configure the deletion markers for your database.

using var zoneTree = new ZoneTreeFactory<int, int>()
  // Additional stuff goes here
  .SetIsValueDeletedDelegate((in int x) => x == -1)
  .SetMarkValueDeletedDelegate((ref int x) => x = -1)
  .OpenOrCreate();  

or

using var zoneTree = new ZoneTreeFactory<int, MyDeletableValueType>()
  // Additional stuff goes here
  .SetIsValueDeletedDelegate((in MyDeletableValueType x) => x.IsDeleted)
  .SetMarkValueDeletedDelegate((ref MyDeletableValueType x) => x.IsDeleted = true)
  .OpenOrCreate();  

If you forget to provide the deletion marker delegates, you can never delete the record from your database.

How to iterate over data?

Iteration is possible in both directions, forward and backward. Unlike other LSM tree implementations, iteration performance is equal in both directions. The following sample shows how to do the iteration.

 using var zoneTree = new ZoneTreeFactory<int, int>()
    // Additional stuff goes here
    .OpenOrCreate();
 using var iterator = zoneTree.CreateIterator();
 while(iterator.Next()) {
    var key = iterator.CurrentKey;
    var value = iterator.CurrentValue;
 } 
 
 using var reverseIterator = zoneTree.CreateReverseIterator();
 while(reverseIterator.Next()) {
    var key = reverseIterator.CurrentKey;
    var value = reverseIterator.CurrentValue;
 }

How to iterate starting with a key (Seekable Iterator)?

ZoneTreeIterator provides Seek() method to jump into any record with in O(log(n)) complexity. That is useful for doing prefix search with forward-iterator or with backward-iterator.

 using var zoneTree = new ZoneTreeFactory<string, int>()
    // Additional stuff goes here
    .OpenOrCreate();
 using var iterator = zoneTree.CreateIterator();
 // iterator jumps into the first record starting with "SomePrefix" in O(log(n)) complexity. 
 iterator.Seek("SomePrefix");
 
 //iterator.Next() complexity is O(1)
 while(iterator.Next()) {
    var key = iterator.CurrentKey;
    var value = iterator.CurrentValue;
 } 

I need more information. Where can I find it?

I am going to write more detailed documentation as soon as possible.

I want to contribute. What should I do?

I appreciate any contribution to the project. These are the things I do think we need at the moment:

  1. Write tests / benchmarks.
  2. Write documentation.
  3. Convert documentation to a website using static site generators.
  4. Feature requests & bug fixes.
  5. Performance improvements.
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on ZoneTree:

Package Downloads
DifferentialComputeDotNet.Core

Package Description

ZoneTree.FullTextSearch

ZoneTree.FullTextSearch is an open-source library that extends ZoneTree to provide efficient full-text search capabilities. It offers a fast, embedded search engine suitable for applications that require high performance and do not rely on external databases.

ManagedCode.ZoneTree.BlobFileSystem

Azure Blob FileSystem for ZoneTree

ManagedCode.Database.ZoneTree

Repository for ZoneTree

RickDotNet.FusionStore.Stores.ZoneTree

A FusionCache and ZoneTree inspired data store abstraction.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.8.4 42 11/14/2024
1.8.3 203 10/16/2024
1.8.2 297 9/7/2024
1.8.1 119 9/7/2024
1.8.0 126 9/1/2024
1.7.9 138 8/30/2024
1.7.8 103 8/30/2024
1.7.7 164 8/24/2024
1.7.6 168 8/17/2024
1.7.5 132 8/17/2024
1.7.4 129 8/11/2024
1.7.3 105 8/10/2024
1.7.2 116 7/19/2024
1.7.1 220 6/14/2024
1.7.0 1,975 12/12/2023
1.6.9 628 9/13/2023
1.6.8 134 9/13/2023
1.6.7 132 9/13/2023
1.6.6 183 8/18/2023
1.6.5 194 6/17/2023
1.6.4 138 6/16/2023
1.6.3 201 5/29/2023
1.6.2 16,869 5/26/2023
1.6.1 283 4/5/2023
1.6.0 1,897 1/14/2023
1.5.9 308 1/14/2023
1.5.8 1,000 11/20/2022
1.5.7 382 11/14/2022
1.5.6 352 11/13/2022
1.5.5 466 10/20/2022
1.5.2 496 9/14/2022
1.5.1 415 9/3/2022
1.5.0 416 9/2/2022
1.4.9 418 8/31/2022
1.4.8 381 8/30/2022
1.4.7 417 8/30/2022
1.4.6 426 8/29/2022
1.4.5 440 8/28/2022
1.4.4 608 8/27/2022
1.4.3 426 8/26/2022
1.4.2 419 8/26/2022
1.4.1 388 8/26/2022
1.4.0 418 8/25/2022
1.3.9 404 8/25/2022
1.3.8 422 8/24/2022
1.3.7 684 8/23/2022
1.3.6 403 8/22/2022
1.3.5 425 8/22/2022
1.3.4 418 8/21/2022
1.3.3 405 8/18/2022
1.3.2 434 8/17/2022
1.3.1 403 8/17/2022
1.3.0 416 8/17/2022
1.2.9 404 8/16/2022
1.2.8 397 8/15/2022
1.2.7 423 8/15/2022
1.2.6 413 8/13/2022
1.2.5 428 8/13/2022
1.2.4 413 8/13/2022
1.2.3 438 8/9/2022
1.2.2 393 8/9/2022
1.2.1 436 8/9/2022
1.2.0 455 8/8/2022
1.1.9 429 8/8/2022
1.1.8 419 8/8/2022
1.1.7 443 8/8/2022
1.1.6 406 8/7/2022
1.1.5 432 8/6/2022
1.1.4 416 8/6/2022
1.1.3 423 8/5/2022
1.1.2 431 8/5/2022
1.1.1 431 7/27/2022
1.1.0 435 7/27/2022
1.0.9 424 7/25/2022
1.0.8 410 7/24/2022
1.0.7 385 7/23/2022
1.0.6 423 7/20/2022
1.0.5 437 7/18/2022
1.0.4 436 7/11/2022
1.0.3 474 7/11/2022
1.0.2 465 7/11/2022
1.0.1 450 7/6/2022
1.0.0 449 7/6/2022