lifecycle

package
v1.53.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Overview

Package lifecycle classifies "why did this goroutine generation end" from several independent local signals (a deliberate-close flag, a clean-exit flag, ctx.Err()) into a closed Reason, so a subsystem's reconnect/exit- callback decision is one function call instead of a boolean-OR of flags. See project_plans/session-lifecycle-state-machine for the motivating incidents.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AwaitBounded

func AwaitBounded(done <-chan struct{}, wait time.Duration) bool

AwaitBounded waits for done to close, returning true if it does before wait elapses and false otherwise. Generalizes the bounded-wait-then- abandon idiom needed when tearing down a generation whose goroutine may be blocked on a read Go cannot forcibly interrupt.

func EndGeneration

func EndGeneration(span trace.Span, subsystem string, reason Reason)

EndGeneration tags span with reason, ends it, increments session_lifecycle_ends_total, and decrements session_lifecycle_active_generations. Pairs with StartGeneration.

func IsBenignTimeout

func IsBenignTimeout(err error) bool

IsBenignTimeout reports whether err is an expected poll-timeout rather than a real disconnect: a net.Error with Timeout() true, os.ErrDeadlineExceeded, or io.ErrUnexpectedEOF (a partial read before the deadline fired). Ports exactly the three typed checks session/external_streamer.go used inline, deliberately without a strings.Contains fallback — a classifier that falls through to matching error text silently breaks if a wrapped library changes its message.

func RecordEnd

func RecordEnd(ctx context.Context, subsystem string, reason Reason)

RecordEnd is a lighter-weight alternative to EndGeneration for a lifecycle-ending decision that does not own a dedicated generation span (e.g. a reconnect loop's own give-up branches, running mid-generation on an already-open span). Adds a span event on ctx's current span, if any — a no-op otherwise — plus the same counter increment as EndGeneration. It does not touch the active-generations gauge: callers of RecordEnd never called StartGeneration, so there is nothing to decrement.

func RegisterMetrics

func RegisterMetrics() error

RegisterMetrics registers the session_lifecycle_* instruments against telemetry.GetMeter(). Idempotent via sync.Once — package init already calls this once; exported so a test can call it again safely.

func StartGeneration

func StartGeneration(ctx context.Context, subsystem string) (context.Context, trace.Span)

StartGeneration opens one observability span for one goroutine- generation's lifetime and increments session_lifecycle_active_generations for subsystem. Pair with EndGeneration.

Types

type Reason

type Reason int

Reason is a closed enum representing why a goroutine-lifecycle generation ended. Its zero value, ReasonUnknown, is safe by default: any code path that never explicitly classifies a reason (a forgotten assignment, or a future variant added without updating a predicate's switch) is treated as "stop, and notify" by ShouldContinue/ShouldFireExitCallback respectively, rather than silently reconnecting or silently swallowing an exit. Mirrors session/detection.DetectedStatus's iota-plus-exhaustive-linter idiom (see .golangci.yml's exhaustive carve-out for this package).

const (
	// ReasonUnknown is Reason's zero value — see the type doc comment.
	ReasonUnknown Reason = iota
	// ReasonDeliberateClose means the whole session/session-owner is ending
	// on purpose.
	ReasonDeliberateClose
	// ReasonDeliberateSupersede means this generation is being torn down
	// deliberately for a reopen, but the owning session is not closing.
	ReasonDeliberateSupersede
	// ReasonCleanExit means the remote side already reported a clean exit
	// before this generation's read/receive failed.
	ReasonCleanExit
	// ReasonTransportDrop means an unexpected, non-deliberate stream/process
	// end — the only Reason for which ShouldContinue() returns true.
	ReasonTransportDrop
	// ReasonReconnectExhausted means a reconnect loop ran out of attempts
	// without reconnecting, as opposed to being interrupted by a deliberate
	// close.
	ReasonReconnectExhausted
)

func (Reason) ShouldContinue

func (r Reason) ShouldContinue() bool

ShouldContinue reports whether the calling goroutine should attempt to reconnect/retry for this Reason. Only ReasonTransportDrop returns true; every other value — including ReasonUnknown and any unhandled future variant — returns false (fail toward stopping).

func (Reason) ShouldFireExitCallback

func (r Reason) ShouldFireExitCallback() bool

ShouldFireExitCallback reports whether a one-shot exit callback should fire for this Reason. Only ReasonDeliberateClose returns false; every other value — including ReasonUnknown and any unhandled future variant — returns true (fail toward notifying, since a silently-swallowed exit is the worse outcome for a one-shot callback contract).

func (Reason) String

func (r Reason) String() string

String returns r's snake_case label for use as a span/metric attribute value. An unhandled value returns "unknown", matching ReasonUnknown's label.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL