fix(client): a failover abandons itself once the user disconnects or reconnects; paging and teardown minors
Review I1: ConnectAsync, DisconnectAsync and Dispose each start a new connection epoch under _subscriptionLock. A keep-alive failover runs under the epoch it started in and abandons itself the moment it moves: it installs its session only if the epoch still matches (checked and assigned under the lock, otherwise the session is disposed), its state transitions are epoch-guarded, it stops trying endpoints, and it never flips a user's new connection to Disconnected. A late keep-alive from a previous connection's session starts no failover. Gate-driven tests cover the success and all-fail arms, the stale keep-alive, with a genuine failover as the positive control (4 of them red before). Minors: m8 only the CompareExchange winner sets LastFailover; m5 the endpoint loop stops early, and a failed Unsubscribe still drops its bookkeeping; m1 a read cancelled mid-paging releases its cursor (bounded token) and rethrows, and the cap release no longer rides the caller's token; m2 results are trimmed to the cap; m3 a non-positive cap is refused (CLI and UI already bound it to >= 1); m6 the subscription adapter's Dispose uses one bounded bridge for delete + detach. Refs #747 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -145,17 +145,16 @@ internal sealed class DefaultSubscriptionAdapter : ISubscriptionAdapter
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
// One bounded bridge (5 s) for delete + detach, so a dispose never blocks longer than before (review m6).
|
||||
// DetachAsync swallows its own failures; a failed delete must not skip the detach.
|
||||
try
|
||||
{
|
||||
DisposeBridge.Run(ct => _subscription.DeleteAsync(true, ct));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DisposeBridge.Run(DetachAsync);
|
||||
DisposeBridge.Run(async ct =>
|
||||
{
|
||||
try { await _subscription.DeleteAsync(true, ct); }
|
||||
catch (Exception ex) { Logger.Warning(ex, "Error deleting subscription"); }
|
||||
await DetachAsync(ct);
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user