cf_valkey

package module
v0.0.10 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

README

caerus-framework-valkey

CI codecov License

Caerus Framework Valkey Component — the client & ops chassis for Valkey / Redis. Wraps a valkey-go client and owns lifecycle, health, reload, key prefix, named instances, TLS, timeouts, and metrics.

Not a Redis ORM. There are no struct tags, no HASH repositories, and no replacement for valkey-go's command API. Client() remains first-class. For common patterns (distributed lock, JSON helpers, singleflight cache-aside), see the patterns subpackage. Rate-limit Lua (and the sticky-note counter map) live on caerus-framework-valkey-state; HTTP 429 / middleware live on caerus-framework-http-ratelimiter.

Registers in the data initialization stage.

Wiring

Two wiring shapes. Prefer the app-owned shape.

App-owned consumer (golden — demoapp pattern)

main declares valkey as chassis. The app holds *CFValkey and calls Client() / Key() per use (never copy the client at Init).

fw := cf.New(&cf.FrameworkOptions{
	Logs:          &cf.LogsSettings{Format: "json", Level: "info", ConfigSource: "logs"},
	Observability: &cf.ObservabilitySettings{Bind: ":9090", ConfigSource: "observability"},
	Components: []cf.CaerusComponent{
		cf_valkey.New(cf_valkey.WithConfigSource("valkey", "config/valkey.json")),
		app.New(),
	},
})
type App struct {
	vk *cf_valkey.CFValkey
}

func (a *App) GetDependencies() []string {
	return []string{cf_valkey.ComponentName}
}

func (a *App) Init(ctx context.Context, fw *cf.CaerusFramework) error {
	vk, ok := cf.Get[*cf_valkey.CFValkey](fw)
	if !ok {
		return errors.New("app: valkey missing")
	}
	a.vk = vk
	return nil
}

func (a *App) set(ctx context.Context, k, v string) error {
	cl := a.vk.Client() // live client after reconnect / reload
	return cl.Do(ctx, cl.B().Set().Key(a.vk.Key(k)).Value(v).Build()).Error()
}

Wrong: a.client = vk.Client() once at Init (dead after reload). Right: store *CFValkey; Client() per command.

Simple main-level wiring
fw := cf.New()
fw.AddComponent(cf_logs.New(cf_logs.WithWriter(os.Stdout)))
fw.AddComponent(cf_valkey.New(cf_valkey.WithAddress("127.0.0.1:6379")))

Then cf.MustGet[*cf_valkey.CFValkey](fw) in that binary. Still call Client() per use, not a snapshot.

Usage

After fw.Run (or in any component whose stage runs after data), get the client and issue commands through the valkey-go builder API:

client := cf.MustGet[*cf_valkey.CFValkey](fw).Client()

err := client.Do(ctx, client.B().Set().Key("k").Value("v").Build()).Error()
got, err := client.Do(ctx, client.B().Get().Key("k").Build()).ToString()

Commands are auto-pipelined by valkey-go for throughput. The client supports RESP3, client-side caching (DoCache), pub/sub (Receive), blocking commands and cluster/sentinel topologies.

Key prefixing

Give a service a shared key namespace with WithKeyPrefix — every key it reads or writes is scoped automatically, so several services can share one Valkey without collisions. Use Key(...) to build keys through the same prefixing:

valkey := cf_valkey.New(
	cf_valkey.WithAddress("127.0.0.1:6379"),
	cf_valkey.WithKeyPrefix("auth"),
)

err := valkey.Client().Do(ctx, client.B().Set().Key(valkey.Key("session", "abc")).Value("1").Build()).Error()
// writes "auth:session:abc"

Key("a", "b") joins the parts with : and prepends the prefix ("auth:session:abc"). An empty prefix collapses to a plain :-join, so code written against Key runs unchanged with or without a prefix. KeyPrefix() returns the configured prefix, and Key() is safe to call before Init (it never touches the server).

Options

Option Description
WithConfig(ValkeyConfig) connection config loaded from the configuration component; non-zero fields override option-set defaults
WithClientOption(valkey.ClientOption) full valkey-go client option; call before convenience setters you want overridden
WithAddress(addr) single server address (default 127.0.0.1:6379)
WithAddresses(addrs...) multiple addresses (cluster/sentinel)
WithUsername(u) / WithPassword(p) AUTH credentials
WithDB(n) logical database selection
WithClientName(name) CLIENT SETNAME on connections
WithKeyPrefix(prefix) scope all keys (see above); trailing : trimmed
WithPingTimeout(d) Init connectivity-ping timeout (default 5s)
WithDegradedMode(bool) when true, Init may succeed without a live ping (default off / hard-fail)
WithHealthWhenDegraded("not_ready"|"ready") /readyz while degraded: default not_ready; ready is break-glass LB traffic
WithName(name) custom component name for multiple instances (default "valkey")
WithLogger(*slog.Logger) explicit logger override; defaults to the framework logs component's logger (re-delivered on logs Reconfigure), falling back to slog.Default()
WithTLS(caFile, certFile, keyFile) TLS from PEM file paths (Kubernetes-mounted secrets); CA for server verification, cert+key for mTLS
WithTLSInsecureSkipVerify(true) skip cert verify (lab only; not implied by rediss://)
WithDialTimeout(d) TCP dial timeout
WithConnWriteTimeout(d) per-connection read/write timeout; bounds pipeline waits and triggers periodic PINGs
WithConnLifetime(d) maximum connection lifetime; zero means no limit

Multiple instances (cookbook)

Use WithName to run multiple valkey clients in the same process (e.g., cache, sessions, rate-limit). Each gets its own key prefix, health check, and metrics labels:

cache := cf_valkey.New(
    cf_valkey.WithName("cache"),
    cf_valkey.WithAddress("valkey:6379"),
    cf_valkey.WithDB(0),
    cf_valkey.WithKeyPrefix("app:cache"),
)
sessions := cf_valkey.New(
    cf_valkey.WithName("sessions"),
    cf_valkey.WithAddress("valkey:6379"),
    cf_valkey.WithDB(1),
    cf_valkey.WithKeyPrefix("app:sessions"),
)

fw.AddComponent(cache)
fw.AddComponent(sessions)

// Retrieve by name
cacheClient := cf.MustGetByName[*cf_valkey.CFValkey](fw, "cache").Client()
sessionsClient := cf.MustGetByName[*cf_valkey.CFValkey](fw, "sessions").Client()

When multiple instances exist, cf.Get[*cf_valkey.CFValkey](fw) returns false to prevent ambiguous lookups. Always use GetByName for named instances. Each instance's metrics carry a component label (e.g. valkey_info{component="cache"}).

Configuration

Drive connection settings via caerus-framework-configuration (file → env → URL). ValkeyConfig has json/yaml/env tags.

The module is self-sufficient: WithConfigSource(name, path) registers its own Source[ValkeyConfig] with the configuration component (via cf.ConfigSourceRegistrar, run by the framework during argv absorption). The default EnvPrefix is the uppercase source name ("valkey""VALKEY_"). VALKEY_URL is overlaid in AfterLoad inside the module. main only points the instance at where config lives:

valkey := cf_valkey.New(
	cf_valkey.WithConfigSource("valkey", "config.yaml"), // Init + OnConfigReload reconnect
)

For low-level control (custom AfterLoad, format, env prefix), register the source manually instead:

conf := cf_configuration.New()
_ = fw.AddComponent(conf)
_ = cf_configuration.AddSource(conf, cf_configuration.Source[cf_valkey.ValkeyConfig]{
	Name:      "valkey",
	Path:      "config.yaml", // optional if EnvPrefix set
	Format:    cf_configuration.FormatYAML,
	Owner:     cf_valkey.ComponentName,
	EnvPrefix: "VALKEY_",
	AfterLoad: func(c *cf_valkey.ValkeyConfig) error {
		if u := os.Getenv("VALKEY_URL"); u != "" {
			return cf_valkey.OverlayURL(c, u) // wins over file+env fields
		}
		return nil
	},
})

valkey := cf_valkey.New(
	cf_valkey.WithConfigSource("valkey", ""), // bind by name only
)

Helpers: ParseURL / OverlayURL for redis://, valkey://, or host:port. ParseURL errors never include the raw URL (userinfo). Password is tagged secret:"redact"; connect/reload logs use password_set only. WithConfigSource implements ConfigReloader: on file reload (or cfg.Reload), builds a new client, pings, swaps, closes the old client; on failure keeps the previous client. In Kubernetes prefer file-mounted secrets for rotation; use env/URL for local and CI.

Fail-fast behaviour (default)

Init creates the client and pings the server. If the connection is refused or the ping times out, Init returns an error and startup aborts before any dependent component runs. Client() returns nil before Init or after Shutdown.

DegradedMode (optional break-glass)

Not automatic. Default remains hard Init. Set degraded_mode: true (or WithDegradedMode(true)) when the process must finish Initialize even if Valkey is unreachable (e.g. a rate limiter that can run on sticky notes with force_memory).

{
  "addr": "valkey:6379",
  "degraded_mode": true,
  "health_when_degraded": "not_ready"
}
Setting Meaning
degraded_mode Init may succeed without a successful ping; logs/metrics scream (degraded_unreachable, degraded_mode_uses_total). Default off.
health_when_degraded: "not_ready" Default — Health still fails → /readyz 503 while disconnected.
health_when_degraded: "ready" Break-glass — Health returns nil while down so LB may send traffic. Prefer a dedicated Valkey instance when the same pod also has a hard session/DB dependency.

DegradedMode answers “may Initialize finish?” — it does not mean the store is healthy. Hot reload of the valkey source can reconnect later when the file updates; env alone does not wake a running process.

Observability

CFValkey implements cf.HealthProvider: Health(ctx) pings the server, so the observability component's /readyz endpoint reflects real connectivity. Before Init or after Shutdown (nil client) it reports unhealthy. After DegradedMode without a live ping, behaviour follows health_when_degraded.

It also implements cf_observability.MetricsProvider: while connected it contributes samples to /metrics:

Sample Type Labels
valkey_info gauge addresses, db, component
valkey_degraded_unreachable gauge degraded_mode, health_when_degraded, …
valkey_degraded_mode_uses_total counter same
valkey_ping_failures_total counter same
valkey_reconnects_total counter same
valkey_lock_acquire_ok_total counter same
valkey_lock_acquire_busy_total counter same
valkey_lock_unlock_ok_total counter same
valkey_lock_unlock_mismatch_total counter same

The valkey_lock_* counters aggregate distributed-lock traffic from patterns.Mutex across the component instance (per-lock breakdown is out of scope to keep the lock helpers dependency-free and cardinality bounded).

Before Init or after Shutdown it reports nothing (lazy pickup). While connected, the ping, reconnect, and lock counters are always emitted (zero until first fire), so the series stay present on /metrics. Counter samples use cf_observability.MetricTypeCounter and are scraped as Prometheus counters (not gauges). The metrics contract lives in caerus-framework-observability, not core.

Command hooks (tracing)

CFValkey exposes a generic command-hook seam so you can attach tracing, slow-command logging, or command counters to every Client().Do / DoMulti call — without importing an instrumentation library into this module. Register hooks with WithCommandHook; they run in order around each command, each calling next to continue the chain:

type CommandHook interface {
    Do(ctx context.Context, cmd valkey.Completed,
        next func(context.Context, valkey.Completed) valkey.ValkeyResult) valkey.ValkeyResult
    DoMulti(ctx context.Context, cmds []valkey.Completed,
        next func(context.Context, []valkey.Completed) []valkey.ValkeyResult) []valkey.ValkeyResult
}

An OpenTelemetry example (the otel import lives in your app, not here):

import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/codes"
)

type spanHook struct{ tracer string }

func (h spanHook) Do(ctx context.Context, cmd valkey.Completed,
    next func(context.Context, valkey.Completed) valkey.ValkeyResult) valkey.ValkeyResult {
    ctx, span := otel.Tracer(h.tracer).Start(ctx, "valkey:"+cmd.Commands()[0])
    defer span.End()
    resp := next(ctx, cmd)
    if err := resp.Error(); err != nil {
        span.SetStatus(codes.Error, err.Error())
    }
    return resp
}
func (h spanHook) DoMulti(ctx context.Context, cmds []valkey.Completed,
    next func(context.Context, []valkey.Completed) []valkey.ValkeyResult) []valkey.ValkeyResult {
    return next(ctx, cmds)
}

v := cf_valkey.New(cf_valkey.WithAddress("valkey:6379"),
    cf_valkey.WithCommandHook(spanHook{tracer: "valkey"}))

cmd.Commands() returns the command words (e.g. GET, key). The hook sees lock/JSON/GetOrLoad traffic from patterns too, since they all go through Client().Do. DoCache / DoStream / Dedicated / Nodes bypass the hook (advanced paths).

Patterns

The patterns subpackage (github.com/caerus-framework/caerus-framework-valkey/patterns) provides small, optional, prefix-aware helpers for common Valkey usage. It is not a Redis ORM — Client() remains first-class. Apps that only need a client never import it.

Distributed lock (Mutex)
import "github.com/caerus-framework/caerus-framework-valkey/patterns"

m := patterns.NewMutex(vk, "reconcile", 30*time.Second)
err := m.WithLock(ctx, func(ctx context.Context) error {
    // only one holder at a time (per Valkey instance)
    return doReconcile(ctx)
})

TryLock / Unlock are also available. TTL is mandatory; token-based unlock (Lua) ensures you never delete another holder's lock. Not Redlock — see godoc for failure modes. Lock traffic is counted and exposed on /metrics (valkey_lock_acquire_ok_total, valkey_lock_acquire_busy_total, valkey_lock_unlock_ok_total, valkey_lock_unlock_mismatch_total), so contention and lost unlocks are observable without ad-hoc instrumentation.

JSON helpers
var user User
err := patterns.GetJSON(ctx, vk, &user, "user", id)
err = patterns.SetJSON(ctx, vk, user, 5*time.Minute, "user", id)
Singleflight GetOrLoad
import "golang.org/x/sync/singleflight"

g := &singleflight.Group{}
val, shared, err := patterns.GetOrLoad(ctx, vk, g, "price:"+sku, time.Minute,
    func(ctx context.Context) ([]byte, error) {
        return fetchPrice(ctx, sku)
    },
)

Coalesces concurrent loads within this process. Other pods still stampede; compose with Mutex for cross-pod coalescing.

TLS

Two different things:

What it means
URL scheme rediss:// / valkeys:// This URL wants TLS. The client gets TLSConfig (MinVersion 1.2, system roots) even with no PEM files. redis:// / valkey:// do not enable TLS by themselves.
PEM files Custom CA and/or mTLS. Files win for trust material. Re-read on reload / reconnect.

Client cert and key are a pair (same rule as postgresql): both set, both empty, or the overlay is rejected and last-good stays. Do not set only tls_cert_file or only tls_key_file (env or JSON). CA (tls_ca_file) rotates on its own.

Kubernetes layout (cert-manager / kubernetes.io/tls): mount ca.crt, tls.crt, and tls.key; point the three settings at those paths. A Secret update that replaces the files is picked up on the next reload.

tls_insecure_skip_verify is a named switch for broken lab certs. It is not implied by rediss://.

vk := cf_valkey.New(
    cf_valkey.WithAddress("valkey:6380"),
    cf_valkey.WithTLS("/certs/ca.pem", "/certs/client.pem", "/certs/client-key.pem"),
)

Or tls: true in JSON, or VALKEY_URL=rediss://host:6379.

With degraded_mode, a failed Init ping does not abort. A background loop retries ping/rebuild (backoff + jitter) until the server is up or Shutdown. Health stays honest unless health_when_degraded is ready.

Tests

Unit tests cover the component contract without a server. Integration tests are gated on VALKEY_ADDR:

VALKEY_ADDR=127.0.0.1:6379 go test -race ./...

License

Apache License 2.0 — see LICENSE.

Documentation

Index

Constants

View Source
const (
	// ComponentName is the framework component name for the valkey component.
	// It is the identifier other components use in GetDependencies to require
	// valkey.
	ComponentName = "valkey"

	// ComponentStage is the stage data-layer components initialize in. It is
	// not a built-in bootstrap stage; AddComponent registers it automatically
	// the first time a component declares it.
	ComponentStage = cf.Stage("data")
)

Variables

This section is empty.

Functions

func OverlayURL

func OverlayURL(cfg *ValkeyConfig, raw string) error

OverlayURL merges connection fields from a redis/valkey URL into cfg. URL-derived fields win over existing values (file/env).

Types

type CFValkey

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

CFValkey is the caerus-framework-valkey component. It wraps a valkey-go client, verifies connectivity at Init, and closes it at Shutdown.

func New

func New(opts ...Option) *CFValkey

New creates a valkey component. The client is created and pinged at Init, not here.

func (*CFValkey) Client

func (c *CFValkey) Client() valkey.Client

Client returns the valkey-go client. It is non-nil after a successful Init and nil before Init or after Shutdown. When command hooks are configured (WithCommandHook), the returned client routes Do and DoMulti through the hook chain; all other methods are delegated to the underlying client.

func (*CFValkey) GetDependencies

func (c *CFValkey) GetDependencies() []string

GetDependencies implements cf.Dependencies. The component logs through the framework logs component, and depends on configuration when WithConfigSource is set.

func (*CFValkey) GetInitOrderStage

func (c *CFValkey) GetInitOrderStage() cf.Stage

GetInitOrderStage implements cf.CaerusComponent.

func (*CFValkey) Health

func (c *CFValkey) Health(ctx context.Context) error

Health implements cf.HealthProvider. It pings the valkey server, so the observability component's readiness endpoint reflects real connectivity. A nil client (before Init or after Shutdown) is unhealthy. After DegradedMode with a failed ping, behaviour follows health_when_degraded (default not_ready → still unhealthy for /readyz).

func (*CFValkey) Init

func (c *CFValkey) Init(ctx context.Context, fw *cf.CaerusFramework) error

Init implements cf.CaerusComponent. It creates the valkey-go client and verifies connectivity with a ping. By default a broken connection fails startup (fail-fast). With DegradedMode, a failed ping keeps the client and lets Initialize continue (metrics/logs scream; Health stays honest unless health_when_degraded=ready).

func (*CFValkey) Key

func (c *CFValkey) Key(parts ...string) string

Key builds a namespaced key by joining the configured prefix and parts with ":". The prefix's trailing ":" is normalized, so WithKeyPrefix("prod:") and WithKeyPrefix("prod") both produce the same keys:

v := cf_valkey.New(cf_valkey.WithKeyPrefix("prod:"))
v.Key("session", "abc")        // "prod:session:abc"
v.Key("ratelimit", c.RealIP()) // "prod:ratelimit:192.0.2.1"

With an empty prefix, Key is a plain ":"-join of the parts.

func (*CFValkey) KeyPrefix

func (c *CFValkey) KeyPrefix() string

KeyPrefix returns the configured namespace prefix (empty if none).

func (*CFValkey) LockMeter

func (c *CFValkey) LockMeter() *LockMeter

LockMeter returns the component's shared lock-traffic meter. Distributed lock helpers in the patterns subpackage feed it via this accessor; the totals ride the component's Metrics() output (aggregated per component instance, disambiguated by the component label).

func (*CFValkey) Metrics

func (c *CFValkey) Metrics() []cf_observability.Metric

Metrics implements cf_observability.MetricsProvider. Before Init or after Shutdown it returns nil. After Init (including DegradedMode without a live ping) it always returns samples so degrade/unreachable state is visible.

func (*CFValkey) Name

func (c *CFValkey) Name() string

Name implements cf.CaerusComponent. Returns the custom name set via WithName, or the default ComponentName ("valkey") if no custom name was set.

func (*CFValkey) OnConfigReload

func (c *CFValkey) OnConfigReload(source string, cfg any)

OnConfigReload implements cf.ConfigReloader. It rebuilds the client from the bound configuration source. The fresh value is delivered as cfg but the client is rebuilt from the source so the translation stays in one place. On failure the previous client is kept.

func (*CFValkey) RegisterConfigSources

func (c *CFValkey) RegisterConfigSources(conf any) error

RegisterConfigSources implements cf.ConfigSourceRegistrar. The framework calls it during argv absorption; it registers this component's configuration source (name, path, env prefix, format, Owner and the VALKEY_URL AfterLoad overlay) with the configuration component. No-op when no source is bound.

func (*CFValkey) Shutdown

func (c *CFValkey) Shutdown(ctx context.Context) error

Shutdown implements cf.CaerusComponent. It closes the valkey client; further use of Client() after shutdown returns the closed client.

type CommandHook

type CommandHook interface {
	// Do wraps a single command execution.
	Do(ctx context.Context, cmd valkey.Completed,
		next func(context.Context, valkey.Completed) valkey.ValkeyResult) valkey.ValkeyResult
	// DoMulti wraps a pipelined batch of commands.
	DoMulti(ctx context.Context, cmds []valkey.Completed,
		next func(context.Context, []valkey.Completed) []valkey.ValkeyResult) []valkey.ValkeyResult
}

CommandHook intercepts commands sent through the component's client before they reach Valkey. Hooks are configured at construction with WithCommandHook and run in registration order around the real command: each hook calls next to continue the chain (and finally the network round-trip), or short-circuits by returning without calling next. Use it to attach spans, log slow commands, or count traffic without importing an instrumentation library into this module.

type LockMeter

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

LockMeter aggregates distributed-lock traffic counters for one valkey component. Mutexes created against the component increment it through LockMeter(); CFValkey.Metrics() then exposes the totals on /metrics as Prometheus counters. Counters are cumulative and only increase for the process lifetime and are emitted (zero until first increment) while the component is connected.

func (*LockMeter) IncAcquireBusy

func (m *LockMeter) IncAcquireBusy()

IncAcquireBusy records an acquisition rejected because the lock was held.

func (*LockMeter) IncAcquireOK

func (m *LockMeter) IncAcquireOK()

IncAcquireOK records a successful lock acquisition.

func (*LockMeter) IncUnlockMismatch

func (m *LockMeter) IncUnlockMismatch()

IncUnlockMismatch records a release where the caller no longer owned the lock (expired or stolen).

func (*LockMeter) IncUnlockOK

func (m *LockMeter) IncUnlockOK()

IncUnlockOK records a release that actually deleted the lock key.

func (*LockMeter) Metrics

func (m *LockMeter) Metrics(labels map[string]string) []cf_observability.Metric

Metrics renders the meter's four counters, each carrying a copy of the caller's labels so the lock series share the component's identity. Counters are emitted while the component is connected (zero until first fired), so the series are always present on /metrics.

type Option

type Option func(*options)

Option configures the valkey component at construction time.

func WithAddress

func WithAddress(addr string) Option

WithAddress sets the single server address (default "127.0.0.1:6379").

func WithAddresses

func WithAddresses(addrs ...string) Option

WithAddresses sets multiple server addresses (for cluster/sentinel setups).

func WithClientName

func WithClientName(name string) Option

WithClientName sets CLIENT SETNAME on connections.

func WithClientOption

func WithClientOption(opt valkey.ClientOption) Option

WithClientOption sets the full valkey-go client option. Convenience setters (WithAddress/WithAddresses, WithUsername, WithPassword, WithDB, WithClientName) override the matching fields, so call them after WithClientOption if you combine them.

func WithCommandHook

func WithCommandHook(hooks ...CommandHook) Option

WithCommandHook registers command hooks on the component. Multiple calls append; hooks run in registration order, the first hook wrapping the outermost. Use it to attach tracing spans, slow-command logging, or command counters to every Client().Do / DoMulti call. The hook interface lives here so the valkey module needs no instrumentation dependency (e.g. OpenTelemetry); apps implement CommandHook against the instrumenter of their choice.

func WithConfig

func WithConfig(cfg ValkeyConfig) Option

WithConfig sets a static connection configuration snapshot. Non-zero fields of cfg override the values set by the convenience options. Prefer WithConfigSource when using caerus-framework-configuration with hot-reload.

func WithConfigSource

func WithConfigSource(name, path string, opts ...SourceOption) Option

WithConfigSource binds this component to a named configuration source and registers that source with the configuration component (via the framework's ConfigSourceRegistrar pass during argv absorption). The module owns the Source: the config type, default EnvPrefix, the VALKEY_URL AfterLoad overlay and its Owner (Name(), so named instances reload correctly). main only points the instance at where the config lives.

cf_valkey.New(cf_valkey.WithConfigSource("valkey", "config/valkey.json"))
cf_valkey.New(cf_valkey.WithConfigSource("valkey-cache", "/etc/app/valkey-cache.yaml",
    cf_valkey.WithSourceFormat(cf_configuration.FormatYAML)))

A path of "" registers an env-only (fileless) source when the EnvPrefix is non-empty. The path CLI override stays --<source-name> (ParseFlags). Declares a dependency on "configuration".

func WithConnLifetime

func WithConnLifetime(d time.Duration) Option

WithConnLifetime sets a maximum connection lifetime. Connections older than this are closed and replaced. Zero means no limit (valkey-go default).

func WithConnWriteTimeout

func WithConnWriteTimeout(d time.Duration) Option

WithConnWriteTimeout sets the per-connection read/write timeout. It bounds pipeline response waits and triggers periodic PINGs for liveness.

func WithDB

func WithDB(db int) Option

WithDB selects the logical database (0-15 on a standalone server).

func WithDegradedMode added in v0.0.3

func WithDegradedMode(enabled bool) Option

WithDegradedMode allows Init to succeed when the connectivity ping fails. Default is hard-fail. Degraded mode screams in logs/metrics; Health still fails ping unless HealthWhenDegraded is "ready".

func WithDialTimeout

func WithDialTimeout(d time.Duration) Option

WithDialTimeout sets the TCP dial timeout (default: valkey-go's default, typically 5s). Applied to the underlying net.Dialer.

func WithHealthWhenDegraded added in v0.0.3

func WithHealthWhenDegraded(policy string) Option

WithHealthWhenDegraded sets Health() behaviour while unreachable after DegradedMode: "not_ready" (default) or "ready" (break-glass LB traffic).

func WithKeyPrefix

func WithKeyPrefix(prefix string) Option

WithKeyPrefix sets a namespace prefix applied by Key to every key this component's users build. Useful when several services or environments share one instance. The prefix is trimmed of a trailing ":"; an empty prefix keeps Key a plain ":"-join.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger overrides the logger used for component diagnostics. By default the component logs through the framework logs component (declared in GetDependencies); WithLogger is an explicit override for tests and embedded use and wins over the framework logger. slog.Default() remains the fallback only when neither is available.

func WithName

func WithName(name string) Option

WithName sets a custom component name, allowing multiple valkey instances in the same process. The default name is "valkey" (ComponentName). Use this when you need multiple valkey clients (e.g., cache and sessions) in one binary. Retrieve named instances with GetByName[*CFValkey](fw, "cache").

func WithPassword

func WithPassword(password string) Option

WithPassword sets the AUTH password.

func WithPingTimeout

func WithPingTimeout(d time.Duration) Option

WithPingTimeout sets how long Init waits for the connectivity ping before failing (default 5s).

func WithTLS

func WithTLS(tlsCAFile, tlsCertFile, tlsKeyFile string) Option

WithTLS configures TLS from PEM file paths. Suitable for Kubernetes-mounted secrets (External Secrets). CA is optional (server verify / private CA). Client cert and key are a pair: both set for mTLS, or both empty. A half pair is rejected at apply / reload (last-good), same as postgresql.

func WithTLSInsecureSkipVerify added in v0.0.7

func WithTLSInsecureSkipVerify(skip bool) Option

WithTLSInsecureSkipVerify skips server certificate verification. Use only for broken lab certs; rediss:// does not turn this on by itself.

func WithUsername

func WithUsername(username string) Option

WithUsername sets the AUTH username.

type SourceOption

type SourceOption func(*sourceOptions)

SourceOption configures the self-registered configuration source created by WithConfigSource.

func WithSourceEnvPrefix

func WithSourceEnvPrefix(prefix string) SourceOption

WithSourceEnvPrefix sets the environment overlay prefix for the source (default: the uppercase source name with "-" replaced by "_", plus "_"). An empty prefix disables env overlay.

func WithSourceFormat

func WithSourceFormat(f cf_configuration.Format) SourceOption

WithSourceFormat forces the file format instead of inferring it from the path extension (".yaml"/".yml" → YAML; anything else JSON).

type ValkeyConfig

type ValkeyConfig struct {
	Addresses   []string `json:"addresses" yaml:"addresses" env:"ADDRESSES"`
	Username    string   `json:"username,omitempty" yaml:"username,omitempty" env:"USERNAME"`
	Password    string   `json:"password,omitempty" yaml:"password,omitempty" env:"PASSWORD" secret:"redact"`
	DB          int      `json:"db" yaml:"db" env:"DB"`
	ClientName  string   `json:"client_name,omitempty" yaml:"client_name,omitempty" env:"CLIENT_NAME"`
	KeyPrefix   string   `json:"key_prefix,omitempty" yaml:"key_prefix,omitempty" env:"KEY_PREFIX"`
	TLSCAFile   string   `json:"tls_ca_file,omitempty" yaml:"tls_ca_file,omitempty" env:"TLS_CA_FILE"`
	TLSCertFile string   `json:"tls_cert_file,omitempty" yaml:"tls_cert_file,omitempty" env:"TLS_CERT_FILE"`
	TLSKeyFile  string   `json:"tls_key_file,omitempty" yaml:"tls_key_file,omitempty" env:"TLS_KEY_FILE"`
	// TLS enables TLS with system roots (MinVersion 1.2) when no PEM files
	// are set. ParseURL/OverlayURL set this for rediss:// and valkeys://.
	// PEM files still win for custom CA / mTLS.
	TLS *bool `json:"tls,omitempty" yaml:"tls,omitempty" env:"TLS"`
	// TLSInsecureSkipVerify skips certificate verify. Lab/broken certs only;
	// never the default. Explicit setting, not implied by rediss://.
	TLSInsecureSkipVerify *bool   `json:"tls_insecure_skip_verify,omitempty" yaml:"tls_insecure_skip_verify,omitempty" env:"TLS_INSECURE_SKIP_VERIFY"`
	DialTimeoutSec        float64 `json:"dial_timeout_sec,omitempty" yaml:"dial_timeout_sec,omitempty" env:"DIAL_TIMEOUT_SEC"`
	ConnWriteTimeoutSec   float64 `json:"conn_write_timeout_sec,omitempty" yaml:"conn_write_timeout_sec,omitempty" env:"CONN_WRITE_TIMEOUT_SEC"`
	ConnLifetimeSec       float64 `json:"conn_lifetime_sec,omitempty" yaml:"conn_lifetime_sec,omitempty" env:"CONN_LIFETIME_SEC"`
	// DegradedMode — when true, a failed Init ping does not abort the process.
	// The client is kept for later reconnect; metrics/logs scream. Default off
	// (pointer so omitted ≠ explicit false). Off by default (hard Init).
	DegradedMode *bool `json:"degraded_mode,omitempty" yaml:"degraded_mode,omitempty" env:"DEGRADED_MODE"`
	// HealthWhenDegraded: "not_ready" (default) or "ready". Controls Health()
	// (and thus /readyz) while the client cannot ping after a degraded Init
	// or while disconnected. "ready" is break-glass: send LB traffic anyway.
	HealthWhenDegraded string `json:"health_when_degraded,omitempty" yaml:"health_when_degraded,omitempty" env:"HEALTH_WHEN_DEGRADED"`
}

ValkeyConfig is the file/env-drivable connection configuration. Load it through the configuration component (caerus-framework-configuration) and pass it via WithConfig; both JSON and YAML tags are provided.

func ParseURL

func ParseURL(raw string) (ValkeyConfig, error)

ParseURL parses a redis:// or valkey:// URL (or host:port) into ValkeyConfig. Examples:

redis://user:pass@127.0.0.1:6379/0
valkey://127.0.0.1:6379
127.0.0.1:6379

Directories

Path Synopsis
Package patterns provides small, optional helpers for common Valkey usage patterns.
Package patterns provides small, optional helpers for common Valkey usage patterns.

Jump to

Keyboard shortcuts

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