Files
valkey/CLAUDE.md

93 lines
3.6 KiB
Markdown
Raw Normal View History

# 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.Client` returns the native `valkey-go` client
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()` returns `health.LevelDegraded`, not
`LevelCritical`. 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 `logger` field is typed as `logz.Logger`, the shared
interface from the `logz` module (ADR-001 global pattern).
## Patterns
**Lifecycle registration:**
```go
vk := valkey.New(logger, cfg)
lc.Append(vk) // registers OnInit / OnStart / OnStop
```
**Accessing the client in a repository:**
```go
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:**
```go
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:**
```go
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.Client` methods here.
If a consumer needs a minimal testable interface, define it in the consumer package.
- Do not call `Client()` before `OnInit` has run — it will return `nil`.
- Do not treat `health.LevelDegraded` as if it were `LevelCritical`. 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_InvalidAddr` verifies that an empty address slice does not panic.
Whether `OnInit` returns 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` (package `valkey_test`) asserts `New(...)` satisfies `Component`
at compile time.