[#24] Client: Implement pool part2

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

@ -1,38 +1,36 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Grpc.Core;
namespace FrostFS.SDK.ClientV2;
// clientWrapper is used by default, alternative implementations are intended for testing purposes only.
public class ClientWrapper
public class ClientWrapper : ClientStatusMonitor
{
private readonly object _lock = new();
public ClientWrapper(WrapperPrm wrapperPrm)
private SessionCache sessionCache;
internal ClientWrapper(WrapperPrm wrapperPrm, Pool pool) : base(wrapperPrm.Logger, wrapperPrm.Address)
{
WrapperPrm = wrapperPrm;
StatusMonitor = new ClientStatusMonitor(wrapperPrm.Logger, wrapperPrm.Address, wrapperPrm.ErrorThreshold);
ErrorThreshold = wrapperPrm.ErrorThreshold;
try
{
Client = new FrostFSClient(WrapperPrm);
StatusMonitor.SetHealthy();
}
catch (FrostFsException)
{
}
sessionCache = pool.SessionCache;
Client = new FrostFSClient(WrapperPrm, sessionCache);
}
internal FrostFSClient? Client { get; private set; }
internal WrapperPrm WrapperPrm { get; }
internal ClientStatusMonitor StatusMonitor { get; }
internal FrostFSClient? GetClient()
{
lock (_lock)
{
if (StatusMonitor.IsHealthy())
if (IsHealthy())
{
return Client;
}
@ -44,21 +42,29 @@ public class ClientWrapper
// dial establishes a connection to the server from the FrostFS network.
// Returns an error describing failure reason. If failed, the client
// SHOULD NOT be used.
internal async Task<string?> Dial(CallContext ctx)
internal async Task Dial(CallContext ctx)
{
var client = GetClient();
var client = GetClient() ?? throw new FrostFsInvalidObjectException("pool client unhealthy");
if (client == null)
return "pool client unhealthy";
await client.Dial(ctx).ConfigureAwait(false);
}
var result = await client.Dial(ctx).ConfigureAwait(false);
if (!string.IsNullOrEmpty(result))
internal void HandleError(Exception ex)
{
if (ex is FrostFsResponseException responseException && responseException.Status != null)
{
StatusMonitor.SetUnhealthyOnDial();
return result;
switch (responseException.Status.Code)
{
case FrostFsStatusCode.Internal:
case FrostFsStatusCode.WrongMagicNumber:
case FrostFsStatusCode.SignatureVerificationFailure:
case FrostFsStatusCode.NodeUnderMaintenance:
IncErrorRate();
return;
}
}
return null;
IncErrorRate();
}
private async Task ScheduleGracefulClose()
@ -79,31 +85,31 @@ public class ClientWrapper
try
{
var prmNodeInfo = new PrmNodeInfo { Context = ctx };
var prmNodeInfo = new PrmNodeInfo(ctx);
var response = await Client!.GetNodeInfoAsync(prmNodeInfo).ConfigureAwait(false);
return false;
}
catch (FrostFsException)
catch (RpcException)
{
wasHealthy = true;
}
// if connection is dialed before, to avoid routine/connection leak,
// pool has to close it and then initialize once again.
if (StatusMonitor.IsDialed())
if (IsDialed())
{
await ScheduleGracefulClose().ConfigureAwait(false);
}
#pragma warning disable CA2000 // Dispose objects before losing scope: will be disposed manually
FrostFSClient client = new(WrapperPrm);
FrostFSClient client = new(WrapperPrm, sessionCache);
#pragma warning restore CA2000
//TODO: set additioanl params
var error = await client.Dial(ctx).ConfigureAwait(false);
if (!string.IsNullOrEmpty(error))
{
StatusMonitor.SetUnhealthyOnDial();
SetUnhealthyOnDial();
return wasHealthy;
}
@ -114,22 +120,22 @@ public class ClientWrapper
try
{
var prmNodeInfo = new PrmNodeInfo { Context = ctx };
var prmNodeInfo = new PrmNodeInfo(ctx);
var res = await client.GetNodeInfoAsync(prmNodeInfo).ConfigureAwait(false);
}
catch (FrostFsException)
{
StatusMonitor.SetUnhealthy();
SetUnhealthy();
return wasHealthy;
}
StatusMonitor.SetHealthy();
SetHealthy();
return !wasHealthy;
}
internal void IncRequests(ulong elapsed, MethodIndex method)
{
var methodStat = StatusMonitor.Methods[(int)method];
var methodStat = Methods[(int)method];
methodStat.IncRequests(elapsed);
}