ZoneTree 1.0.6
Prefix ReservedSee the version list below for details.
dotnet add package ZoneTree --version 1.0.6
NuGet\Install-Package ZoneTree -Version 1.0.6
<PackageReference Include="ZoneTree" Version="1.0.6" />
paket add ZoneTree --version 1.0.6
#r "nuget: ZoneTree, 1.0.6"
// Install ZoneTree as a Cake Addin #addin nuget:?package=ZoneTree&version=1.0.6 // Install ZoneTree as a Cake Tool #tool nuget:?package=ZoneTree&version=1.0.6
ZoneTree
ZoneTree is a persistent, high-performance, transactional, and ACID-compliant key-value database for .NET. It can operate in memory or on disk. (Optimized for SSDs)
ZoneTree is a lightweight, transactional 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?
- It is pure C#. Easy to maintain, easy to develop new features.
- 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).
- .NET EcoSystem does not have any feature-complete and thread-safe LSM Tree that operates both in memory and on disk.
- Supports transactional and non-transactional access with blazing speeds and ACID guarantees.
- Why do you need an SQL database or another product for the projects that a persistent tree bundled with your code is sufficient? You don't need to maintain another product shipped with yours!
- You decide where to put your data. You can adjust a few parameters to improve performance with more data loaded into memory whenever needed.When you don't need you can drop in memory data without a danger of a data loss.
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 Int32ComparerAscending())
.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 Int32ComparerAscending())
.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 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;
}
Transaction Support
ZoneTree supports Optimistic Transactions. It is proud to announce that the ZoneTree is ACID-compliant. Of course, you can use non-transactional API for the scenarios where eventual consistency is sufficient.
Please note that Transactional reads/writes are roughly three times slower than non-transactional ones.
The following sample shows how to do the transactions with ZoneTree.
using var zoneTree = new ZoneTreeFactory<int, int>()
// Additional stuff goes here
.OpenOrCreateTransactional();
try
{
var txId = zoneTree.BeginTransaction();
zoneTree.TryGet(txId, 3, out var value);
zoneTree.Upsert(txId, 3, 9);
var result = zoneTree.Prepare(txId);
while (result.IsPendingTransactions) {
Thread.Sleep(100);
result = zoneTree.Prepare(txId);
}
zoneTree.Commit(txId);
}
catch(TransactionAbortedException e)
{
//retry or cancel
}
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:
- Write tests / benchmarks.
- Write documentation.
- Convert documentation to a website using static site generators.
- Feature requests & bug fixes.
- Performance improvements.
Product | Versions 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. |
-
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 |