redisconn

package
v1.2.4 Latest Latest
Warning

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

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package redisconn is the one way a nova tool opens its Redis connection.

It is what a tool uses in place of the deprecated internal/nsprint/store's Open, and it knows nothing of sprints, cards, seats or fleets: it imports the standard library, go-redis and pkg/oneline.

The API. Options says where the store is (Addr), who logs in (User), the NAME of the variable that holds the password (PasswordEnv), and Env, the caller's names for the variables that supply whichever of the three was not given. Resolve fills and refuses without dialing. Open dials once and returns within OpenTimeout with the handshake done, or with one error of this package. Conn is the connection: Client for commands and pipelines, Close, String (what every message about it opens with), Explain (a command's error as this package's). Classify reads any error's class, Unreachable, AuthRefused or Other. CountTrips counts a program's round trips; FirstError and Exec read a pipeline's first real error. No value of this package holds the password, and no error, String or formatting verb shows it.

The names come from the caller

This package names no tool's environment variable and no deployment's. A tool passes its own names in Options.Env, or GeneralEnv (NOVA_REDIS_ADDR, NOVA_REDIS_USER, NOVA_REDIS_PASSWORD_ENV) when it has none; the zero Env reads nothing, and no older name of any tool is an alias here. A user with no password variable named is refused before the dial, and no default variable is read in its place: a caller that relied on a deployment's default password variable names that variable itself, in Options.PasswordEnv or through the variable its Env.PasswordEnv names. How a caller chooses its login (a seat, a profile, flags) is the caller's, and so are the words of its deployment in the message it prints; this package's next step on a login the store refused names the caller's variables and nothing else.

Index

Constants

View Source
const (
	// OpenTimeout bounds Open as a whole: the dial and the handshake
	// together.
	OpenTimeout = 5 * time.Second
	// DialTimeout bounds every dial a connection makes after Open: one
	// attempt, never a second.
	DialTimeout = 5 * time.Second
	// WriteTimeout bounds the write of every command and pipeline.
	WriteTimeout = 5 * time.Second
	// ReadTimeout bounds the read of every reply. A command that asks the
	// store to block (BLPOP, XREAD BLOCK) is given the time it asked for and
	// ten seconds more.
	ReadTimeout = 5 * time.Second
	// PoolTimeout bounds the wait for a free connection when every
	// connection of the client is in use.
	PoolTimeout = 5 * time.Second
)

The bounds. Nothing in this package waits without one of them, and a context's own deadline, when it comes sooner, bounds the same step.

Variables

View Source
var GeneralEnv = Env{Addr: "NOVA_REDIS_ADDR", User: "NOVA_REDIS_USER", PasswordEnv: "NOVA_REDIS_PASSWORD_ENV"}

GeneralEnv is the general set of names, for a tool that has none of its own: NOVA_REDIS_ADDR, NOVA_REDIS_USER and NOVA_REDIS_PASSWORD_ENV.

Functions

func Exec

func Exec(ctx context.Context, pipe redis.Pipeliner) error

Exec runs the pipeline and answers FirstError over what it returned: the one call a reader of values that may be absent makes in place of a bare Exec. It is one round trip. The commands keep their own results and errors for the caller to read afterwards.

func FirstError

func FirstError(cmds []redis.Cmder, execErr error) error

FirstError is the error of a pipeline, read so that no command's failure is missed. cmds and execErr are what the pipeline's Exec returned.

Exec answers the first command that failed, and an absent value counts as a failure there: a pipeline whose first command read a field that is not set (redis.Nil) and whose later command was refused answers the Nil, the caller who expects absent fields lets it pass, and the refusal is never seen. FirstError walks every command instead: the first error that is not redis.Nil is the pipeline's error; when no command carries one, execErr is, unless it is redis.Nil; otherwise nil. So nil means every command either succeeded or found nothing, and an error is a real one, the earliest in the order the commands were queued.

func TripLabel

func TripLabel(ctx context.Context) string

TripLabel is the label ctx carries, "" when it carries none.

Types

type Class

type Class int

Class is what a failure to work with the store comes down to, for a tool that answers each with its own exit code and its own next step.

const (
	// Other is everything that is neither of the two below: the store
	// answered and refused the command, the caller cancelled, the client was
	// closed. It is the zero value, and the class of a nil error.
	Other Class = iota
	// Unreachable is a store that could not be reached or did not answer in
	// time: no address, a refused or timed out dial, a name that does not
	// resolve, a connection that dropped, a reply that did not come.
	Unreachable
	// AuthRefused is a login that was not accepted: refused by the store
	// (NOAUTH, WRONGPASS), or refused by Resolve before any dial because the
	// password could not be had.
	AuthRefused
)

func Classify

func Classify(err error) Class

Classify reads any error a tool holds after working with the store, from this package or straight from go-redis, wrapped or not, and answers its class. It is total and it never reads a secret.

An error of this package keeps the class it was made with. Otherwise the store's own answer decides first: a refusal the store wrote is AuthRefused when it says NOAUTH or WRONGPASS and Other for anything else, never Unreachable, because a store that answers was reached. A cancelled context is read next, so its identity survives the transport wrappers net and go-redis put around it. Then the transport: a timeout (a deadline of the context included), a failed dial, a name that does not resolve, a dropped connection (EOF) and a wait for a connection of the pool that ran out are Unreachable. An error that kept only its text is read by the same words. Everything else, a closed client among it, is Other.

func (Class) String

func (c Class) String() string

String is the class in one lower-case word: "other", "unreachable" or "auth-refused". A value outside the three reads "other".

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is an open connection to the store: a go-redis client that was dialed, shook hands and logged in before Open returned it. It holds no field a formatting verb can print the password from.

func Open

func Open(ctx context.Context, o Options, getenv func(string) string) (*Conn, error)

Open resolves the options (Resolve: what was given first, then getenv's environment), dials the store once and returns the connection only when the store has answered the handshake and accepted the login. It returns within OpenTimeout, or sooner when ctx ends sooner.

What Open costs is the connect and one exchange, the handshake (HELLO 3, carrying the login when there is one). It sends no PING, no CLIENT SETINFO and nothing else, so a tool's first command is its first round trip. A store that refuses HELLO (one older than Redis 6, or one that wants a login and was given none) is asked a second time, by a PING sent for real, and its answer decides.

Open makes one dial attempt and no retry. After Open the same holds for every command: it is sent at most once, a connection that broke is dialed again by the next command, once, and nothing waits longer than the bounds of this package. The password is read from getenv when Open runs and at no other time.

An error is one of this package's (Classify): Unreachable, AuthRefused or Other, with one line that names what was tried and the next thing to do, and it never holds the password. On an error nothing is left open.

func (*Conn) Client

func (c *Conn) Client() *redis.Client

Client is the go-redis client, for the caller's commands and pipelines. It is the same client for the life of the connection.

func (*Conn) Close

func (c *Conn) Close() error

Close closes the connection and every socket it holds. Closing twice, and closing a nil *Conn, is nil.

func (*Conn) Explain

func (c *Conn) Explain(err error) error

Explain turns an error that a command on this connection returned into this package's error: its class (Classify), and one line that names the store and the login that were tried, what came back and the next thing to do, with the password taken out of any text that held it. nil stays nil, and an error that is already this package's comes back as it is.

func (*Conn) String

func (c *Conn) String() string

String names the store, the user and the variable the password came from, on one line: what every message of this connection opens with. It holds no secret.

type Env

type Env struct {
	// Addr names the variable that holds the store's address: host:port, or
	// the absolute path of a Unix socket.
	Addr string
	// User names the variable that holds the ACL user; unset is the default
	// user.
	User string
	// PasswordEnv names the variable that holds the NAME of the variable
	// that holds the password, never the password.
	PasswordEnv string
}

Env names the environment variables Resolve reads for the fields of an Options that were left empty: one variable per field, each read through the getenv Resolve is handed and never from the process on its own. A name that is empty is not read, so the zero Env reads nothing: what was given is all there is. The names are the caller's. This package has no name of its own for any tool's variable and keeps no older name as an alias: a tool that reads its own names passes them, and a tool with none passes GeneralEnv.

type Options

type Options struct {
	// Addr is host:port (a name or an IP, a port from 1 to 65535) or the
	// absolute path of a Unix socket.
	Addr string
	// User is the ACL user; empty is the store's default user.
	User string
	// PasswordEnv is the NAME of the environment variable that holds the
	// password; empty is a login with no password.
	PasswordEnv string
	// Env names the variables Resolve reads for whichever of the three above
	// is empty. The zero Env reads nothing.
	Env Env
}

Options says where the store is and who logs in. It holds no secret and cannot: the password is named by the variable that holds it, so printing an Options, by any verb, prints nothing that must be kept.

func Resolve

func Resolve(explicit Options, getenv func(string) string) (Options, error)

Resolve fills the options a tool was given from the environment and refuses the ones that cannot make a connection. It dials nothing.

Each field is resolved on its own: the value in explicit when it is not empty, else the variable explicit.Env names for it, when it names one. getenv is the only environment Resolve reads; nil is an empty environment, which is how a caller says "only what I gave you", as is the zero Env.

What comes back is ready for Open, and resolving it again changes nothing. The refusals, each an error of this package with its class (Classify), one line that names what was tried and the next thing to do:

  • no address anywhere, or an address that is neither host:port nor an absolute path (Unreachable). An address that is a URL or carries a login is refused without being shown.
  • a user with no password variable named (AuthRefused): the login is never made as the default user instead, and no default variable is read in its place.
  • a password variable whose name is not capital letters, digits and underscores (AuthRefused). The name is not shown, in case the password was put where its variable's name belongs.
  • a password variable that is named and empty (AuthRefused), whoever the user is.

No user and no password variable is the default user with no password, so a throwaway store needs nothing but its address.

func (Options) String

func (o Options) String() string

String renders the options as three key=value fields on one line, each value a single token (oneline.Field), whatever the values hold.

type Trips

type Trips struct {
	// contains filtered or unexported fields
}

Trips counts the round trips a client makes to the store from the moment CountTrips attaches it, and this is what it counts: each single command is one, and each pipeline or transaction is one however many commands it carries. A connection's own setup is not counted: the dial, and the handshake (HELLO, which carries the login) that go-redis runs inside the first command to use a new connection. A command that fails is counted, because it was sent.

A tool prints the count on its receipt, or a test pins it, so that a change which turns one batch into a chain of reads shows as a number. Every method is safe to call from many goroutines, and on a nil *Trips, which has counted nothing.

func CountTrips

func CountTrips(client *redis.Client) *Trips

CountTrips attaches a new counter to the client and returns it. The count starts at zero and covers every goroutine that uses the client; the span of work a caller asks about is the difference of two readings of N (or of Of, for one label). More than one counter may be attached to a client; each counts on its own.

func (*Trips) ByLabel

func (t *Trips) ByLabel() map[string]int64

ByLabel is every label's count, in a map of the caller's own: changing it changes nothing here. Trips made under no label are in N only.

func (*Trips) DialHook

func (t *Trips) DialHook(next redis.DialHook) redis.DialHook

DialHook is the go-redis hook for a dial. It passes the dial through: a dial is not a round trip.

func (*Trips) N

func (t *Trips) N() int64

N is the number of round trips counted so far.

func (*Trips) Of

func (t *Trips) Of(label string) int64

Of is the number of round trips counted under label so far; zero for a label nothing was counted under.

func (*Trips) ProcessHook

func (t *Trips) ProcessHook(next redis.ProcessHook) redis.ProcessHook

ProcessHook is the go-redis hook for a single command. It counts one round trip for a command the caller sent and none for a connection's setup.

func (*Trips) ProcessPipelineHook

func (t *Trips) ProcessPipelineHook(next redis.ProcessPipelineHook) redis.ProcessPipelineHook

ProcessPipelineHook is the go-redis hook for a pipeline and for a transaction. It counts one round trip for the whole of it.

Jump to

Keyboard shortcuts

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