[#24] Client: Implement pool part2
All checks were successful
DCO / DCO (pull_request) Successful in 46s

Signed-off-by: Pavel Gross <p.gross@yadro.com>
This commit is contained in:
Pavel Gross 2024-11-01 10:30:28 +03:00
parent c9a75ea025
commit ee20798379
63 changed files with 801 additions and 526 deletions

View file

@ -0,0 +1,33 @@
using Grpc.Core;
using Grpc.Core.Interceptors;
namespace FrostFS.SDK.SmokeTests;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods",
Justification = "parameters are provided by GRPC infrastructure")]
public class CallbackInterceptor(Action<string>? callback = null) : Interceptor
{
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(
HandleUnaryResponse(call),
call.ResponseHeadersAsync,
call.GetStatus,
call.GetTrailers,
call.Dispose);
}
private async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
{
var response = await call;
callback?.Invoke($"elapsed");
return response;
}
}

View file

@ -1,41 +0,0 @@
using System.Diagnostics;
using Grpc.Core;
using Grpc.Core.Interceptors;
namespace FrostFS.SDK.SmokeTests;
public class MetricsInterceptor() : Interceptor
{
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
TRequest request,
ClientInterceptorContext<TRequest, TResponse> context,
AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
{
ArgumentNullException.ThrowIfNull(continuation);
using var call = continuation(request, context);
return new AsyncUnaryCall<TResponse>(
HandleUnaryResponse(call),
call.ResponseHeadersAsync,
call.GetStatus,
call.GetTrailers,
call.Dispose);
}
private static async Task<TResponse> HandleUnaryResponse<TResponse>(AsyncUnaryCall<TResponse> call)
{
var watch = new Stopwatch();
watch.Start();
var response = await call.ResponseAsync.ConfigureAwait(false);
watch.Stop();
// Do something with call info
// var elapsed = watch.ElapsedTicks * 1_000_000/Stopwatch.Frequency;
return response;
}
}

View file

@ -28,19 +28,16 @@ public class NetworkTest : NetworkTestsBase
Mocker.Parameters.Add("HomomorphicHashingDisabled", [1]);
Mocker.Parameters.Add("MaintenanceModeAllowed", [1]);
var param = new PrmNetworkSettings();
if (useContext)
{
param.Context = new CallContext
var param = useContext ?
new PrmNetworkSettings(new CallContext
{
CancellationToken = Mocker.CancellationTokenSource.Token,
Timeout = TimeSpan.FromSeconds(20),
OwnerId = OwnerId,
Key = ECDsaKey,
Version = Version
};
}
})
: new PrmNetworkSettings();
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
@ -116,12 +113,11 @@ public class NetworkTest : NetworkTestsBase
Mocker.NetmapSnapshotResponse = new NetmapSnapshotResponse { Body = body };
var param = new PrmNetmapSnapshot();
PrmNetmapSnapshot param;
if (useContext)
{
param.XHeaders.Add("headerKey1", "headerValue1");
param.Context = new CallContext
var ctx = new CallContext
{
CancellationToken = Mocker.CancellationTokenSource.Token,
Timeout = TimeSpan.FromSeconds(20),
@ -129,6 +125,14 @@ public class NetworkTest : NetworkTestsBase
Key = ECDsaKey,
Version = Version
};
param = new(ctx);
param.XHeaders.Add("headerKey1", "headerValue1");
}
else
{
param = new();
}
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);
@ -208,12 +212,11 @@ public class NetworkTest : NetworkTestsBase
Mocker.NodeInfoResponse = new LocalNodeInfoResponse { Body = body };
var param = new PrmNodeInfo();
PrmNodeInfo param;
if (useContext)
{
param.XHeaders.Add("headerKey1", "headerValue1");
param.Context = new CallContext
var ctx = new CallContext
{
CancellationToken = Mocker.CancellationTokenSource.Token,
Timeout = TimeSpan.FromSeconds(20),
@ -221,6 +224,14 @@ public class NetworkTest : NetworkTestsBase
Key = ECDsaKey,
Version = Version
};
param = new(ctx);
param.XHeaders.Add("headerKey1", "headerValue1");
}
else
{
param = new();
}
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);

View file

@ -32,7 +32,7 @@ public class ObjectTest : ObjectTestsBase
var objectId = client.CalculateObjectId(Mocker.ObjectHeader!, ctx);
var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId) { Context = ctx });
var result = await client.GetObjectAsync(new PrmObjectGet(ContainerId, objectId, ctx));
Assert.NotNull(result);
@ -50,7 +50,7 @@ public class ObjectTest : ObjectTestsBase
[Fact]
public async void PutObjectTest()
{
Mocker.ResultObjectIds.Add(SHA256.HashData([]));
Mocker.ResultObjectIds!.Add(SHA256.HashData([]));
Random rnd = new();
var bytes = new byte[1024];
@ -107,7 +107,7 @@ public class ObjectTest : ObjectTestsBase
rnd.NextBytes(objIds.ElementAt(2));
foreach (var objId in objIds)
Mocker.ResultObjectIds.Add(objId);
Mocker.ResultObjectIds!.Add(objId);
var result = await GetClient().PutObjectAsync(param);

View file

@ -23,16 +23,6 @@ public class PoolSmokeTests : SmokeTestsBase
Key = keyString.LoadWif(),
NodeParams = [new(1, this.url, 100.0f)],
DialOptions = [new()
{
Authority = "",
Block = false,
DisableHealthCheck = false,
DisableRetry = false,
ReturnLastError = true,
Timeout = 30_000_000
}
],
ClientBuilder = null,
GracefulCloseOnSwitchTimeout = 30_000_000,
Logger = null
@ -85,6 +75,44 @@ public class PoolSmokeTests : SmokeTestsBase
Assert.Equal(9, result.Attributes.Count);
}
[Fact]
public async void NodeInfoStatisticsTwoNodesTest()
{
var options = new InitParameters
{
Key = keyString.LoadWif(),
NodeParams = [
new(1, this.url, 100.0f),
new(2, this.url.Replace('0', '1'), 100.0f)
],
ClientBuilder = null,
GracefulCloseOnSwitchTimeout = 30_000_000,
Logger = null
};
using var pool = new Pool(options);
var callbackText = string.Empty;
var ctx = new CallContext
{
Callback = (cs) => callbackText = $"{cs.MethodName} took {cs.ElapsedMicroSeconds} microseconds"
};
var error = await pool.Dial(ctx).ConfigureAwait(true);
Assert.Null(error);
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
var result = await client.GetNodeInfoAsync();
var statistics = pool.Statistic();
Assert.False(string.IsNullOrEmpty(callbackText));
Assert.Contains(" took ", callbackText, StringComparison.Ordinal);
}
[Fact]
public async void NodeInfoStatisticsTest()
{
@ -308,31 +336,29 @@ public class PoolSmokeTests : SmokeTestsBase
};
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]))
{
Context = ctx
};
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
var createdContainer = await pool.CreateContainerAsync(createContainerParam);
var container = await pool.GetContainerAsync(new PrmContainerGet(createdContainer) { Context = ctx });
var container = await pool.GetContainerAsync(new PrmContainerGet(createdContainer));
Assert.NotNull(container);
Assert.True(callbackInvoked);
var bytes = GetRandomBytes(objectSize);
var param = new PrmObjectPut
var ctx1 = new CallContext
{
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
};
var param = new PrmObjectPut(ctx1)
{
Header = new FrostFsObjectHeader(
containerId: createdContainer,
type: FrostFsObjectType.Regular,
[new FrostFsAttributePair("fileName", "test")]),
Payload = new MemoryStream(bytes),
ClientCut = false,
Context = new CallContext
{
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
}
ClientCut = false
};
var objectId = await pool.PutObjectAsync(param);
@ -400,19 +426,16 @@ public class PoolSmokeTests : SmokeTestsBase
};
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))))
{
Context = ctx
};
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))), ctx);
var container = await pool.CreateContainerAsync(createContainerParam);
var containerInfo = await pool.GetContainerAsync(new PrmContainerGet(container) { Context = ctx });
var containerInfo = await pool.GetContainerAsync(new PrmContainerGet(container, ctx));
Assert.NotNull(containerInfo);
var bytes = GetRandomBytes(objectSize);
var param = new PrmObjectPut
var param = new PrmObjectPut(new CallContext { Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) })
{
Header = new FrostFsObjectHeader(
containerId: container,
@ -420,10 +443,6 @@ public class PoolSmokeTests : SmokeTestsBase
[new FrostFsAttributePair("fileName", "test")]),
Payload = new MemoryStream(bytes),
ClientCut = false,
Context = new CallContext
{
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
},
SessionToken = token
};
@ -496,10 +515,11 @@ public class PoolSmokeTests : SmokeTestsBase
var ctx = new CallContext
{
Timeout = TimeSpan.FromSeconds(10),
Interceptors = new([new MetricsInterceptor()])
};
var container = await pool.GetContainerAsync(new PrmContainerGet(containerId) { Context = ctx });
ctx.Interceptors.Add(new CallbackInterceptor());
var container = await pool.GetContainerAsync(new PrmContainerGet(containerId, ctx));
Assert.NotNull(container);
@ -520,7 +540,7 @@ public class PoolSmokeTests : SmokeTestsBase
var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test");
bool hasObject = false;
await foreach (var objId in pool.SearchObjectsAsync(new PrmObjectSearch(containerId, filter)))
await foreach (var objId in pool.SearchObjectsAsync(new PrmObjectSearch(containerId, null, filter)))
{
hasObject = true;
@ -551,21 +571,10 @@ public class PoolSmokeTests : SmokeTestsBase
await Cleanup(pool);
var deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(5));
IAsyncEnumerator<FrostFsContainerId>? enumerator = null;
do
await foreach (var cid in pool.ListContainersAsync())
{
if (deadline <= DateTime.UtcNow)
{
Assert.Fail("Containers exist");
break;
}
enumerator = pool.ListContainersAsync().GetAsyncEnumerator();
await Task.Delay(500);
Assert.Fail($"Container {cid.GetValue()} exist");
}
while (await enumerator!.MoveNextAsync());
}
private static byte[] GetRandomBytes(int size)

View file

@ -14,12 +14,11 @@ public class SessionTest : SessionTestsBase
public async void CreateSessionTest(bool useContext)
{
var exp = 100u;
var param = new PrmSessionCreate(exp);
PrmSessionCreate param;
if (useContext)
{
param.XHeaders.Add("headerKey1", "headerValue1");
param.Context = new CallContext
var ctx = new CallContext
{
CancellationToken = Mocker.CancellationTokenSource.Token,
Timeout = TimeSpan.FromSeconds(20),
@ -27,6 +26,14 @@ public class SessionTest : SessionTestsBase
Key = ECDsaKey,
Version = Mocker.Version
};
param = new PrmSessionCreate(exp, ctx);
param.XHeaders.Add("headerKey1", "headerValue1");
}
else
{
param = new PrmSessionCreate(exp);
}
var validTimeoutFrom = DateTime.UtcNow.AddSeconds(20);

View file

@ -26,7 +26,7 @@ public class SmokeClientTests : SmokeTestsBase
? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url))
: FrostFSClient.GetInstance(GetOptions(this.url));
PrmBalance? prm = isSingleOnwerClient ? default : new() { Context = Ctx };
PrmBalance? prm = isSingleOnwerClient ? default : new(Ctx);
var result = await client.GetBalanceAsync(prm);
}
@ -38,7 +38,7 @@ public class SmokeClientTests : SmokeTestsBase
{
using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url));
PrmNetmapSnapshot? prm = isSingleOnwerClient ? default : new() { Context = Ctx };
PrmNetmapSnapshot? prm = isSingleOnwerClient ? default : new(Ctx);
var result = await client.GetNetmapSnapshotAsync(prm);
Assert.True(result.Epoch > 0);
@ -59,9 +59,11 @@ public class SmokeClientTests : SmokeTestsBase
[InlineData(true)]
public async void NodeInfoTest(bool isSingleOnwerClient)
{
using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url));
using var client = isSingleOnwerClient
? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url))
: FrostFSClient.GetInstance(GetOptions(this.url));
PrmNodeInfo? prm = isSingleOnwerClient ? default : new() { Context = Ctx };
PrmNodeInfo? prm = isSingleOnwerClient ? default : new(Ctx);
var result = await client.GetNodeInfoAsync(prm);
@ -93,7 +95,7 @@ public class SmokeClientTests : SmokeTestsBase
{
using var client = isSingleOnwerClient ? FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)) : FrostFSClient.GetInstance(GetOptions(this.url));
PrmSessionCreate? prm = isSingleOnwerClient ? new PrmSessionCreate(100) : new PrmSessionCreate(100) { Context = Ctx };
PrmSessionCreate? prm = isSingleOnwerClient ? new PrmSessionCreate(100) : new PrmSessionCreate(100, Ctx);
var token = await client.CreateSessionAsync(prm);
@ -262,31 +264,27 @@ public class SmokeClientTests : SmokeTestsBase
};
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]))
{
Context = ctx
};
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx);
var createdContainer = await client.CreateContainerAsync(createContainerParam);
var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer) { Context = ctx });
var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer, ctx));
Assert.NotNull(container);
Assert.True(callbackInvoked);
var bytes = GetRandomBytes(objectSize);
var param = new PrmObjectPut
var param = new PrmObjectPut(new CallContext
{
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
})
{
Header = new FrostFsObjectHeader(
containerId: createdContainer,
type: FrostFsObjectType.Regular,
[new FrostFsAttributePair("fileName", "test")]),
Payload = new MemoryStream(bytes),
ClientCut = false,
Context = new CallContext
{
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
}
ClientCut = false
};
var objectId = await client.PutObjectAsync(param);
@ -348,19 +346,19 @@ public class SmokeClientTests : SmokeTestsBase
};
var createContainerParam = new PrmContainerCreate(
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))))
{
Context = ctx
};
new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1))), ctx);
var container = await client.CreateContainerAsync(createContainerParam);
var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container) { Context = ctx });
var containerInfo = await client.GetContainerAsync(new PrmContainerGet(container, ctx));
Assert.NotNull(containerInfo);
var bytes = GetRandomBytes(objectSize);
var param = new PrmObjectPut
var param = new PrmObjectPut(new CallContext
{
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
})
{
Header = new FrostFsObjectHeader(
containerId: container,
@ -368,10 +366,6 @@ public class SmokeClientTests : SmokeTestsBase
[new FrostFsAttributePair("fileName", "test")]),
Payload = new MemoryStream(bytes),
ClientCut = false,
Context = new CallContext
{
Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0))
},
SessionToken = token
};
@ -437,11 +431,12 @@ public class SmokeClientTests : SmokeTestsBase
var ctx = new CallContext
{
Timeout = TimeSpan.FromSeconds(10),
Interceptors = new([new MetricsInterceptor()])
Timeout = TimeSpan.FromSeconds(10)
};
var container = await client.GetContainerAsync(new PrmContainerGet(containerId) { Context = ctx });
ctx.Interceptors.Add(new CallbackInterceptor());
var container = await client.GetContainerAsync(new PrmContainerGet(containerId, ctx));
Assert.NotNull(container);
@ -462,7 +457,7 @@ public class SmokeClientTests : SmokeTestsBase
var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test");
bool hasObject = false;
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, filter)))
await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(containerId, null, filter)))
{
hasObject = true;
@ -510,6 +505,38 @@ public class SmokeClientTests : SmokeTestsBase
while (await enumerator!.MoveNextAsync());
}
[Fact]
public async void NodeInfoCallbackAndInterceptorTest()
{
using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url));
bool callbackInvoked = false;
bool intercepterInvoked = false;
var ctx = new CallContext
{
Callback = new((CallStatistics cs) =>
{
callbackInvoked = true;
Assert.True(cs.ElapsedMicroSeconds > 0);
})
};
ctx.Interceptors.Add(new CallbackInterceptor(s => intercepterInvoked = true));
var result = await client.GetNodeInfoAsync(new PrmNodeInfo(ctx));
Assert.True(callbackInvoked);
Assert.True(intercepterInvoked);
Assert.Equal(2, result.Version.Major);
Assert.Equal(13, result.Version.Minor);
Assert.Equal(NodeState.Online, result.State);
Assert.Equal(33, result.PublicKey.Length);
Assert.Single(result.Addresses);
Assert.Equal(9, result.Attributes.Count);
}
private static byte[] GetRandomBytes(int size)
{
Random rnd = new();