perf(net): make ElectrumClient's in-flight request cap configurable
MaxInFlight was a hardcoded constant (32); different indexing servers tolerate different levels of request concurrency before throttling, so expose it as an optional ConnectAsync parameter instead of requiring a recompile to tune initial-sync throughput against a given server.
This commit is contained in:
@@ -36,8 +36,12 @@ public sealed class ElectrumClient : IAsyncDisposable
|
|||||||
// single segment; this gate avoids flooding the server with thousands of
|
// single segment; this gate avoids flooding the server with thousands of
|
||||||
// simultaneous requests on large wallets → no bursts of -101/-102 nor
|
// simultaneous requests on large wallets → no bursts of -101/-102 nor
|
||||||
// connection drops. Writes still stay pipelined up to this degree.
|
// connection drops. Writes still stay pipelined up to this degree.
|
||||||
private const int MaxInFlight = 32;
|
// Configurable per connection (see ConnectAsync): the right value trades off
|
||||||
private readonly SemaphoreSlim _inFlight = new(MaxInFlight, MaxInFlight);
|
// initial-sync throughput against how aggressively a given server tolerates
|
||||||
|
// concurrent requests before throttling — no single constant is right for
|
||||||
|
// every server, so callers may raise/lower it instead of recompiling.
|
||||||
|
public const int DefaultMaxInFlight = 32;
|
||||||
|
private readonly SemaphoreSlim _inFlight;
|
||||||
|
|
||||||
private long _nextId;
|
private long _nextId;
|
||||||
|
|
||||||
@@ -49,19 +53,22 @@ public sealed class ElectrumClient : IAsyncDisposable
|
|||||||
public event Action<string, JsonElement>? NotificationReceived;
|
public event Action<string, JsonElement>? NotificationReceived;
|
||||||
public event Action<Exception?>? Disconnected;
|
public event Action<Exception?>? Disconnected;
|
||||||
|
|
||||||
private ElectrumClient(TcpClient tcp, Stream stream, string host, int port, bool useSsl)
|
private ElectrumClient(TcpClient tcp, Stream stream, string host, int port, bool useSsl,
|
||||||
|
int maxInFlight)
|
||||||
{
|
{
|
||||||
_tcp = tcp;
|
_tcp = tcp;
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
Host = host;
|
Host = host;
|
||||||
Port = port;
|
Port = port;
|
||||||
UseSsl = useSsl;
|
UseSsl = useSsl;
|
||||||
|
_inFlight = new SemaphoreSlim(maxInFlight, maxInFlight);
|
||||||
_readLoop = Task.Run(ReadLoopAsync);
|
_readLoop = Task.Run(ReadLoopAsync);
|
||||||
_writeLoop = Task.Run(WriteLoopAsync);
|
_writeLoop = Task.Run(WriteLoopAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ElectrumClient> ConnectAsync(string host, int port, bool useSsl,
|
public static async Task<ElectrumClient> ConnectAsync(string host, int port, bool useSsl,
|
||||||
CertificatePinStore? pins = null, CancellationToken ct = default)
|
CertificatePinStore? pins = null, CancellationToken ct = default,
|
||||||
|
int maxInFlight = DefaultMaxInFlight)
|
||||||
{
|
{
|
||||||
var tcp = new TcpClient { NoDelay = true };
|
var tcp = new TcpClient { NoDelay = true };
|
||||||
try
|
try
|
||||||
@@ -90,7 +97,7 @@ public sealed class ElectrumClient : IAsyncDisposable
|
|||||||
stream = ssl;
|
stream = ssl;
|
||||||
}
|
}
|
||||||
|
|
||||||
var client = new ElectrumClient(tcp, stream, host, port, useSsl);
|
var client = new ElectrumClient(tcp, stream, host, port, useSsl, maxInFlight);
|
||||||
await client.RequestAsync("server.version", ct, ClientName, ProtocolVersion);
|
await client.RequestAsync("server.version", ct, ClientName, ProtocolVersion);
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user