Skip to content

Commit

Permalink
Merge tag '3.6.1' into port/3.6.1-to-3.7.1
Browse files Browse the repository at this point in the history
Libplanet 3.6.1
  • Loading branch information
greymistcube committed Nov 20, 2023
2 parents 14419c5 + 83f829b commit f3f4e5b
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 130 deletions.
14 changes: 14 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ Released on October 27, 2023.
[Bencodex.Json 0.16.0]: https://www.nuget.org/packages/Bencodex.json/0.16.0


Version 3.6.1
-------------

Released on November 20, 2023.

- (Libplanet.Store) Added optional `cache` parameter of type `HashNodeCache`
to `MerkleTrie()` constructors. [[#3495]]
- (Libplanet.Store) Added `HashNodeCache` class. [[#3495]]
- (Libplanet.Store) Changed internal caching strategy of `TrieStateStore` for
read/write optimization. [[#3495]]

[#3495]: https://github.com/planetarium/libplanet/pull/3495


Version 3.6.0
-------------

Expand Down
16 changes: 1 addition & 15 deletions Libplanet.Action/State/AccountState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,17 @@ namespace Libplanet.Action.State
public class AccountState : IAccountState
{
private ITrie _trie;
private AccountStateCache _cache;

public AccountState(ITrie trie)
{
_trie = trie;
_cache = new AccountStateCache();
}

/// <inheritdoc cref="IAccountState.Trie"/>
public ITrie Trie => _trie;

/// <inheritdoc cref="IAccountState.GetState"/>
public IValue? GetState(Address address)
{
if (_cache.TryGetValue(address, out IValue? cachedValue))
{
return cachedValue;
}
else
{
IValue? fetched = Trie.Get(ToStateKey(address));
_cache.AddOrUpdate(address, fetched);
return fetched;
}
}
public IValue? GetState(Address address) => Trie.Get(ToStateKey(address));

/// <inheritdoc cref="IAccountState.GetStates"/>
public IReadOnlyList<IValue?> GetStates(IReadOnlyList<Address> addresses) =>
Expand Down
76 changes: 0 additions & 76 deletions Libplanet.Action/State/AccountStateCache.cs

This file was deleted.

20 changes: 8 additions & 12 deletions Libplanet.Extensions.Cocona/Commands/MptCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,12 @@ public void Diff(
kvStoreUri = ConvertKVStoreUri(kvStoreUri, toolConfiguration);
otherKvStoreUri = ConvertKVStoreUri(otherKvStoreUri, toolConfiguration);

IKeyValueStore keyValueStore = LoadKVStoreFromURI(kvStoreUri);
IKeyValueStore otherKeyValueStore = LoadKVStoreFromURI(otherKvStoreUri);
var trie = new MerkleTrie(
keyValueStore,
HashDigest<SHA256>.FromString(stateRootHashHex));
var otherTrie = new MerkleTrie(
otherKeyValueStore,
HashDigest<SHA256>.FromString(otherStateRootHashHex));
IStateStore stateStore = new TrieStateStore(LoadKVStoreFromURI(kvStoreUri));
IStateStore otherStateStore = new TrieStateStore(LoadKVStoreFromURI(otherKvStoreUri));
var trie =
stateStore.GetStateRoot(HashDigest<SHA256>.FromString(stateRootHashHex));
var otherTrie =
otherStateStore.GetStateRoot(HashDigest<SHA256>.FromString(otherStateRootHashHex));

var codec = new Codec();
HashDigest<SHA256> originRootHash = trie.Hash;
Expand Down Expand Up @@ -113,10 +111,8 @@ public void Export(
ToolConfiguration toolConfiguration = configurationService.Load();
kvStoreUri = ConvertKVStoreUri(kvStoreUri, toolConfiguration);

IKeyValueStore keyValueStore = LoadKVStoreFromURI(kvStoreUri);
var trie = new MerkleTrie(
keyValueStore,
HashDigest<SHA256>.FromString(stateRootHashHex));
IStateStore stateStore = new TrieStateStore(LoadKVStoreFromURI(kvStoreUri));
var trie = stateStore.GetStateRoot(HashDigest<SHA256>.FromString(stateRootHashHex));
var codec = new Codec();

// This assumes the original key was encoded from a sensible string.
Expand Down
88 changes: 88 additions & 0 deletions Libplanet.Store/HashNodeCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System.Diagnostics;
using System.Security.Cryptography;
using System.Threading;
using Bencodex.Types;
using Libplanet.Common;
using Libplanet.Store.Trie;
using LruCacheNet;
using Serilog;

namespace Libplanet.Store
{
/// <summary>
/// A class used for internally caching hashed nodes of <see cref="MerkleTrie"/>s.
/// </summary>
public class HashNodeCache
{
// FIXME: Tuned to 9c mainnet. Should be refactored to accept cache size as an argument.
private const int _cacheSize = 1_048_576;
private const int _reportPeriod = 60_000;

private LruCache<HashDigest<SHA256>, IValue> _cache;
private Stopwatch _stopwatch;
private int _attempts;
private int _hits;
private object _reportLock;

internal HashNodeCache()
{
_cache = new LruCache<HashDigest<SHA256>, IValue>(_cacheSize);
_stopwatch = new Stopwatch();
_attempts = 0;
_hits = 0;
_reportLock = new object();
_stopwatch.Start();
}

public bool TryGetValue(HashDigest<SHA256> hash, out IValue? value)
{
lock (_reportLock)
{
Report();
}

Interlocked.Increment(ref _attempts);
if (_cache.TryGetValue(hash, out value))
{
Interlocked.Increment(ref _hits);
return true;
}
else
{
value = null;
return false;
}
}

public void AddOrUpdate(HashDigest<SHA256> hash, IValue value)
{
_cache.AddOrUpdate(hash, value);
}

private void Report()
{
long period = _stopwatch.ElapsedMilliseconds;
if (period > _reportPeriod)
{
Log
.ForContext("Source", nameof(HashNodeCache))
.ForContext("Tag", "Metric")
.ForContext("Subtag", "HashNodeCacheReport")
.Debug(
"Successfully fetched {HitCount} cached values out of last " +
"{AttemptCount} attempts with hitrate of {HitRate} and " +
"{CacheUsed}/{CacheSize} cache in use during last {PeriodMs} ms",
_hits,
_attempts,
(double)_hits / _attempts,
_cache.Count,
_cache.Capacity,
period);

_stopwatch.Restart();
Interlocked.Exchange(ref _attempts, 0);
Interlocked.Exchange(ref _hits, 0);
}
}
}
}
31 changes: 25 additions & 6 deletions Libplanet.Store/Trie/MerkleTrie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public partial class MerkleTrie : ITrie

private static readonly Codec _codec;

private readonly HashNodeCache _cache;

static MerkleTrie()
{
_codec = new Codec();
Expand All @@ -36,10 +38,12 @@ static MerkleTrie()
/// nodes.</param>
/// <param name="rootHash">The root <see cref="ITrie.Hash"/> of
/// <see cref="MerkleTrie"/>.</param>
/// <param name="cache">The <see cref="HashNodeCache"/> to use as cache.</param>
public MerkleTrie(
IKeyValueStore keyValueStore,
HashDigest<SHA256> rootHash)
: this(keyValueStore, new HashNode(rootHash))
HashDigest<SHA256> rootHash,
HashNodeCache? cache = null)
: this(keyValueStore, new HashNode(rootHash), cache)
{
}

Expand All @@ -50,13 +54,18 @@ public MerkleTrie(
/// nodes.</param>
/// <param name="root">The root node of <see cref="MerkleTrie"/>. If it is
/// <see langword="null"/>, it will be treated like empty trie.</param>
public MerkleTrie(IKeyValueStore keyValueStore, INode? root = null)
/// <param name="cache">The <see cref="HashNodeCache"/> to use as cache.</param>
public MerkleTrie(
IKeyValueStore keyValueStore,
INode? root = null,
HashNodeCache? cache = null)
{
// FIXME: It might be a good idea to have something like IReadOnlyKeyValueStore.
KeyValueStore = keyValueStore;
Root = root is HashNode hashNode && hashNode.HashDigest.Equals(EmptyRootHash)
? null
: root;
_cache = cache ?? new HashNodeCache();
}

/// <inheritdoc cref="ITrie.Root"/>
Expand Down Expand Up @@ -84,7 +93,7 @@ public ITrie Set(in KeyBytes key, IValue value)
new ValueNode(value),
true);

return new MerkleTrie(KeyValueStore, newRootNode);
return new MerkleTrie(KeyValueStore, newRootNode, _cache);
}

/// <inheritdoc cref="ITrie.Get(KeyBytes)"/>
Expand Down Expand Up @@ -439,8 +448,18 @@ private INode InsertToHashNode(
/// </remarks>
private INode UnhashNode(HashNode hashNode)
{
IValue intermediateEncoding = _codec.Decode(
KeyValueStore.Get(new KeyBytes(hashNode.HashDigest.ByteArray)));
IValue intermediateEncoding;
if (_cache.TryGetValue(hashNode.HashDigest, out var value))
{
intermediateEncoding = value!;
}
else
{
intermediateEncoding =
_codec.Decode(KeyValueStore.Get(new KeyBytes(hashNode.HashDigest.ByteArray)));
_cache.AddOrUpdate(hashNode.HashDigest, intermediateEncoding);
}

return NodeDecoder.Decode(intermediateEncoding, NodeDecoder.HashEmbeddedNodeType) ??
throw new NullReferenceException();
}
Expand Down
Loading

0 comments on commit f3f4e5b

Please sign in to comment.