Skip to content

Commit

Permalink
Merge pull request #2337 from planetarium/rc-v200110-stagepolicy-test…
Browse files Browse the repository at this point in the history
…-heimdall

Prep for `v200111` hotfix
  • Loading branch information
area363 authored Dec 4, 2023
2 parents 1045728 + 5f2839c commit 5943f3d
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Lib9c
34 changes: 34 additions & 0 deletions NineChronicles.Headless.Tests/GraphTypes/StateQueryTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
Expand All @@ -15,6 +16,7 @@
using Nekoyume.Model.Garages;
using Nekoyume.Model.Item;
using Nekoyume.Model.State;
using Nekoyume.TableData;
using NineChronicles.Headless.GraphTypes;
using NineChronicles.Headless.GraphTypes.States;
using NineChronicles.Headless.Tests.Common;
Expand Down Expand Up @@ -222,6 +224,38 @@ public async Task Garage(
}
}

[Theory]
[InlineData(true, "expected")]
[InlineData(false, null)]
public async Task CachedSheet(bool cached, string? expected)
{
var tableName = nameof(ItemRequirementSheet);
var cache = new StateMemoryCache();
var cacheKey = Addresses.GetSheetAddress(tableName).ToString();
if (cached)
{
cache.SheetCache.SetSheet(cacheKey, (Text)expected, TimeSpan.FromMinutes(1));
}
var query = $"{{ cachedSheet(tableName: \"{tableName}\") }}";
MockState mockState = MockState.Empty;
var queryResult = await ExecuteQueryAsync<StateQuery>(
query,
source: new StateContext(
mockState,
0L, cache));
Assert.Null(queryResult.Errors);
var data = (Dictionary<string, object>)((ExecutionNode)queryResult.Data!).ToValue()!;
Assert.Equal(cached, cache.SheetCache.TryGetSheet(cacheKey, out byte[] _));
if (cached)
{
Assert.Equal(expected, data["cachedSheet"]);
}
else
{
Assert.Null(data["cachedSheet"]);
}
}

private static IEnumerable<object[]> GetMemberDataOfGarages()
{
var agentAddr = new PrivateKey().ToAddress();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,41 @@ public async Task TransactionResultIsSuccess()
Assert.Equal("SUCCESS", txStatus);
}

[Fact]
public async Task TransactionResults()
{
var privateKey = new PrivateKey();
// Because `AddActivatedAccount` doesn't need any prerequisites.
var action = new AddActivatedAccount(default);
Transaction tx = _blockChain.MakeTransaction(privateKey, new ActionBase[] { action });
var action2 = new DailyReward
{
avatarAddress = default
};
Transaction tx2 = _blockChain.MakeTransaction(new PrivateKey(), new ActionBase[] { action2 });
Block block = _blockChain.ProposeBlock(_proposer);
_blockChain.Append(block, GenerateBlockCommit(block.Index, block.Hash, _proposer));
var queryFormat = @"query {{
transactionResults(txIds: [""{0}"", ""{1}""]) {{
blockHash
txStatus
}}
}}";
var result = await ExecuteAsync(string.Format(
queryFormat,
tx.Id.ToString(),
tx2.Id.ToString()
));
Assert.NotNull(result.Data);
var transactionResults =
(object[])((Dictionary<string, object>)((ExecutionNode)result.Data!).ToValue()!)["transactionResults"];
Assert.Equal(2, transactionResults.Length);
var txStatus = (string)((Dictionary<string, object>)transactionResults[0])["txStatus"];
Assert.Equal("SUCCESS", txStatus);
txStatus = (string)((Dictionary<string, object>)transactionResults[1])["txStatus"];
Assert.Equal("FAILURE", txStatus);
}

[Fact]
public async Task NcTransactionsOnTip()
{
Expand Down
39 changes: 39 additions & 0 deletions NineChronicles.Headless.Tests/MemoryCacheExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Threading.Tasks;
using Bencodex;
using Bencodex.Types;
using MessagePack;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Nekoyume;
using Nekoyume.TableData;
using Xunit;

namespace NineChronicles.Headless.Tests;

public class MemoryCacheExtensionsTest
{
[Fact]
public async Task Sheet()
{
var codec = new Codec();
var lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
var cache = new MemoryCache(new OptionsWrapper<MemoryCacheOptions>(new MemoryCacheOptions
{
SizeLimit = null
}));

var sheets = TableSheetsImporter.ImportSheets();
var tableName = nameof(ItemRequirementSheet);
var csv = sheets[tableName];
var cacheKey = Addresses.GetSheetAddress(tableName).ToString();
var value = (Text)csv;
var compressed = MessagePackSerializer.Serialize(codec.Encode(value), lz4Options);
cache.SetSheet(cacheKey, value, TimeSpan.FromMilliseconds(100));
Assert.True(cache.TryGetValue(cacheKey, out byte[] cached));
Assert.Equal(compressed, cached);
Assert.Equal(csv, cache.GetSheet(cacheKey));
await Task.Delay(100);
Assert.False(cache.TryGetValue(cacheKey, out byte[] _));
}
}
2 changes: 1 addition & 1 deletion NineChronicles.Headless/ActionEvaluationPublisher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public ActionEvaluationPublisher(
{
var action = ev.Action;
var sheetAddress = Addresses.GetSheetAddress(action.TableName);
_memoryCache.Set(sheetAddress.ToString(), (Text)action.TableCsv);
_memoryCache.SetSheet(sheetAddress.ToString(), (Text)action.TableCsv, TimeSpan.FromMinutes(1));
}
});
}
Expand Down
9 changes: 3 additions & 6 deletions NineChronicles.Headless/BlockChainService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public class BlockChainService : ServiceBase<IBlockChainService>, IBlockChainSer
private ActionEvaluationPublisher _publisher;
private ConcurrentDictionary<string, Sentry.ITransaction> _sentryTraces;
private MemoryCache _memoryCache;
private readonly MessagePackSerializerOptions _lz4Options;

public BlockChainService(
BlockChain blockChain,
Expand All @@ -64,7 +63,6 @@ StateMemoryCache cache
_publisher = actionEvaluationPublisher;
_sentryTraces = sentryTraces;
_memoryCache = cache.SheetCache;
_lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);
}

public UnaryResult<bool> PutTransaction(byte[] txBytes)
Expand Down Expand Up @@ -223,7 +221,7 @@ public UnaryResult<Dictionary<byte[], byte[]>> GetSheets(
foreach (var b in addressBytesList)
{
var address = new Address(b);
if (_memoryCache.TryGetValue(address.ToString(), out byte[] cached))
if (_memoryCache.TryGetSheet(address.ToString(), out byte[] cached))
{
result.TryAdd(b, cached);
}
Expand All @@ -245,9 +243,8 @@ public UnaryResult<Dictionary<byte[], byte[]>> GetSheets(
for (int i = 0; i < addresses.Count; i++)
{
var address = addresses[i];
var value = _codec.Encode(values[i] ?? Null.Value);
var compressed = MessagePackSerializer.Serialize(value, _lz4Options);
_memoryCache.Set(address.ToString(), compressed);
var value = values[i] ?? Null.Value;
var compressed = _memoryCache.SetSheet(address.ToString(), value, TimeSpan.FromMinutes(1));
result.TryAdd(address.ToByteArray(), compressed);
}
}
Expand Down
20 changes: 19 additions & 1 deletion NineChronicles.Headless/GraphTypes/StateQuery.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Bencodex;
using Bencodex.Types;
using GraphQL;
using GraphQL.Types;
Expand All @@ -11,7 +12,6 @@
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Arena;
using Nekoyume.Battle;
using Nekoyume.Extensions;
using Nekoyume.Model.Arena;
using Nekoyume.Model.EnumType;
Expand All @@ -32,6 +32,8 @@ namespace NineChronicles.Headless.GraphTypes
{
public partial class StateQuery : ObjectGraphType<StateContext>
{
private readonly Codec _codec = new Codec();

public StateQuery()
{
Name = "StateQuery";
Expand Down Expand Up @@ -699,6 +701,22 @@ public StateQuery()
return result;
}
);

Field<StringGraphType>(
name: "cachedSheet",
arguments: new QueryArguments(
new QueryArgument<StringGraphType>
{
Name = "tableName"
}
),
resolve: context =>
{
var tableName = context.GetArgument<string>("tableName");
var cacheKey = Addresses.GetSheetAddress(tableName).ToString();
return context.Source.StateMemoryCache.SheetCache.GetSheet(cacheKey);
}
);
}

public static List<RuneOptionSheet.Row.RuneOptionInfo> GetRuneOptions(
Expand Down
89 changes: 54 additions & 35 deletions NineChronicles.Headless/GraphTypes/TransactionHeadlessQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,42 +192,22 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext)
),
resolve: context =>
{
if (!(standaloneContext.BlockChain is BlockChain blockChain))
{
throw new ExecutionError(
$"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
}

if (!(standaloneContext.Store is IStore store))
{
throw new ExecutionError(
$"{nameof(StandaloneContext)}.{nameof(StandaloneContext.Store)} was not set yet!");
}

TxId txId = context.GetArgument<TxId>("txId");
if (!(store.GetFirstTxIdBlockHashIndex(txId) is { } txExecutedBlockHash))
{
return blockChain.GetStagedTransactionIds().Contains(txId)
? new TxResult(TxStatus.STAGING, null, null, null, null, null)
: new TxResult(TxStatus.INVALID, null, null, null, null, null);
}
var txId = context.GetArgument<TxId>("txId");
return TxResult(standaloneContext, txId);
});

try
{
TxExecution execution = blockChain.GetTxExecution(txExecutedBlockHash, txId);
Block txExecutedBlock = blockChain[txExecutedBlockHash];
return new TxResult(
execution.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS,
txExecutedBlock.Index,
txExecutedBlock.Hash.ToString(),
execution.InputState,
execution.OutputState,
execution.ExceptionNames);
}
catch (Exception)
{
return new TxResult(TxStatus.INVALID, null, null, null, null, null);
}
Field<NonNullGraphType<ListGraphType<TxResultType>>>(
name: "transactionResults",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<ListGraphType<TxIdType>>>
{ Name = "txIds", Description = "transaction ids." }
),
resolve: context =>
{
return context.GetArgument<List<TxId>>("txIds")
.AsParallel()
.AsOrdered()
.Select(txId => TxResult(standaloneContext, txId));
}
);

Expand Down Expand Up @@ -312,6 +292,45 @@ public TransactionHeadlessQuery(StandaloneContext standaloneContext)
);
}

private static object? TxResult(StandaloneContext standaloneContext, TxId txId)
{
if (!(standaloneContext.BlockChain is BlockChain blockChain))
{
throw new ExecutionError(
$"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!");
}

if (!(standaloneContext.Store is IStore store))
{
throw new ExecutionError(
$"{nameof(StandaloneContext)}.{nameof(StandaloneContext.Store)} was not set yet!");
}

if (!(store.GetFirstTxIdBlockHashIndex(txId) is { } txExecutedBlockHash))
{
return blockChain.GetStagedTransactionIds().Contains(txId)
? new TxResult(TxStatus.STAGING, null, null, null, null, null)
: new TxResult(TxStatus.INVALID, null, null, null, null, null);
}

try
{
TxExecution execution = blockChain.GetTxExecution(txExecutedBlockHash, txId);
Block txExecutedBlock = blockChain[txExecutedBlockHash];
return new TxResult(
execution.Fail ? TxStatus.FAILURE : TxStatus.SUCCESS,
txExecutedBlock.Index,
txExecutedBlock.Hash.ToString(),
execution.InputState,
execution.OutputState,
execution.ExceptionNames);
}
catch (Exception)
{
return new TxResult(TxStatus.INVALID, null, null, null, null, null);
}
}

private IEnumerable<Block> ListBlocks(BlockChain chain, long from, long limit)
{
if (chain.Tip.Index < from)
Expand Down
35 changes: 35 additions & 0 deletions NineChronicles.Headless/MemoryCacheExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using Bencodex;
using Bencodex.Types;
using MessagePack;
using Microsoft.Extensions.Caching.Memory;

namespace NineChronicles.Headless;

public static class MemoryCacheExtensions
{
private static readonly Codec Codec = new Codec();
private static readonly MessagePackSerializerOptions Lz4Options = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4BlockArray);

public static byte[] SetSheet(this MemoryCache cache, string cacheKey, IValue value, TimeSpan ex)
{
var compressed = MessagePackSerializer.Serialize(Codec.Encode(value), Lz4Options);
cache.Set(cacheKey, compressed, ex);
return compressed;
}

public static bool TryGetSheet<T>(this MemoryCache cache, string cacheKey, out T cached)
{
return cache.TryGetValue(cacheKey, out cached);
}

public static string? GetSheet(this MemoryCache cache, string cacheKey)
{
if (cache.TryGetSheet(cacheKey, out byte[] cached))
{
return (Text)Codec.Decode(MessagePackSerializer.Deserialize<byte[]>(cached, Lz4Options));
}

return null;
}
}

0 comments on commit 5943f3d

Please sign in to comment.