Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mysteriumnetwork/node
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: shellroute/node
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 15 commits
  • 36 files changed
  • 1 contributor

Commits on May 28, 2026

  1. fix: detect WireGuard handshake when nanosecond component is zero

    The condition required both hsSec > 0 AND hsNano > 0, but a valid
    handshake can have nsec=0 (when it happens at an exact second boundary).
    Changed to OR so any non-zero timestamp is recognized as a successful
    handshake.
    cvl committed May 28, 2026
    Configuration menu
    Copy the full SHA
    db8ce1c View commit details
    Browse the repository at this point in the history
  2. fix: WireGuard log level respects --log-level flag

    WireGuard device logger was hardcoded to LogLevelVerbose, producing
    excessive DEBUG output (peer handshakes, keepalives) regardless of
    the node's --log-level setting.
    
    Added logconfig.WireGuardLogLevel() that maps zerolog levels:
      trace/debug → LogLevelVerbose (handshakes, keepalives)
      info/warn   → LogLevelError (errors only)
      error+      → LogLevelSilent
    
    All 5 device creation sites now call logconfig.WireGuardLogLevel()
    instead of hardcoding a level.
    cvl committed May 28, 2026
    Configuration menu
    Copy the full SHA
    b28a2c7 View commit details
    Browse the repository at this point in the history
  3. fix: proxyclient lifecycle — immediate close, sync bind, nil guards

    - Close(): close device immediately instead of 2-minute deferred goroutine.
      Orphaned devices accumulated, sending rogue keepalives.
    - ConfigureDevice(): close old device/proxy before creating new ones.
      ReConfigureDevice leaked old netstack, device, and proxy server.
    - Proxy(): synchronous net.Listen + server.Serve so port conflicts
      fail ConfigureDevice immediately instead of silently in background.
    - PeerStats(): nil/lock guard on Device to prevent panic after Close.
    - Suppress http.ErrServerClosed log noise on normal shutdown.
    cvl committed May 28, 2026
    Configuration menu
    Copy the full SHA
    57a54d8 View commit details
    Browse the repository at this point in the history
  4. fix: skip interface release and cleanup in proxymode

    - Stop(): skip ReleaseInterface when ProxyPort > 0. Proxymode names
      interfaces myst<port> without AllocateInterface, so release always
      errored with "allocated interface not found".
    - StartConsumerMode(): same skip on configure failure path.
    - cleanAbandonedInterfaces(): skip in proxymode (same reason as dVPN —
      multiple concurrent connections should not destroy each other).
    cvl committed May 28, 2026
    Configuration menu
    Copy the full SHA
    6194722 View commit details
    Browse the repository at this point in the history
  5. fix: skip keepalive auto-disconnect in proxymode

    In proxymode the gateway manages tunnel health via its own sentinel
    and probe mechanisms. The P2P keepalive failure (3 × 5s) was killing
    perfectly healthy WireGuard tunnels because the P2P channel (NATS/UDP
    signaling) is less stable than the tunnel itself in Docker.
    cvl committed May 28, 2026
    Configuration menu
    Copy the full SHA
    e1db169 View commit details
    Browse the repository at this point in the history
  6. fix: suppress context canceled error in proxy CONNECT handler

    When a probe client closes before the tunnel dial completes (deadline
    exceeded), DialContext returns context.Canceled. This is not a tunnel
    failure — just the client leaving early. Silencing prevents noisy
    ERR logs every 30 seconds from the gateway probe loop.
    cvl committed May 28, 2026
    Configuration menu
    Copy the full SHA
    1b8adde View commit details
    Browse the repository at this point in the history

Commits on May 30, 2026

  1. fix: force balance resync when spendable balance is near zero

    NeedsForceSync only checked BCBalance (channel capacity), not the
    actual spendable balance (BCBalance - GrandTotalPromised + BCSettled).
    After a Pilvytis top-up, BCBalance stays the same but Hermes resets
    GrandTotalPromised. Without checking spendable balance, the node
    used stale GrandTotalPromised and thought balance was zero for up
    to 30 minutes (offchain sync interval).
    cvl committed May 30, 2026
    Configuration menu
    Copy the full SHA
    29a03b4 View commit details
    Browse the repository at this point in the history
  2. fix: enable periodic balance sync for offchain (Pilvytis) identities

    lifetimeBCSync skipped entirely for offchain identities, meaning
    the balance was never refreshed from Hermes during the node's
    lifetime. After a top-up or limit change, the node used stale
    GrandTotalPromised indefinitely. Now offchain identities get the
    same periodic sync as on-chain ones (default: every 1 hour,
    configurable via --payments.balance-long-poll.interval).
    cvl committed May 30, 2026
    Configuration menu
    Copy the full SHA
    d494546 View commit details
    Browse the repository at this point in the history
  3. fix: fast balance sync when spendable balance below 1 MYST

    When GetBalance() drops below 1 MYST, the periodic sync interval
    switches from the configured poll interval (1 min) to 10 seconds.
    The node detects low balance itself and resyncs with Hermes
    frequently until a top-up is picked up — no external trigger needed.
    cvl committed May 30, 2026
    Configuration menu
    Copy the full SHA
    a1821fc View commit details
    Browse the repository at this point in the history
  4. test: balance sync — force sync on low spendable, fast interval, thre…

    …shold
    
    - TestNeedsForceSync_SpendableBalanceNearZero: spendable=0 triggers sync
      even when BCBalance (channel capacity) is high
    - TestNeedsForceSync_HealthyBalance: 4 MYST spendable does not trigger
    - TestPeriodicSync_FastIntervalWhenLowBalance: verifies threshold comparison
    - TestLowBalanceThreshold_Is1MYST: threshold is exactly 1e18 wei
    cvl committed May 30, 2026
    Configuration menu
    Copy the full SHA
    d48c949 View commit details
    Browse the repository at this point in the history
  5. build: add scripts/run-tests.sh for our modified packages

    Runs proxyclient, balance tracker, and connection manager tests
    with race detector, plus build checks across all three packages.
    cvl committed May 30, 2026
    Configuration menu
    Copy the full SHA
    441031d View commit details
    Browse the repository at this point in the history

Commits on Aug 4, 2026

  1. Configuration menu
    Copy the full SHA
    0a2d83d View commit details
    Browse the repository at this point in the history

Commits on Aug 12, 2026

  1. Merge pull request #1 from shellroute/fix/manager-lifecycle-clean

    Serialize connection manager lifecycle to prevent stale managers during concurrent connect/disconnect
    cvl authored Aug 12, 2026
    Configuration menu
    Copy the full SHA
    c035faa View commit details
    Browse the repository at this point in the history

Commits on Sep 3, 2026

  1. fix: make node connection lifecycle bounded to prevent gateway startu…

    …p deadlock (#2)
    
    * docs: add AGENTS.md as primary agent instructions, symlink CLAUDE.md
    
    * docs: add node lifecycle recovery plan
    
    * fix: make node connection lifecycle bounded
    
    Replace RWMutex/stripe barriers with generation-fenced state machine.
    No coordinator mutex spans Manager network calls.
    
    - Context-aware MultiManager API (Connect, Disconnect, Reconnect)
    - Generation fencing: bulk cleanup covers all pre-existing managers
    - Single-flight bulk: concurrent callers share one cleanup operation
    - Bounded shutdown: 5s deadline in Node.Kill, all stops proceed
    - ErrLifecycleBusy sentinel for active reconciliation
    - TequilAPI passes request context, returns 503 on lifecycle busy
    - Goroutine dump on bulk timeout for incident diagnosis
    - 15 deterministic lifecycle/race tests
    
    * fix: address review findings — orphan prevention, bulk ctx continuation, CancelCurrentOperation, retryable cleanup, port reservation
    
    * fix: worker-owned Connect finalization, server-owned bulk worker goroutine
    
    * fix: atomic port reservation, operation tracking, cleanup waits for operation completion
    
    * fix: Disconnect waits for existing cleanup instead of returning false nil
    
    * fix: worker owns cleanup on cancel/supersede, resultErr separates connect success from stale
    
    * fix: publish cleanup channel atomically with Disconnecting state, single-flight disconnect completion
    
    * fix: nil-guard Status/Stats/cancelManager for reserved entries with nil manager
    
    * fix: check supersession after factory construction, don't invoke Connect when generation advanced
    
    * fix: claim cleanup ownership under lock before waiting on operationDone, prevents double Disconnect
    
    * fix: cancelManager outside mu, context deadline/cancel maps to 503 in TequilAPI
    
    * test: Node.Kill bounded shutdown, cancelManager outside mu, context 503 mapping
    
    * fix: retireEntry waits for operation before nil-manager check, bulk fails on cleanup error instead of auto-retrying, recording shutdown test
    
    * fix: retry keeps generation, clear stale cleanup on new attempt, ctx check post-factory, linearize completion-vs-cancel tie, wrap errors with ErrLifecycleBusy
    
    * fix: immutable per-attempt cleanup result, ctx error preserves ErrConnectionCancelled+ErrLifecycleBusy, completion-vs-cancel linearization
    
    * fix: immutable per-attempt cleanup result object, waiters capture attempt not entry field
    
    * fix: tracked generation-fenced Reconnect, rejects during reconciliation, retires on supersession
    
    * fix: Reconnect uses worker-owned pattern with new operationDone, ctx select, phase admission, cleanup coordination
    
    * fix: production lifecycleManager capability — ConnectContext, DisconnectContext, ReconnectContext, CancelCurrentOperation
    
    * fix: lifecycleManager methods run synchronously in worker, Reconnect opDone closes after result/phase set
    
    * fix: deep manager refactor — ConnectContext owns lifetime ctx with AfterFunc, ReconnectContext sequential, legacy methods delegate to Context variants
    
    * fix: shared discoAttempt for truthful disconnect, NotConnected guard before lifetime ctx, error defer uses DisconnectContext, ReconnectContext uses DisconnectContext
    
    * fix: async cleanup in DisconnectContext, AfterFunc captures lifetimeCancel directly, error defer from lifetime creation, cancel outside ctxLock
    
    * fix(connection): ctx checks, CancelCurrentOperation cleanup, reconnect error capture
    
    - Add ctx.Err() checks after newConnection/initSession/getPublicIP in ConnectContext
    - Error defer: always call lifetimeCancel(), only DisconnectContext if state past NotConnected
    - runDisconnectCleanup uses CancelCurrentOperation() instead of direct ctxLock cancel
    - Multi.Reconnect captures and returns ReconnectContext error
    - Multi.retireEntry uses 30s deadline context for DisconnectContext instead of Background
    
    * test(connection): strengthen assertions, add reconnect error propagation tests
    
    - Fix weak/empty assertions in TestBulkDisconnectJoinsErrors and TestBlockedConnectVsBulkDisconnect
    - Add TestReconnectPropagatesError: verifies multi.Reconnect returns ReconnectContext errors
    - Add TestReconnectSucceeds: verifies happy-path reconnect via lifecycleManager
    - Add reconnectableManager mock implementing lifecycleManager interface
    
    * test(connection): gateway retry, stress test; docs: swagger 503 annotations
    
    - TestGatewayRetryAfterTimeout: first bulk times out, stuck cleanup blocks
      new connects, second bulk succeeds after unblock
    - TestRaceStressConnectDisconnect: 10 goroutines x 20 cycles + concurrent
      bulk disconnects, verified clean with -race -count=10
    - Add 503 Swagger annotations to PUT/DELETE /connection endpoints
    
    * style: gofmt formatting fixes
    
    * fix(test): reset activeProposal.Price in SetupTest to fix flaky TestDisconnectDueToPriceDrop
    
    TestDisconnectDueToPriceDrop mutates the package-level activeProposal
    price but never restores it. Under -count=2+, subsequent runs start
    with the already-mutated price so the drop threshold is never hit.
    
    * fix(connection): operation context per entry, disco ordering, error normalization, clearIPCache
    
    Addresses gateway agent review messages 869-876:
    
    - 876: Each Connect/Reconnect entry owns opCtx/opCancel derived from caller
      ctx. Bulk/individual retirement extracts and cancels opCancel outside mu
      before CancelCurrentOperation. Prevents fresh connect phase after bulk.
    - 869: DisconnectContext clears discoAttempt pointer under lock before
      closing done channel. Prevents ReconnectContext from racing a concurrent
      DisconnectContext that attaches to the stale completed attempt.
    - 870: Error defer normalizes cancellation: if ctx.Err()!=nil and error
      doesn't already wrap ErrConnectionCancelled, joins both. TequilAPI maps
      503 instead of 422.
    - 871: Re-register clearIPCache cleanup after statusConnecting so it runs
      on every disconnect but isn't stranded by early proposal/validation errors.
    - 872: Remove NotConnected guard around error-defer DisconnectContext.
      Unconditional call correctly attaches to running cleanup or returns
      ErrNoConnection harmlessly.
    - 873: retireEntry uses mcm.BulkTimeout instead of literal 30s. Remove
      e.cleanupAttempt=nil in Reconnect admission.
    
    Test: TestBulkCancelsReconnectBeforeFreshConnect — deterministic, 10/10 -race.
    Full suite: -race -count=20 clean.
    
    * fix(connection): normalize ctx error by concrete type, channel-based lifecycle test
    
    - Error defer: check !errors.Is(err, ctxErr) instead of ErrConnectionCancelled.
      Catches handleStartError returning bare ErrConnectionCancelled without the
      concrete ctx error that TequilAPI needs for 503 mapping.
    - Move TestBulkCancelsReconnectBeforeFreshConnect to multi_lifecycle_test.go
      per plan file-split guidance.
    - Replace sleep-based synchronization with disconnectEntered/cancelObserved
      channels for deterministic ordering.
    - Assert reconnect matches ErrConnectionCancelled/ErrLifecycleBusy, bulk==nil.
    
    * fix(connection): Stats only from phaseActive entries, regression test
    
    Multi.Stats now captures manager under mu only when phase==phaseActive.
    Connecting/reconnecting managers may not have initialized statsTracker,
    causing a data race between Stats read and Connect write (invariant 8).
    Status remains safe during any phase — manager.Status uses its own lock.
    
    Test: TestStatsSafeDuringConnect — concurrent Stats during blocked
    ConnectContext returns zero without race or panic.
    
    * test(connection): Stats phase gate proves manager not called during connecting
    
    Override Stats on slowConnectManager to panic if not activated and count
    calls. Asserts zero Stats calls during phaseConnecting (would panic
    without phase gate), then verifies one call with real data after active.
    
    * fix(connection): guard statsTracker with dedicated mutex for safe concurrent access
    
    statsTracker was a value type replaced wholesale during ConnectContext.
    A concurrent Stats() call could read the old tracker's RWMutex while a
    reconnect replaced it — causing "RUnlock of unlocked RWMutex" panic.
    
    Fix: statsTracker is now a pointer protected by statsLock. Stats()
    captures the pointer under lock then reads outside it. ConnectContext
    publishes new tracker under lock. Cleanup nils the pointer only if it
    still points to the current session's tracker.
    
    * Revert "fix(connection): guard statsTracker with dedicated mutex for safe concurrent access"
    
    This reverts commit 14c6045.
    
    * fix(connection): complete plan acceptance — tests, diagnostics, file split
    
    Manager-level lifecycle tests (from gateway probes):
    - TestConnectContextKeepsUnderlyingOperationTracked
    - TestDisconnectContextSharesRunningCleanup
    - TestConnectContextPreservesCallerCancellationFromWait
    
    Endpoint 503 tests:
    - TestCreateReturns503OnLifecycleBusy
    - TestDeleteReturns503OnLifecycleBusy
    - TestDeleteReturns503OnContextCanceled
    - TestDeleteReturns202OnSuccess
    
    Restored pre-plan tests (adapted for context-aware API):
    - TestConcurrentSamePortConnect
    - TestBlockedConnectVsIndividualDisconnect
    - TestExactManagerRemoval
    - TestConnectRacingBulkDisconnect
    
    Strengthened stress test: collect and assert only expected error types,
    assert final bulk succeeds.
    
    Structured lifecycle diagnostics: operation/port/generation/elapsed on
    Connect/Disconnect/Reconnect. Bulk timeout dumps state counts, sorted
    cleanup-running/pending port lists, elapsed. Caller timeout logged when
    shared cleanup continues server-side. One goroutine dump per generation.
    
    File split: types, retireEntry, waitRetirement, cancelManager,
    stateChanged, waitNotify extracted to multi_lifecycle.go (~210 LOC).
    
    * fix(connection): ctx-capturing endpoint tests, tighten restored tests, diagnostics improvements
    
    Endpoint tests: ctx-capturing mock proves PUT/DELETE forward request
    context. Table-driven for ErrLifecycleBusy, context.Canceled,
    context.DeadlineExceeded → 503; success DELETE → 202.
    
    Restored tests tightened:
    - BlockedConnectVsIndividualDisconnect: deadline instead of busy-spin,
      assert connect/disconnect results
    - ExactManagerRemoval: assert all errors
    - ConnectRacingBulkDisconnect: assert bulk error, final reconciliation
    
    Diagnostics:
    - Operation logs include returned error + generation
    - Caller-timeout log on both activeBulk joiner and starter paths
    - Cleanup-error path includes elapsed, retiring count, pending ports
    
    * refactor(connection): complete file split, prior-phase diagnostics, tighten tests
    
    File split: multi.go=417, multi_lifecycle.go=431, multi_test.go=410 LOC.
    disconnectAll + bulkWorker moved to multi_lifecycle.go alongside types,
    retireEntry, waitRetirement, cancelManager. All source files under 500.
    
    Diagnostics: portEntry.retire() preserves priorPhase. Bulk timeout
    snapshot reports was_connecting/was_reconnecting/was_active port lists.
    Operation logs include error + generation via named returns.
    
    Tests: endpoint 503 uses ctx-capturing mock + table-driven for all three
    error types + ctx!=nil assertion. Restored tests assert all errors and
    final reconciliation. Reconnect/timeout/retry tests moved to lifecycle
    test file.
    
    * fix(connection): retry failed cleanup, idempotent retire, ctx propagation tests
    
    Bug fix (invariant 7): waitRetirement resets a completed failed
    cleanupAttempt when no bulk owns the entry, allowing retireEntry to
    run again. TestIndividualDisconnectRetriesFailedCleanup verifies
    first cleanup fails, second retries and removes reservation.
    
    retire() is now idempotent — preserves priorPhase when already retiring.
    Prevents bulk re-retirement from overwriting diagnostic phase identity.
    
    Endpoint tests: ctx marker via context.WithValue proves exact request
    context propagation, not just non-nil.
    
    Manager lifecycle test: replaced busy-spin with sleep+channel approach.
    Renamed to TestConnectContextPreservesCallerCancellationFromLookup
    using bare connectionManager for deterministic control.
    
    * fix(connection): pre-canceled wrapping, retiring Reconnect, retry admission, test determinism
    
    Bugs:
    - Pre-canceled Connect/Reconnect wraps ErrConnectionCancelled+ctx.Err
      (was raw ctx.Err, mapping 422 not 503)
    - Reconnect checks retiring[id] → ErrLifecycleBusy (was ErrNoConnection)
    - waitRetirement resets failed cleanupAttempt for individual retry
    
    Tests:
    - TestConnectContextPreservesCallerCancellationFromWait: real eventbus,
      sync subscriber on Connecting, cancel during waitForConnectedState
    - TestPreCanceledConnectWrapsError, TestPreCanceledReconnectWrapsError
    - TestReconnectRejectsRetiringPort
    - TestBulkSingleFlight: blocking disconnect, asserts exactly 1 call
    - TestBlockedConnectVsBulkDisconnect: factory channel, notify wait
    - TestBlockedConnectVsIndividualDisconnect: factory channel
    - GPL copyright headers on all new test files
    - Swagger 503 regenerated
    
    blockingDisconnectManager.Disconnect increments disconnCount.
    
    * fix(connection): lifecycle snapshot diagnostics, embedded docs, headers
    
    Diagnostics §9: locked snapshot() helper returns sorted
    current/connecting/reconnecting/retiring/cleanup-running/was-* port
    lists. Used at bulk start/success/error/timeout and caller-timeout.
    Field names: operation=disconnect_all|connect|disconnect|reconnect,
    generation, elapsed, error, caller_timed_out. Timeout snapshot is
    fresh (recomputed after mu lock, not stale from before select).
    One pprof dump per unresolved generation preserved.
    
    Generated docs: swagger.json + docs.go re-embedded via mage
    generateSwagger + generateDocs. Exactly 2 new 503 response blocks.
    
    GPL copyright header added to multi_test.go (CI CheckCopyright fix).
    
    * fix(connection): diagnostics counts, caller_timed_out schema, admission-time generation
    
    - logSnapshot includes *_count for each list (current_count, connecting_count, etc.)
    - caller_timed_out=false on bulk start/success/error/timeout; =true on caller-timeout
    - Caller-timeout logs include elapsed (captured at disconnectAll entry)
    - Per-port defers capture generation at admission (opGen), not at return time
    
    * fix(connection): compile-time interface assertions for lifecycleManager and MultiManager
    
    * fix(connection): deterministic tests, compile-time assertions, remove all sleeps
    
    - TestTwoUnrelatedPortsConcurrent: block both managers, require both
      entered before release — proves concurrency not serialization
    - TestConnectCancellation: factory-entered channel replaces sleep
    - Replace all time.Sleep retirement waits with authoritative bulk cleanup
    - Compile-time interface assertions for lifecycleManager and MultiManager
    
    * fix(connection): TestTwoUnrelatedPorts signals from Manager.Connect, not factory
    
    * test(connection): deterministic bulk/retry/timeout tests, file split
    
    Tests added/rewritten per plan §937(3):
    - TestBulkSingleFlight: leader waits disconnectEntered, then joiners
      attach, asserts exactly 1 Manager.Disconnect
    - TestBlockedConnectVsIndividualDisconnect: waits retiring state via
      notify loop, asserts cleanup==1
    - TestBlockedConnectVsBulkDisconnect: asserts cleanup==1
    - TestBulkCleanupErrorRetrySucceeds: first error, second success,
      same generation, reconcile clears
    - TestCallerDeadlineServerContinues: leader times out, retry attaches,
      cleanup==1
    - TestGatewayRetryAfterTimeout: retry during running cleanup, no
      duplicate, cleanup==1
    
    File split: multi_bulk_test.go (413 LOC) has all bulk/timeout/retry/
    reconnect/invariant7/pre-canceled/retiring tests. multi_lifecycle_test.go
    (492 LOC) has phasedReconnect/stats/stress/restored connect tests.
    All test files under 500 LOC.
    
    * fix(connection): eliminate scheduling false-positives in deterministic tests
    
    - TestBulkSingleFlight: verify activeBulk exists while leader blocks,
      joiners attach via fast path
    - TestBlockedConnectVsBulkDisconnect: use connectEntered + notify
      predicate loop until retiring[100] exact, then release
    - TestGatewayRetryAfterTimeout: wait for activeBulk installed before
      release, assert cleanup==1 before release, restore Connect
      ErrLifecycleBusy + post-reconciliation success assertions
    - TestCallerDeadlineServerContinues: wait for activeBulk installed
      before release
    
    Targeted -race -count=100 clean on all four tests.
    
    * fix(connection): observingContext barrier for deterministic bulk tests
    
    observingContext wraps a context; closes observed channel on first
    Done() call, proving the goroutine reached its select statement.
    
    - TestBulkSingleFlight: two joiners use observingContext, wait for
      both observed channels before releasing gate
    - TestCallerDeadlineServerContinues: retry uses observingContext
      before release
    - TestGatewayRetryAfterTimeout: keeps activeBulk predicate (valid
      because first worker cleared it)
    - BulkSingleFlight moved to multi_bulk_test.go; reconnect tests
      back in multi_lifecycle_test.go
    
    Targeted -race -count=100 clean. Full suite -race -count=20 clean.
    
    * refactor(connection): extract reconnect tests to multi_reconnect_test.go, all files <=500
    cvl authored Sep 3, 2026
    Configuration menu
    Copy the full SHA
    6f92883 View commit details
    Browse the repository at this point in the history

Commits on Sep 9, 2026

  1. fix(p2p): honour ctx and channel stop when the send queue is full (#3)

    The send queue enqueue in sendRequest was a plain blocking chan send
    placed before the ctx select, so once the send loop got stuck on a peer
    that stopped acknowledging (kcp write blocked) the queue filled up and
    every Send blocked forever regardless of its context. Connection
    cleanup calls Send (session destroy) before closing the channel, so
    cleanup never finished, the port never left retirement, and bulk
    disconnect kept failing until the container was restarted.
    
    Enqueue now selects on ctx.Done() and the channel stop signal; reply
    enqueues in handleRequest give up once the channel is stopped.
    Regression test reproduces the full-queue hang.
    cvl authored Sep 9, 2026
    Configuration menu
    Copy the full SHA
    eb13008 View commit details
    Browse the repository at this point in the history
Loading