Documentation
¶
Overview ¶
Package reconnect implements a generic retrying network client.
Index ¶
- Constants
- Variables
- func IsClosed(err error) bool
- func IsFatal(err error) bool
- func IsNonError(err error) bool
- func IsNotConnected(err error) bool
- func NewConstantWaiter(d time.Duration) func(context.Context) error
- func NewDoNotReconnectWaiter(err error) func(context.Context) error
- func NewImmediateErrorWaiter(err error) func(context.Context) error
- func ParseRemote(remote string) (network, address string, err error)
- func TimeoutToAbsoluteTime(base time.Time, d time.Duration) time.Time
- func ValidateRemote(remote string) error
- type CatcherFunc
- type Client
- func (c *Client) Close() error
- func (c *Client) Config() *Config
- func (c *Client) Connect() error
- func (c *Client) Done() <-chan struct{}
- func (c *Client) Err() error
- func (c *Client) Go(funcs ...WorkerFunc)
- func (c *Client) GoCatch(run WorkerFunc, catch CatcherFunc)
- func (c *Client) LocalAddr() net.Addr
- func (c *Client) Read(p []byte) (int, error)
- func (c *Client) Reload() error
- func (c *Client) RemoteAddr() net.Addr
- func (c *Client) ResetDeadline() error
- func (c *Client) ResetReadDeadline() error
- func (c *Client) ResetWriteDeadline() error
- func (c *Client) SetDeadline(read, write time.Duration) error
- func (c *Client) SetReadDeadline(d time.Duration) error
- func (c *Client) SetWriteDeadline(d time.Duration) error
- func (c *Client) Shutdown(ctx context.Context) error
- func (c *Client) Wait() error
- func (c *Client) WithDebug(addr net.Addr) (slog.Logger, bool)
- func (c *Client) WithError(addr net.Addr, err error) (slog.Logger, bool)
- func (c *Client) WithInfo(addr net.Addr) (slog.Logger, bool)
- func (c *Client) Write(p []byte) (int, error)
- type Config
- type OptionFunc
- type Shutdowner
- type StreamSession
- func (s *StreamSession[_, _]) Close() error
- func (s *StreamSession[_, _]) Done() <-chan struct{}
- func (s *StreamSession[Input, Output]) Err() error
- func (s *StreamSession[_, _]) Go(funcs ...WorkerFunc)
- func (s *StreamSession[_, _]) GoCatch(run WorkerFunc, catch CatcherFunc)
- func (s *StreamSession[Input, _]) Next() (Input, bool)
- func (s *StreamSession[Input, _]) Recv() <-chan Input
- func (s *StreamSession[_, Output]) Send(m Output) error
- func (s *StreamSession[_, _]) Shutdown(ctx context.Context) error
- func (s *StreamSession[_, _]) Spawn() error
- func (s *StreamSession[_, _]) Wait() error
- type Waiter
- type WorkGroup
- type WorkerFunc
Constants ¶
const ( // LogFieldAddress is the field name used to store the address // when logging. LogFieldAddress = "addr" // LogFieldError is the field name used to store the error // when logging. LogFieldError = slog.ErrorFieldName )
const ( // NetworkTCP represents TCP network type NetworkTCP = "tcp" // NetworkUnix represents Unix domain socket network type NetworkUnix = "unix" // MaxUNIXSocketPathLength is the maximum length for UNIX domain socket paths // Limited by sockaddr_un.sun_path (108 bytes including null terminator on Linux) MaxUNIXSocketPathLength = 107 )
const ( // DefaultWaitReconnect specifies how long [NewConstantWaiter] // waits between reconnection attempts by default. DefaultWaitReconnect = 5 * time.Second )
Variables ¶
var ( // ErrAbnormalConnect indicates the dialer didn't return error // nor connection. ErrAbnormalConnect = core.QuietWrap(syscall.ECONNABORTED, "abnormal response") // ErrDoNotReconnect indicates the Waiter // instructed us to not reconnect ErrDoNotReconnect = errors.New("don't reconnect") // ErrNotConnected indicates the [Client] isn't currently connected. // It wraps [ErrClosed] so a single errors.Is target covers both a // closed client and the not-connected window. ErrNotConnected = core.QuietWrap(ErrClosed, "client not connected") // ErrRunning indicates the [Client] has already been started. ErrRunning = core.QuietWrap(syscall.EBUSY, "client already running") // ErrClosed indicates the [Client] or [StreamSession] has // already been shut down. It wraps the workgroup's sentinel so // the shutdown signal still matches the one the group returns // across the lifecycle stack. ErrClosed = core.QuietWrap(errors.ErrClosed, "already closed") // ErrNameEmpty indicates a name is empty. It wraps [core.ErrInvalid] // so a caller matching the invalid-argument family with errors.Is // catches it. ErrNameEmpty = core.QuietWrap(core.ErrInvalid, "name missing") // ErrNameTooLong indicates a name exceeds maximum length. It wraps // [core.ErrInvalid] for the same reason. ErrNameTooLong = core.QuietWrap(core.ErrInvalid, "name too long") )
var ( // ErrConfigBusy indicates the [Config] is in use and can't // be used to create another [Client]. ErrConfigBusy = core.QuietWrap(fs.ErrPermission, "config already in use") )
Functions ¶
func IsClosed ¶ added in v0.8.1
IsClosed reports whether err indicates the Client has been shut down, matching ErrClosed anywhere in the chain. Because ErrNotConnected wraps ErrClosed, IsClosed is the broad companion to IsNotConnected: it is true for both a fully closed client and the transient not-connected window, whereas IsNotConnected matches only the latter.
func IsFatal ¶
IsFatal tells if the error means the connection should be closed and not retried. Only ErrDoNotReconnect, possibly wrapped, is considered fatal; anything else is treated as recoverable.
IsFatal classifies connection errors seen inside the reconnect loop. Caller-misuse errors are reported at setup time and never reach that decision; they extend core.ErrInvalid, so match them with errors.Is against that sentinel instead.
func IsNonError ¶
IsNonError reports whether the error represents a user-initiated shutdown instead of an actual failure.
func IsNotConnected ¶ added in v0.8.1
IsNotConnected reports whether err indicates the Client had no session when a request was attempted, matching ErrNotConnected anywhere in the chain. A fully shut-down client surfaces ErrClosed without ErrNotConnected wrapping it; match ErrClosed instead to cover both the closed and not-connected cases with a single target.
func NewConstantWaiter ¶
NewConstantWaiter blocks for a given amount of time, or until the context is cancelled. If the given duration is zero, DefaultWaitReconnect is used. If negative, reconnecting is disabled, failing with ErrDoNotReconnect.
func NewDoNotReconnectWaiter ¶
NewDoNotReconnectWaiter returns a Waiter that stops reconnection attempts, failing with the given error, or ErrDoNotReconnect when nil. The context's error takes precedence if the context has already terminated.
func NewImmediateErrorWaiter ¶
NewImmediateErrorWaiter returns a Waiter that doesn't wait. It returns the context's error if the context has already terminated, or the given error otherwise. A nil error allows an immediate reconnection attempt.
func ParseRemote ¶ added in v0.6.1
ParseRemote determines the network type and address from a remote string. It supports: - "unix:/path/to/socket" - explicit Unix socket. - "/path/to/socket" - Unix socket (absolute path). - "@abstract-name" - abstract Unix socket. - "path/to/file.sock" - Unix socket (ends with .sock). - "host:port" - TCP socket.
func TimeoutToAbsoluteTime ¶
TimeoutToAbsoluteTime adds the given time.Duration to a base time.Time. If the duration is negative, a zero time.Time will be returned. If the base is zero, the current time will be used.
func ValidateRemote ¶ added in v0.6.1
ValidateRemote validates a remote address for use with reconnect clients. It returns nil if the address is valid for either TCP or Unix socket connection. For TCP addresses, it validates host:port format. For Unix socket addresses, it accepts the address as-is.
Types ¶
type CatcherFunc ¶ added in v0.3.0
CatcherFunc is a catch function for WorkGroup.GoCatch.
func NewCatchFunc ¶ added in v0.3.0
func NewCatchFunc(nonErrors ...error) CatcherFunc
NewCatchFunc creates a CatcherFunc turning any of the given errors into nil.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a reconnecting network client.
func Must ¶
func Must(cfg *Config, options ...OptionFunc) *Client
Must is like New but it panics on errors.
func New ¶
func New(cfg *Config, options ...OptionFunc) (*Client, error)
New creates a new Client using the given Config and options.
func (*Client) Close ¶
Close terminates the current connection, if any. The Client keeps running and will reconnect; use Client.Shutdown to stop it.
func (*Client) Config ¶
Config returns the Config object used when Client.Reload is called.
func (*Client) Connect ¶
Connect launches the Client, failing with ErrRunning when called more than once. A nil return means the reconnection loop has started, not that a connection is established — a failed first dial is retried in the background like any other disconnection. Calling it on a Client that has already been shut down fails with ErrClosed.
func (*Client) Done ¶
func (c *Client) Done() <-chan struct{}
Done returns a channel that is closed once the Client workers have finished. Use Client.Err to learn the cancellation reason.
func (*Client) Err ¶
Err returns the cancellation reason. It will return nil if the cause was initiated by the user.
func (*Client) Go ¶
func (c *Client) Go(funcs ...WorkerFunc)
Go spawns a goroutine within the Client's context. Submissions after shutdown are no-ops: the worker is dropped rather than run with an already-cancelled context.
func (*Client) GoCatch ¶ added in v0.3.0
func (c *Client) GoCatch(run WorkerFunc, catch CatcherFunc)
GoCatch spawns a goroutine within the Client's context, optionally allowing filtering the error to stop cascading. Submissions after shutdown are no-ops, as with Client.Go.
func (*Client) Reload ¶
Reload attempts to apply changes done to the Config since the last time, or since created.
func (*Client) RemoteAddr ¶ added in v0.2.4
RemoteAddr returns the remote address if connected.
func (*Client) ResetDeadline ¶
ResetDeadline sets the connection's read and write deadlines using the default values.
func (*Client) ResetReadDeadline ¶
ResetReadDeadline resets the connection's read deadline using the default duration.
func (*Client) ResetWriteDeadline ¶
ResetWriteDeadline resets the connection's write deadline using the default duration.
func (*Client) SetDeadline ¶
SetDeadline sets the connection's read and write deadlines. If write is zero but read is positive, write is set using the same value as read. Zero or negative can be used to disable the deadline.
func (*Client) SetReadDeadline ¶ added in v0.2.3
SetReadDeadline sets the connection's read deadline to the specified duration. Use zero or negative to disable it.
func (*Client) SetWriteDeadline ¶ added in v0.2.3
SetWriteDeadline sets the connection's write deadline to the specified duration. Use zero or negative to disable it.
func (*Client) Shutdown ¶
Shutdown initiates a shutdown and waits until the workers are done, or the given context times out.
func (*Client) Wait ¶
Wait blocks until the Client workers have finished, and returns the cancellation reason, nil if the shutdown was user-initiated.
func (*Client) WithDebug ¶ added in v0.2.4
WithDebug gets a logger at Debug level optionally annotated by an IP address. If the Debug log-level is disabled, it will return `nil, false`.
func (*Client) WithError ¶
WithError gets a logger at Error level optionally annotated by an IP address. If the Error log-level is disabled, it will return `nil, false`.
type Config ¶
type Config struct {
Context context.Context
Logger slog.Logger
// WaitReconnect is a helper used to wait between re-connection attempts.
WaitReconnect Waiter
// OnSocket is called, when defined, against the raw socket before attempting to
// connect
OnSocket func(context.Context, syscall.RawConn) error
// OnConnect is called, when defined, immediately after the connection is established
// but before the session is created.
OnConnect func(context.Context, net.Conn) error
// OnSession, when defined, owns the connection and is expected
// to block until the session is done. Returning nil or a
// non-fatal error leads to a reconnection attempt; return
// [ErrDoNotReconnect], possibly wrapped, to stop the [Client].
OnSession func(context.Context) error
// OnDisconnect is called after closing the connection and can be used to
// prevent further connection retries by returning [ErrDoNotReconnect].
OnDisconnect func(context.Context, net.Conn) error
// OnError is called after all errors, and its return value replaces
// the error for the reconnection logic. Return nil to discard the
// error, or [ErrDoNotReconnect] to stop the [Client].
OnError func(context.Context, net.Conn, error) error
// Remote indicates the remote endpoint: TCP "host:port" or Unix socket path,
// e.g., "/path/to/socket" or "unix:/path".
Remote string
// KeepAlive indicates the value to be set to TCP connections
// for the low-level keep-alive messages.
KeepAlive time.Duration `default:"5s"`
// DialTimeout indicates how long are we willing to wait for new
// connections getting established.
DialTimeout time.Duration `default:"2s"`
// ReadTimeout is the default read deadline for the connection,
// applied via [Client.ResetReadDeadline] and [Client.ResetDeadline].
// It is not set automatically on new connections.
// Zero or negative disables the deadline.
ReadTimeout time.Duration `default:"2s"`
// WriteTimeout is the default write deadline for the connection,
// applied via [Client.ResetWriteDeadline] and [Client.ResetDeadline].
// It is not set automatically on new connections.
// Zero or negative disables the deadline, except [Client.ResetDeadline]
// substitutes ReadTimeout when WriteTimeout is zero (see [Client.SetDeadline]).
WriteTimeout time.Duration `default:"2s"`
// ReconnectDelay specifies how long to wait between re-connections
// unless [WaitReconnect] is specified. Zero means
// [DefaultWaitReconnect], and negative implies reconnecting
// is disabled.
ReconnectDelay time.Duration
// contains filtered or unexported fields
}
Config describes the operation of the Client.
func (*Config) ExportDialer ¶
ExportDialer creates a net.Dialer from the Config.
func (*Config) SetDefaults ¶
SetDefaults fills any gap in the config.
type OptionFunc ¶
An OptionFunc modifies a Config before Config.SetDefaults and Config.Valid run.
type Shutdowner ¶ added in v0.3.0
A Shutdowner is an object that provides a Shutdown method that takes a context with deadline to shut down all associated workers.
type StreamSession ¶ added in v0.2.2
type StreamSession[Input, Output any] struct { // Conn specifies the underlying connection Conn io.ReadWriteCloser // Context is an optional [context.Context] to allow cascading cancellations. Context context.Context // Split identifies the next encoded [Input] type in the inbound stream. // If not set, [bufio.ScanLines] will be used. Split bufio.SplitFunc // Marshal is used, if MarshalTo isn't set, to encode an [Output] type. // If neither is set, [StreamSession.Spawn] will fail. Marshal func(Output) ([]byte, error) // MarshalTo, if set, is used to write the encoded representation of // an [Output] type. MarshalTo func(Output, io.Writer) error // Unmarshal is used to decode an [Input] type previously identified // by [StreamSession.Split]. // If not set, [StreamSession.Spawn] will fail. Unmarshal func([]byte) (Input, error) // SetReadDeadline is an optional hook called before reading a message SetReadDeadline func() error // SetWriteDeadline is an optional hook called before writing a message SetWriteDeadline func() error // UnsetReadDeadline is an optional hook called after having read a message UnsetReadDeadline func() error // UnsetWriteDeadline is an optional hook called after having written a message UnsetWriteDeadline func() error // OnError is optionally called when an error occurs OnError func(error) // QueueSize specifies how many [Output] type entries can be buffered // for delivery before [StreamSession.Send] blocks. QueueSize uint // contains filtered or unexported fields }
StreamSession provides an asynchronous stream session using message types for receiving and sending. Exported fields are configured before calling StreamSession.Spawn and must not be modified afterwards. The session must be spawned before using any other method.
func (*StreamSession[_, _]) Close ¶ added in v0.2.2
func (s *StreamSession[_, _]) Close() error
Close initiates a shutdown of the session.
func (*StreamSession[_, _]) Done ¶ added in v0.2.2
func (s *StreamSession[_, _]) Done() <-chan struct{}
Done returns a channel that will be closed when all workers are done.
func (*StreamSession[Input, Output]) Err ¶ added in v0.3.0
func (s *StreamSession[Input, Output]) Err() error
Err returns the error that initiated a shutdown.
func (*StreamSession[_, _]) Go ¶ added in v0.2.4
func (s *StreamSession[_, _]) Go(funcs ...WorkerFunc)
Go spawns a goroutine within the session's context.
func (*StreamSession[_, _]) GoCatch ¶ added in v0.3.0
func (s *StreamSession[_, _]) GoCatch(run WorkerFunc, catch CatcherFunc)
GoCatch spawns a goroutine within the session's context, and allows a catcher function to filter returned errors.
func (*StreamSession[Input, _]) Next ¶ added in v0.2.2
func (s *StreamSession[Input, _]) Next() (Input, bool)
Next blocks until a new message is received, returning false once the inbound stream has ended.
func (*StreamSession[Input, _]) Recv ¶ added in v0.2.2
func (s *StreamSession[Input, _]) Recv() <-chan Input
Recv returns the channel where inbound messages are delivered. The channel is closed when the inbound stream ends.
func (*StreamSession[_, Output]) Send ¶ added in v0.2.2
func (s *StreamSession[_, Output]) Send(m Output) error
Send queues a message for asynchronous delivery, blocking while the queue is full. It fails with ErrClosed once the session has been shut down.
func (*StreamSession[_, _]) Shutdown ¶ added in v0.3.0
func (s *StreamSession[_, _]) Shutdown(ctx context.Context) error
Shutdown initiates a shutdown and waits until it's done or the given context has expired.
func (*StreamSession[_, _]) Spawn ¶ added in v0.2.2
func (s *StreamSession[_, _]) Spawn() error
Spawn starts the StreamSession's workers. It fails if the session has already been started, or if Conn, Unmarshal, or a marshalling function is missing.
func (*StreamSession[_, _]) Wait ¶ added in v0.2.2
func (s *StreamSession[_, _]) Wait() error
Wait blocks until all workers are done.
type Waiter ¶
A Waiter is a function that blocks between reconnection attempts. It returns nil when the Client is good to try again, or an error to stop reconnecting.
type WorkGroup ¶ added in v0.3.0
type WorkGroup interface {
Go(...WorkerFunc)
GoCatch(WorkerFunc, CatcherFunc)
Shutdown(context.Context) error
Wait() error
Done() <-chan struct{}
Err() error
}
A WorkGroup is an error group interface. Submissions through Go and GoCatch after shutdown are no-ops.
type WorkerFunc ¶ added in v0.3.0
WorkerFunc is a run function for WorkGroup.GoCatch.
func NewShutdownFunc ¶ added in v0.3.0
func NewShutdownFunc(s Shutdowner, tio time.Duration) WorkerFunc
NewShutdownFunc creates a shutdown WorkerFunc, optionally with a deadline.