diff --git a/.devcontainer/devcontainer.dockerfile b/.devcontainer/devcontainer.dockerfile index d2ccf16581..9e9dd5489a 100644 --- a/.devcontainer/devcontainer.dockerfile +++ b/.devcontainer/devcontainer.dockerfile @@ -1,6 +1,6 @@ # https://github.com/dotnet/dotnet-docker/blob/main/README.sdk.md # https://mcr.microsoft.com/en-us/artifact/mar/dotnet/sdk/tags <-- this shows all images -FROM mcr.microsoft.com/dotnet/sdk:9.0.101-noble +FROM mcr.microsoft.com/dotnet/sdk:9.0.102-noble # Install the libleveldb-dev package RUN apt-get update && apt-get install -y libleveldb-dev diff --git a/benchmarks/Directory.Build.props b/benchmarks/Directory.Build.props index 8ef2d9e6bb..8da9a97283 100644 --- a/benchmarks/Directory.Build.props +++ b/benchmarks/Directory.Build.props @@ -2,6 +2,7 @@ + latest false diff --git a/benchmarks/Neo.Benchmarks/Persistence/Bechmarks_LevelDB.cs b/benchmarks/Neo.Benchmarks/Persistence/Bechmarks_LevelDB.cs new file mode 100644 index 0000000000..357e6f493e --- /dev/null +++ b/benchmarks/Neo.Benchmarks/Persistence/Bechmarks_LevelDB.cs @@ -0,0 +1,59 @@ +// Copyright (C) 2015-2025 The Neo Project. +// +// Bechmarks_LevelDB.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using BenchmarkDotNet.Attributes; +using Neo.Persistence; +using Neo.Plugins.Storage; +using Neo.SmartContract; +using System.Diagnostics; + +namespace Neo.Benchmarks.Persistence.Benchmarks +{ + public class Bechmarks_LevelDB + { + // avoid allocations in benchmarks + private static StorageKey key1; + private static readonly byte[] value = new UInt256().GetSpan().ToArray(); + + private const string PathLevelDB = "Data_LevelDB_Benchmarks"; + + private static readonly LevelDBStore levelDb = new(); + private static ISnapshot snapshot; + + [GlobalSetup] + public void Setup() + { + if (Directory.Exists(PathLevelDB)) + Directory.Delete(PathLevelDB, true); + + key1 = new KeyBuilder(1, 1).Add(new UInt160()); + + var levelDbStore = levelDb.GetStore(PathLevelDB); + snapshot = levelDbStore.GetSnapshot(); + } + + [GlobalCleanup] + public void Cleanup() + { + snapshot.Dispose(); + levelDb.Dispose(); + if (Directory.Exists(PathLevelDB)) + Directory.Delete(PathLevelDB, true); + } + + [Benchmark] + public void LevelDBSnapshotWrites() + { + snapshot.Put(key1.ToArray(), value); + snapshot.Delete(key1.ToArray()); + } + } +} diff --git a/benchmarks/Neo.Benchmarks/Program.cs b/benchmarks/Neo.Benchmarks/Program.cs index 4734db60fc..437da9ed94 100644 --- a/benchmarks/Neo.Benchmarks/Program.cs +++ b/benchmarks/Neo.Benchmarks/Program.cs @@ -19,3 +19,4 @@ BenchmarkRunner.Run(); BenchmarkRunner.Run(); BenchmarkRunner.Run(); +BenchmarkRunner.Run(); diff --git a/global.json b/global.json index 41c9ad2ed8..13ac5fc149 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "9.0.102", "rollForward": "latestFeature", "allowPrerelease": false } diff --git a/src/Neo/Persistence/MemorySnapshot.cs b/src/Neo/Persistence/MemorySnapshot.cs index 930c983a4d..9c986c46cc 100644 --- a/src/Neo/Persistence/MemorySnapshot.cs +++ b/src/Neo/Persistence/MemorySnapshot.cs @@ -20,6 +20,9 @@ namespace Neo.Persistence { + /// + /// On-chain write operations on a snapshot cannot be concurrent. + /// internal class MemorySnapshot : ISnapshot { private readonly ConcurrentDictionary _innerData; @@ -47,9 +50,7 @@ public void Delete(byte[] key) _writeBatch[key] = null; } - public void Dispose() - { - } + public void Dispose() { } public void Put(byte[] key, byte[] value) { diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs index be820473c0..cfe9d0ca8b 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -19,6 +19,7 @@ namespace Neo.Plugins.Storage { /// /// Iterating over the whole dataset can be time-consuming. Depending upon how large the dataset is. + /// On-chain write operations on a snapshot cannot be concurrent. /// internal class Snapshot : ISnapshot, IEnumerable> { @@ -26,7 +27,12 @@ internal class Snapshot : ISnapshot, IEnumerable> private readonly LSnapshot _snapshot; private readonly ReadOptions _readOptions; private readonly WriteBatch _batch; + +#if NET9_0_OR_GREATER + private readonly System.Threading.Lock _lock = new(); +#else private readonly object _lock = new(); +#endif public Snapshot(DB db) { @@ -36,18 +42,27 @@ public Snapshot(DB db) _batch = new WriteBatch(); } + /// public void Commit() { lock (_lock) _db.Write(WriteOptions.Default, _batch); } + /// public void Delete(byte[] key) { lock (_lock) _batch.Delete(key); } + /// + public void Put(byte[] key, byte[] value) + { + lock (_lock) + _batch.Put(key, value); + } + public void Dispose() { _snapshot.Dispose(); @@ -60,12 +75,6 @@ public void Dispose() return _db.Seek(_readOptions, keyOrPrefix, direction); } - public void Put(byte[] key, byte[] value) - { - lock (_lock) - _batch.Put(key, value); - } - public bool Contains(byte[] key) { return _db.Contains(_readOptions, key); diff --git a/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs index 1f4c7616df..cba1cc2942 100644 --- a/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs @@ -16,6 +16,9 @@ namespace Neo.Plugins.Storage { + /// + /// On-chain write operations on a snapshot cannot be concurrent. + /// internal class Snapshot : ISnapshot { private readonly RocksDb db; diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index e4089c9a68..09624646bb 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -2,7 +2,7 @@ - 12.0 + latest false false true