Skip to content

Commit 7471b80

Browse files
devhawkHarry
andauthored
Persistence Refactoring (#44)
* Extensions.Seek * move nullstore out of CheckpointStorageProvider * new None to default(None) * initial MemoryTrackingStore * add seek tests * minor cleanup * RocksDbStore * Checkpoint class * remove storageProvider specific Store and Snapshot * MemoryTrackingStore.AtomicUpdate * fix CheckpointStorageProvider.GetStorageProviderStore * RocksDbUtility * remove global.json Co-authored-by: Harry <[email protected]>
1 parent 25feb1e commit 7471b80

20 files changed

+889
-493
lines changed

global.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/bctklib/persistence/ByteArrayComparer.cs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,32 @@
44

55
namespace Neo.BlockchainToolkit.Persistence
66
{
7-
internal class ByteArrayComparer : IEqualityComparer<byte[]>, IComparer<byte[]>
7+
class ByteArrayComparer : IEqualityComparer<byte[]>, IComparer<byte[]>
88
{
99
public static ByteArrayComparer Default { get; } = new ByteArrayComparer(false);
1010
public static ByteArrayComparer Reverse { get; } = new ByteArrayComparer(true);
1111

12-
private readonly bool reverse;
12+
readonly bool reverse;
1313

14-
private ByteArrayComparer(bool reverse = false)
14+
ByteArrayComparer(bool reverse = false)
1515
{
1616
this.reverse = reverse;
1717
}
1818

19-
public int Compare([AllowNull] byte[] x, [AllowNull] byte[] y)
19+
public int Compare(byte[]? x, byte[]? y)
2020
{
21-
return reverse ? -Compare() : Compare();
21+
if (x == null && y == null) return 0;
22+
if (x == null) return reverse ? 1 : -1;
23+
if (y == null) return reverse ? -1 : 1;
24+
return Compare(x.AsSpan(), y.AsSpan());
25+
}
2226

23-
int Compare()
24-
{
25-
if (x == null && y == null)
26-
return 0;
27-
if (x == null)
28-
return -1;
29-
if (y == null)
30-
return 1;
31-
return x.AsSpan().SequenceCompareTo(y.AsSpan());
32-
}
27+
public int Compare(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y)
28+
{
29+
return reverse ? y.SequenceCompareTo(x) : x.SequenceCompareTo(y);
3330
}
3431

35-
public bool Equals([AllowNull] byte[] x, [AllowNull] byte[] y)
32+
public bool Equals(byte[]? x, byte[]? y)
3633
{
3734
if (x == null && y == null)
3835
return true;
@@ -41,14 +38,14 @@ public bool Equals([AllowNull] byte[] x, [AllowNull] byte[] y)
4138
return x.AsSpan().SequenceEqual(y.AsSpan());
4239
}
4340

44-
public int GetHashCode([DisallowNull] byte[] obj)
41+
public int GetHashCode(byte[] obj)
4542
{
46-
int hash = 0;
43+
var hash = new HashCode();
4744
for (int i = 0; i < obj.Length; i++)
4845
{
49-
hash = HashCode.Combine(hash, i, obj[i]);
46+
hash.Add(obj[i]);
5047
}
51-
return hash;
48+
return hash.ToHashCode();
5249
}
5350
}
5451
}

src/bctklib/persistence/CheckpointStorageProvider.NullStore.cs

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/bctklib/persistence/CheckpointStorageProvider.Snapshot.cs

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/bctklib/persistence/CheckpointStorageProvider.Store.cs

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/bctklib/persistence/CheckpointStorageProvider.cs

Lines changed: 22 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,121 +2,54 @@
22
using System.Collections.Generic;
33
using System.Collections.Immutable;
44
using System.Diagnostics.CodeAnalysis;
5-
using System.Linq;
65
using Neo.Persistence;
7-
using OneOf;
8-
using None = OneOf.Types.None;
6+
using Neo.Plugins;
97

108
namespace Neo.BlockchainToolkit.Persistence
119
{
12-
using TrackingMap = ImmutableSortedDictionary<byte[], OneOf<byte[]?, None>>;
13-
1410
public partial class CheckpointStorageProvider : IDisposableStorageProvider
1511
{
16-
public readonly static TrackingMap EMPTY_TRACKING_MAP = TrackingMap.Empty.WithComparers(ByteArrayComparer.Default);
17-
18-
readonly RocksDbStorageProvider? rocksDbProvider;
19-
readonly bool disposeRocksDbProvider;
12+
readonly IStorageProvider? storageProvider;
2013
readonly IDisposable? checkpointCleanup;
14+
readonly Lazy<IStore> defaultStore;
15+
ImmutableDictionary<string, IStore> stores = ImmutableDictionary<string, IStore>.Empty;
2116

22-
ImmutableDictionary<string, TrackingMap> trackingMaps = ImmutableDictionary<string, TrackingMap>.Empty;
23-
TrackingMap defaultTrackingMap = EMPTY_TRACKING_MAP;
17+
public CheckpointStorageProvider(RocksDbStorageProvider? rocksDbStorageProvider, IDisposable? checkpointCleanup = null)
18+
: this((IStorageProvider?)rocksDbStorageProvider, checkpointCleanup)
19+
{
20+
}
2421

25-
public CheckpointStorageProvider(RocksDbStorageProvider? rocksDbStorageProvider, bool disposeRocksDbProvider = true, IDisposable? checkpointCleanup = null)
22+
public CheckpointStorageProvider(IStorageProvider? storageProvider, IDisposable? checkpointCleanup = null)
2623
{
27-
this.rocksDbProvider = rocksDbStorageProvider;
28-
this.disposeRocksDbProvider = disposeRocksDbProvider;
24+
this.storageProvider = storageProvider;
2925
this.checkpointCleanup = checkpointCleanup;
26+
27+
defaultStore = new Lazy<IStore>(() => new MemoryTrackingStore(GetStorageProviderStore(null)));
3028
}
3129

3230
public void Dispose()
3331
{
34-
if (disposeRocksDbProvider) rocksDbProvider?.Dispose();
32+
(storageProvider as IDisposable)?.Dispose();
3533
checkpointCleanup?.Dispose();
3634
}
3735

38-
internal TrackingMap GetTrackingMap(string? storeName)
39-
=> storeName == null
40-
? defaultTrackingMap
41-
: trackingMaps.TryGetValue(storeName, out var trackingMap)
42-
? trackingMap
43-
: EMPTY_TRACKING_MAP;
44-
45-
IReadOnlyStore GetReadOnlyStore(string? storeName)
46-
=> (rocksDbProvider != null && rocksDbProvider.TryGetStore(storeName, out var store))
47-
? store
48-
: NullStore.Instance;
49-
5036
public IStore GetStore(string? storeName)
5137
{
52-
return new Store(this, storeName);
38+
if (storeName == null) return defaultStore.Value;
39+
return ImmutableInterlocked.GetOrAdd(ref stores, storeName,
40+
key => new MemoryTrackingStore(GetStorageProviderStore(key)));
5341
}
5442

55-
internal ISnapshot GetSnapshot(string? storeName)
43+
IReadOnlyStore GetStorageProviderStore(string? path)
5644
{
57-
var map = GetTrackingMap(storeName);
58-
return new CheckpointStorageProvider.Snapshot(this, map, storeName);
59-
}
60-
61-
internal byte[]? TryGet(string? storeName, byte[]? key) => TryGet(storeName, GetTrackingMap(storeName), key);
62-
63-
internal byte[]? TryGet(string? storeName, TrackingMap map, byte[]? key)
64-
{
65-
if (map.TryGetValue(key ?? Array.Empty<byte>(), out var mapValue))
45+
IReadOnlyStore? roStore = null;
46+
try
6647
{
67-
return mapValue.Match<byte[]?>(v => v, n => null);
48+
roStore = storageProvider?.GetStore(path);
6849
}
50+
catch {}
6951

70-
return GetReadOnlyStore(storeName).TryGet(key);
71-
}
72-
73-
internal IEnumerable<(byte[] Key, byte[]? Value)> Seek(string? storeName, byte[]? key, SeekDirection direction)
74-
=> Seek(storeName, GetTrackingMap(storeName), key, direction);
75-
76-
internal IEnumerable<(byte[] Key, byte[]? Value)> Seek(string? storeName, TrackingMap map, byte[]? key, SeekDirection direction)
77-
{
78-
key ??= Array.Empty<byte>();
79-
var comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse;
80-
81-
var memoryItems = map
82-
.Where(kvp => kvp.Value.IsT0)
83-
.Where(kvp => key.Length == 0 || comparer.Compare(kvp.Key, key) >= 0)
84-
.Select(kvp => (kvp.Key, Value: kvp.Value.AsT0));
85-
86-
var storeItems = GetReadOnlyStore(storeName)
87-
.Seek(key, direction)
88-
.Where<(byte[] Key, byte[]? Value)>(kvp => !map.ContainsKey(kvp.Key));
89-
90-
return memoryItems.Concat(storeItems).OrderBy(kvp => kvp.Key, comparer);
91-
}
92-
93-
internal void Update(string? storeName, byte[]? key, OneOf<byte[]?, None> value)
94-
{
95-
var trackingMap = GetTrackingMap(storeName);
96-
trackingMap = trackingMap.SetItem(key ?? Array.Empty<byte>(), value);
97-
UpdateTrackingMap(storeName, trackingMap);
98-
}
99-
100-
internal void Update(string? storeName, TrackingMap changes)
101-
{
102-
var trackingMap = GetTrackingMap(storeName);
103-
foreach (var change in changes)
104-
{
105-
trackingMap = trackingMap.SetItem(change.Key, change.Value);
106-
}
107-
UpdateTrackingMap(storeName, trackingMap);
108-
}
109-
110-
void UpdateTrackingMap(string? storeName, TrackingMap changes)
111-
{
112-
if (storeName == null)
113-
{
114-
defaultTrackingMap = changes;
115-
}
116-
else
117-
{
118-
trackingMaps = trackingMaps.SetItem(storeName, changes);
119-
}
52+
return roStore ?? NullStore.Instance;
12053
}
12154
}
12255
}

0 commit comments

Comments
 (0)