Valkey (Redis-compatible) client component with launcher lifecycle and health check integration. What's included: - Config with Addrs, Password, SelectDB, CacheSizeEachConn (env-driven) - Provider interface exposing native valkey-go Client() directly (no wrapper) - Component interface: launcher.Component + health.Checkable + Provider - New(logger, cfg) constructor for lifecycle registration via lc.Append - Health check via PING at LevelDegraded priority - Graceful shutdown calling client.Close() in OnStop Tested-via: todo-api POC integration Reviewed-against: docs/adr/
3.6 KiB
3.6 KiB
valkey
Valkey (Redis-compatible) client with launcher lifecycle and health check integration.
Purpose
Manages the lifecycle of a valkey-go client: constructs it from config, verifies
connectivity at startup, exposes the native client to consumers, and closes it on shutdown.
Provides a health check via PING. Does not add serialisation, key namespacing, or any
caching policy on top of the native client.
Tier & Dependencies
Tier 3 (infrastructure) — depends on:
code.nochebuena.dev/go/health(Tier 2)code.nochebuena.dev/go/launcher(Tier 2)code.nochebuena.dev/go/logz(Tier 1)github.com/valkey-io/valkey-go(native Valkey client)
Does not depend on xerrors — errors from the valkey-go client are returned as-is.
Key Design Decisions
- Native client exposure:
Client() vk.Clientreturns the nativevalkey-goclient directly. No wrapper, no re-exported command subset. Callers use the full valkey-go command builder API. See ADR-001. - No serialisation helpers: The module has no
SetJSON,GetJSON, or similar helpers. Callers marshal and unmarshal their own data. See ADR-002. - Health priority is Degraded:
Priority()returnshealth.LevelDegraded, notLevelCritical. A Valkey outage degrades service but should not always halt it, depending on whether the caller falls back to the primary datastore. - Optional client-side caching:
Config.CacheSizeEachConn(in MB) enables valkey-go's built-in client-side cache. Setting it to 0 (the default) disables the cache entirely. - Duck-typed Logger: The internal
loggerfield is typed aslogz.Logger, the shared interface from thelogzmodule (ADR-001 global pattern).
Patterns
Lifecycle registration:
vk := valkey.New(logger, cfg)
lc.Append(vk) // registers OnInit / OnStart / OnStop
Accessing the client in a repository:
type cacheRepo struct {
provider valkey.Provider
}
func (r *cacheRepo) Get(ctx context.Context, key string) ([]byte, error) {
client := r.provider.Client()
result := client.Do(ctx, client.B().Get().Key(key).Build())
return result.AsBytes()
}
Writing with TTL:
client := provider.Client()
cmd := client.B().Set().Key(key).Value(val).Ex(300).Build()
if err := client.Do(ctx, cmd).Error(); err != nil {
return err
}
Health check registration:
health.Register(vkComponent) // satisfies health.Checkable via Name()/Priority()/HealthCheck()
What to Avoid
- Do not add serialisation helpers to this module. Keep marshal/unmarshal in the caller or in a separate cache repository layer.
- Do not define custom interfaces that re-export a subset of
vk.Clientmethods here. If a consumer needs a minimal testable interface, define it in the consumer package. - Do not call
Client()beforeOnInithas run — it will returnnil. - Do not treat
health.LevelDegradedas if it wereLevelCritical. Design callers to handle cache misses gracefully rather than depending on Valkey for correctness.
Testing Notes
- Unit tests (
valkey_test.go) do not require a running Valkey server. They test lifecycle behaviour (nil client safety, name, priority) without real network calls. TestComponent_OnInit_InvalidAddrverifies that an empty address slice does not panic. WhetherOnInitreturns an error depends on the valkey-go implementation; the test documents the accepted behaviour.- Integration tests requiring a live Valkey instance are outside this module and belong in a higher-tier test suite.
compliance_test.go(packagevalkey_test) assertsNew(...)satisfiesComponentat compile time.