fix(localdb): adapter review fixes — deterministic pump teardown, always-hosted initiator, backoff clamp
Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
This commit is contained in:
@@ -17,18 +17,17 @@ public sealed class LocalDbSyncService : LocalDbSync.LocalDbSyncBase
|
||||
private readonly SqliteLocalDb _db;
|
||||
private readonly ReplicationOptions _options;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger<LocalDbSyncService> _logger;
|
||||
|
||||
// 0 = idle, 1 = a stream is active. Interlocked-guarded across concurrent calls (singleton service).
|
||||
private int _active;
|
||||
|
||||
public LocalDbSyncService(ILocalDb db, IOptions<ReplicationOptions> options, ILoggerFactory loggerFactory)
|
||||
{
|
||||
_db = db as SqliteLocalDb
|
||||
?? throw new InvalidOperationException(
|
||||
$"LocalDbSyncService requires the ILocalDb registered by AddZbLocalDb (a {nameof(SqliteLocalDb)}); " +
|
||||
$"got {db.GetType().FullName}.");
|
||||
_db = SyncSessionFactory.RequireSqlite(db, nameof(LocalDbSyncService));
|
||||
_options = options.Value;
|
||||
_loggerFactory = loggerFactory;
|
||||
_logger = loggerFactory.CreateLogger<LocalDbSyncService>();
|
||||
}
|
||||
|
||||
public override async Task Sync(
|
||||
@@ -41,10 +40,29 @@ public sealed class LocalDbSyncService : LocalDbSync.LocalDbSyncBase
|
||||
|
||||
try
|
||||
{
|
||||
var (duplex, readerTask) = GrpcSyncDuplex.Create(requestStream, responseStream, context.CancellationToken);
|
||||
// The linked source exists so the reader pump is deterministically torn down on EVERY
|
||||
// exit path — including a session fault for a local (non-transport) reason, where the
|
||||
// call token never fires and the pump would otherwise keep reading a stream whose
|
||||
// handler has exited.
|
||||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(context.CancellationToken);
|
||||
var (duplex, readerTask) = GrpcSyncDuplex.Create(requestStream, responseStream, linkedCts.Token);
|
||||
var session = SyncSessionFactory.Create(_db, _options, _loggerFactory);
|
||||
await session.RunAsync(duplex, context.CancellationToken);
|
||||
await readerTask;
|
||||
try
|
||||
{
|
||||
await session.RunAsync(duplex, linkedCts.Token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
linkedCts.Cancel();
|
||||
try
|
||||
{
|
||||
await readerTask;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Sync reader pump ended during teardown.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (context.CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user