Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve LevelDBStore #3607

Merged
merged 7 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 19 additions & 22 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#nullable enable

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

Expand All @@ -20,14 +20,15 @@ namespace Neo.IO.Storage.LevelDB
/// <summary>
/// A DB is a persistent ordered map from keys to values.
/// A DB is safe for concurrent access from multiple threads without any external synchronization.
/// <code>Iterating over the whole dataset can be time-consuming. Depending upon how large the dataset is.</code>
/// </summary>
public class DB : LevelDBHandle
public class DB : LevelDBHandle, IEnumerable<KeyValuePair<byte[], byte[]>>
{
private DB(IntPtr handle) : base(handle) { }
private DB(nint handle) : base(handle) { }

protected override void FreeUnManagedObjects()
{
if (Handle != IntPtr.Zero)
if (Handle != nint.Zero)
{
Native.leveldb_close(Handle);
}
Expand All @@ -40,7 +41,7 @@ protected override void FreeUnManagedObjects()
/// </summary>
public void Delete(WriteOptions options, byte[] key)
{
Native.leveldb_delete(Handle, options.Handle, key, (UIntPtr)key.Length, out var error);
Native.leveldb_delete(Handle, options.Handle, key, (nuint)key.Length, out var error);
NativeHelper.CheckError(error);
}

Expand All @@ -50,24 +51,24 @@ public void Delete(WriteOptions options, byte[] key)
/// </summary>
public byte[] Get(ReadOptions options, byte[] key)
{
var value = Native.leveldb_get(Handle, options.Handle, key, (UIntPtr)key.Length, out var length, out var error);
var value = Native.leveldb_get(Handle, options.Handle, key, (nuint)key.Length, out var length, out var error);
try
{
NativeHelper.CheckError(error);
return value.ToByteArray(length);
}
finally
{
if (value != IntPtr.Zero) Native.leveldb_free(value);
if (value != nint.Zero) Native.leveldb_free(value);
}
}

public bool Contains(ReadOptions options, byte[] key)
{
var value = Native.leveldb_get(Handle, options.Handle, key, (UIntPtr)key.Length, out _, out var error);
var value = Native.leveldb_get(Handle, options.Handle, key, (nuint)key.Length, out _, out var error);
NativeHelper.CheckError(error);

if (value != IntPtr.Zero)
if (value != nint.Zero)
{
Native.leveldb_free(value);
return true;
Expand All @@ -76,12 +77,12 @@ public bool Contains(ReadOptions options, byte[] key)
return false;
}

public Snapshot GetSnapshot()
public Snapshot CreateSnapshot()
{
return new Snapshot(Handle);
}

public Iterator NewIterator(ReadOptions options)
public Iterator CreateIterator(ReadOptions options)
{
return new Iterator(Native.leveldb_create_iterator(Handle, options.Handle));
}
Expand All @@ -104,7 +105,7 @@ public static DB Open(string name, Options options)
/// </summary>
public void Put(WriteOptions options, byte[] key, byte[] value)
{
Native.leveldb_put(Handle, options.Handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length, out var error);
Native.leveldb_put(Handle, options.Handle, key, (nuint)key.Length, value, (nuint)value.Length, out var error);
NativeHelper.CheckError(error);
}

Expand All @@ -126,18 +127,14 @@ public void Write(WriteOptions options, WriteBatch write_batch)
NativeHelper.CheckError(error);
}

public IEnumerable<KeyValuePair<byte[], byte[]>> GetAll(Snapshot? snapshot = null)
public IEnumerator<KeyValuePair<byte[], byte[]>> GetEnumerator()
cschuchardt88 marked this conversation as resolved.
Show resolved Hide resolved
{
using var options = new ReadOptions();
if (snapshot != null) options.Snapshot = snapshot;

using var iterator = NewIterator(options);
iterator.SeekToFirst();
while (iterator.Valid())
{
using var iterator = CreateIterator(ReadOptions.Default);
for (iterator.SeekToFirst(); iterator.Valid(); iterator.Next())
yield return new KeyValuePair<byte[], byte[]>(iterator.Key(), iterator.Value());
iterator.Next();
}
}

IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
}
}
2 changes: 1 addition & 1 deletion src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static class Helper
{
public static IEnumerable<(byte[], byte[])> Seek(this DB db, ReadOptions options, byte[] prefix, SeekDirection direction)
{
using Iterator it = db.NewIterator(options);
using Iterator it = db.CreateIterator(options);
if (direction == SeekDirection.Forward)
{
for (it.Seek(prefix); it.Valid(); it.Next())
Expand Down
10 changes: 4 additions & 6 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;

namespace Neo.IO.Storage.LevelDB
{
/// <summary>
/// An iterator yields a sequence of key/value pairs from a database.
/// </summary>
public class Iterator : LevelDBHandle
{
internal Iterator(IntPtr handle) : base(handle) { }
internal Iterator(nint handle) : base(handle) { }

private void CheckError()
{
Expand All @@ -28,7 +26,7 @@ private void CheckError()

protected override void FreeUnManagedObjects()
{
if (Handle != IntPtr.Zero)
if (Handle != nint.Zero)
{
Native.leveldb_iter_destroy(Handle);
}
Expand Down Expand Up @@ -67,9 +65,9 @@ public void Prev()
/// The iterator is Valid() after this call if the source contains
/// an entry that comes at or past target.
/// </summary>
public void Seek(byte[] target)
public void Seek(byte[] key)
{
Native.leveldb_iter_seek(Handle, target, (UIntPtr)target.Length);
Native.leveldb_iter_seek(Handle, key, (nuint)key.Length);
}

public void SeekToFirst()
Expand Down
4 changes: 2 additions & 2 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ namespace Neo.IO.Storage.LevelDB
{
public enum CompressionType : byte
{
kNoCompression = 0x0,
kSnappyCompression = 0x1
NoCompression = 0x0,
SnappyCompression = 0x1
}

public static class Native
Expand Down
4 changes: 2 additions & 2 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,12 @@ public int BlockRestartInterval
/// incompressible, the kSnappyCompression implementation will
/// efficiently detect that and will switch to uncompressed mode.
/// </summary>
public CompressionType Compression
public CompressionType CompressionLevel
{
set { Native.leveldb_options_set_compression(Handle, value); }
}

public IntPtr FilterPolicy
public nint FilterPolicy
{
set { Native.leveldb_options_set_filter_policy(Handle, value); }
}
Expand Down
8 changes: 3 additions & 5 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;

namespace Neo.IO.Storage.LevelDB
{
/// <summary>
Expand All @@ -19,16 +17,16 @@ namespace Neo.IO.Storage.LevelDB
/// </summary>
public class Snapshot : LevelDBHandle
{
internal IntPtr _db;
internal nint _db;

internal Snapshot(IntPtr db) : base(Native.leveldb_create_snapshot(db))
internal Snapshot(nint db) : base(Native.leveldb_create_snapshot(db))
{
_db = db;
}

protected override void FreeUnManagedObjects()
{
if (Handle != IntPtr.Zero)
if (Handle != nint.Zero)
{
Native.leveldb_release_snapshot(_db, Handle);
}
Expand Down
18 changes: 16 additions & 2 deletions src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@

using Neo.IO.Storage.LevelDB;
using Neo.Persistence;
using System.Collections;
using System.Collections.Generic;
using LSnapshot = Neo.IO.Storage.LevelDB.Snapshot;

namespace Neo.Plugins.Storage
{
internal class Snapshot : ISnapshot
/// <summary>
/// <code>Iterating over the whole dataset can be time-consuming. Depending upon how large the dataset is.</code>
/// </summary>
internal class Snapshot : ISnapshot, IEnumerable<KeyValuePair<byte[], byte[]>>
{
private readonly DB _db;
private readonly LSnapshot _snapshot;
Expand All @@ -27,7 +31,7 @@ internal class Snapshot : ISnapshot
public Snapshot(DB db)
{
_db = db;
_snapshot = db.GetSnapshot();
_snapshot = db.CreateSnapshot();
_readOptions = new ReadOptions { FillCache = false, Snapshot = _snapshot };
_batch = new WriteBatch();
}
Expand Down Expand Up @@ -76,5 +80,15 @@ public bool TryGet(byte[] key, out byte[] value)
value = _db.Get(_readOptions, key);
return value != null;
}

public IEnumerator<KeyValuePair<byte[], byte[]>> GetEnumerator()
{
using var iterator = _db.CreateIterator(_readOptions);
cschuchardt88 marked this conversation as resolved.
Show resolved Hide resolved
for (iterator.SeekToFirst(); iterator.Valid(); iterator.Next())
yield return new KeyValuePair<byte[], byte[]>(iterator.Key(), iterator.Value());
}

IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
}
}
19 changes: 17 additions & 2 deletions src/Plugins/LevelDBStore/Plugins/Storage/Store.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,27 @@

using Neo.IO.Storage.LevelDB;
using Neo.Persistence;
using System.Collections;
using System.Collections.Generic;

namespace Neo.Plugins.Storage
{
internal class Store : IStore
/// <summary>
/// <code>Iterating over the whole dataset can be time-consuming. Depending upon how large the dataset is.</code>
/// </summary>
internal class Store : IStore, IEnumerable<KeyValuePair<byte[], byte[]>>
{
private readonly DB _db;
private readonly Options _options;

public Store(string path)
{
_options = new Options { CreateIfMissing = true, FilterPolicy = Native.leveldb_filterpolicy_create_bloom(15) };
_options = new Options
{
CreateIfMissing = true,
FilterPolicy = Native.leveldb_filterpolicy_create_bloom(15),
CompressionLevel = CompressionType.SnappyCompression,
cschuchardt88 marked this conversation as resolved.
Show resolved Hide resolved
};
_db = DB.Open(path, _options);
}

Expand Down Expand Up @@ -60,5 +69,11 @@ public bool TryGet(byte[] key, out byte[] value)

public IEnumerable<(byte[], byte[])> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) =>
_db.Seek(ReadOptions.Default, prefix, direction);

public IEnumerator<KeyValuePair<byte[], byte[]>> GetEnumerator() =>
_db.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
}
}
15 changes: 15 additions & 0 deletions tests/Neo.Plugins.Storage.Tests/StoreTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// modifications are permitted.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.IO.Storage.LevelDB;
using Neo.Persistence;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -72,6 +73,20 @@ public void TestLevelDb()
TestPersistenceRead(levelDbStore.GetStore(path_leveldb), false);
}

[TestMethod]
public void TestLevelDbDatabase()
{
using var db = DB.Open(Path.GetRandomFileName(), new() { CreateIfMissing = true });

db.Put(WriteOptions.Default, [0x00, 0x00, 0x01], [0x01]);
db.Put(WriteOptions.Default, [0x00, 0x00, 0x02], [0x02]);
db.Put(WriteOptions.Default, [0x00, 0x00, 0x03], [0x03]);

CollectionAssert.AreEqual(new byte[] { 0x01, }, db.Get(ReadOptions.Default, [0x00, 0x00, 0x01]));
CollectionAssert.AreEqual(new byte[] { 0x02, }, db.Get(ReadOptions.Default, [0x00, 0x00, 0x02]));
CollectionAssert.AreEqual(new byte[] { 0x03, }, db.Get(ReadOptions.Default, [0x00, 0x00, 0x03]));
}

[TestMethod]
public void TestLevelDbSnapshot()
{
Expand Down
Loading