All checks were successful
DCO / DCO (pull_request) Successful in 46s
first iteration - base classes and methods Signed-off-by: Pavel Gross <p.gross@yadro.com>
137 lines
3.7 KiB
C#
137 lines
3.7 KiB
C#
using System.Threading.Tasks;
|
|
|
|
namespace FrostFS.SDK.ClientV2;
|
|
|
|
// clientWrapper is used by default, alternative implementations are intended for testing purposes only.
|
|
public class ClientWrapper
|
|
{
|
|
private readonly object _lock = new();
|
|
|
|
public ClientWrapper(WrapperPrm wrapperPrm)
|
|
{
|
|
WrapperPrm = wrapperPrm;
|
|
StatusMonitor = new ClientStatusMonitor(wrapperPrm.Logger, wrapperPrm.Address, wrapperPrm.ErrorThreshold);
|
|
|
|
try
|
|
{
|
|
Client = new FrostFSClient(WrapperPrm);
|
|
StatusMonitor.SetHealthy();
|
|
}
|
|
catch (FrostFsException)
|
|
{
|
|
}
|
|
}
|
|
|
|
internal FrostFSClient? Client { get; private set; }
|
|
|
|
internal WrapperPrm WrapperPrm { get; }
|
|
|
|
internal ClientStatusMonitor StatusMonitor { get; }
|
|
|
|
internal FrostFSClient? GetClient()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (StatusMonitor.IsHealthy())
|
|
{
|
|
return Client;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
var client = GetClient();
|
|
|
|
if (client == null)
|
|
return "pool client unhealthy";
|
|
|
|
var result = await client.Dial(ctx).ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(result))
|
|
{
|
|
StatusMonitor.SetUnhealthyOnDial();
|
|
return result;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private async Task ScheduleGracefulClose()
|
|
{
|
|
if (Client == null)
|
|
return;
|
|
|
|
await Task.Delay((int)WrapperPrm.GracefulCloseOnSwitchTimeout).ConfigureAwait(false);
|
|
|
|
Client.Close();
|
|
}
|
|
|
|
// restartIfUnhealthy checks healthy status of client and recreate it if status is unhealthy.
|
|
// Indicating if status was changed by this function call and returns error that caused unhealthy status.
|
|
internal async Task<bool> RestartIfUnhealthy(CallContext ctx)
|
|
{
|
|
bool wasHealthy;
|
|
|
|
try
|
|
{
|
|
var prmNodeInfo = new PrmNodeInfo { Context = ctx };
|
|
var response = await Client!.GetNodeInfoAsync(prmNodeInfo).ConfigureAwait(false);
|
|
return false;
|
|
}
|
|
catch (FrostFsException)
|
|
{
|
|
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())
|
|
{
|
|
await ScheduleGracefulClose().ConfigureAwait(false);
|
|
}
|
|
|
|
#pragma warning disable CA2000 // Dispose objects before losing scope: will be disposed manually
|
|
FrostFSClient client = new(WrapperPrm);
|
|
#pragma warning restore CA2000
|
|
|
|
//TODO: set additioanl params
|
|
var error = await client.Dial(ctx).ConfigureAwait(false);
|
|
if (!string.IsNullOrEmpty(error))
|
|
{
|
|
StatusMonitor.SetUnhealthyOnDial();
|
|
return wasHealthy;
|
|
}
|
|
|
|
lock (_lock)
|
|
{
|
|
Client = client;
|
|
}
|
|
|
|
try
|
|
{
|
|
var prmNodeInfo = new PrmNodeInfo { Context = ctx };
|
|
var res = await client.GetNodeInfoAsync(prmNodeInfo).ConfigureAwait(false);
|
|
}
|
|
catch (FrostFsException)
|
|
{
|
|
StatusMonitor.SetUnhealthy();
|
|
return wasHealthy;
|
|
}
|
|
|
|
StatusMonitor.SetHealthy();
|
|
return !wasHealthy;
|
|
}
|
|
|
|
internal void IncRequests(ulong elapsed, MethodIndex method)
|
|
{
|
|
var methodStat = StatusMonitor.Methods[(int)method];
|
|
|
|
methodStat.IncRequests(elapsed);
|
|
}
|
|
}
|
|
|