Skip to content

Subscription EndReason can be lost or mis-attributed #1169

Description

@mtmk

Surfaced during the OTel metrics PR review (separate from that PR).

NatsSubBase has two independent gates (_endSubscription and
_unsubscribed) and a _exception field that is written before
the reason gate. As a result, NatsSubBase.EndReason can be:

  1. Inconsistent with Exception. If a timer or cancellation token
    fires before an exception path runs, _exception is set but the
    reason field still shows Cancelled / IdleTimeout / etc., not
    Exception. The two fields disagree.

  2. Stuck at None for connection-driven drain. DrainAsync sets
    _unsubscribed = true directly and never calls EndSubscription.
    There is no Drained / ConnectionClosed enum value.

  3. Stuck at None for user-driven DisposeAsync. Same shape: no
    reason is recorded.

  4. Silently overwritten by the "first wins" gate when two end conditions
    race (two timers, timer + exception, etc.). The second caller's
    reason is dropped without surfacing the contention.

Possible directions (not prescriptive):

  • Prioritise Exception over previously-set reasons when _exception
    is written.
  • Add Drained / ConnectionClosed enum values and call
    EndSubscription from DrainAsync and the connection-dispose path.
  • Document the "None means user-disposed" convention if that is the
    intended contract.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions