The package-doc examples showed a fictional launcher.Register / health.Register API (and launcher.Append as a package func). Corrected to the real wiring: lc.Append + web/health.NewHandler(...).ServeHTTP. (component.go doc comment corrected too.) Documentation-only. No code, API, or dependency changes; version stays v1.1.0 (the v1.1.0 tag is moved to include this fix). Verified by compiling the corrected pattern against the real API. Reviewed-on: #2 Co-authored-by: Rene Nochebuena Guerrero <rene@nochebuena.dev> Co-committed-by: Rene Nochebuena Guerrero <rene@nochebuena.dev>
4.5 KiB
einherjar/cache-valkey
Speed is not a virtue. It is the difference between arriving and never arriving.
code.nochebuena.dev/einherjar/cache-valkey is the Valkey cache component of the Einherjar framework. It wraps valkey-go behind a lifecycle-aware Component and ships three adapters — permission cache, rate limiter, and JWT blacklist — that satisfy interfaces in auth, web, and auth-jwt via Go's structural typing, with no cross-module import required.
Quick start
import cachevalkey "code.nochebuena.dev/einherjar/cache-valkey"
vk := cachevalkey.New(logger, cachevalkey.Config{
Addrs: []string{"localhost:6379"},
})
lc.Append(vk) // OnInit → OnStart → OnStop
// vk is observability.Checkable (PING-based, LevelDegraded):
srv.Get("/health", health.NewHandler(logger, vk).ServeHTTP)
Connecting to other modules
Go's structural typing handles interface assignment across module boundaries — no cast, no glue interface required. Create adapters from the component and pass them directly to the consuming modules:
// --- 1. Create the component (lifecycle + health + Provider) ---
vk := cachevalkey.New(logger, cfg)
lc.Append(vk)
srv.Get("/health", health.NewHandler(logger, vk).ServeHTTP)
// --- 2. Create adapters (one per consumer interface) ---
// Each adapter wraps vk (a Provider) and satisfies one interface in another module.
// No import of auth, web, or auth-jwt is needed here.
permCache := cachevalkey.NewPermissionCache(vk)
rateLimiter := cachevalkey.NewRateLimiterStore(vk, time.Second, 100)
blacklist := cachevalkey.NewBlacklist(vk)
// --- 3. Wire adapters into consuming modules ---
// Go's structural typing handles the interface assignment — no cast required.
// auth: permission cache (satisfies rbac.Cache)
permProvider := rbac.NewCachedPermissionProvider(permCache, baseProvider, rbac.CacheConfig{TTL: 5 * time.Minute})
// web: rate limiter (satisfies mw.RateLimiterStore)
router.Use(mw.IPRateLimit(rateLimiter))
// auth-jwt: refresh token blacklist (satisfies authjwt.Blacklist)
pair, err := authjwt.RefreshTokenPair(ctx, signer, oldRefreshToken, blacklist, tokenCfg, newClaims)
The compile-time duck-type checks in compliance_test.go verify that each adapter
satisfies its target interface. If those checks compile, the wiring above is guaranteed
to work.
Configuration
| Environment variable | Required | Default | Description |
|---|---|---|---|
EINHERJAR_VALKEY_ADDRS |
Yes | — | Comma-separated host:port addresses |
EINHERJAR_VALKEY_PASSWORD |
No | "" |
Auth password |
EINHERJAR_VALKEY_DB |
No | 0 |
Database index |
EINHERJAR_VALKEY_CLIENT_CACHE_MB |
No | 0 |
Client-side cache per connection in MB (0 = disabled) |
API
Provider interface
Component satisfies Provider. Pass the result of New() wherever a Provider
is expected.
| Method | Description |
|---|---|
Get(ctx, key) (string, bool, error) |
GET key; returns ("", false, nil) on miss |
Set(ctx, key, value, ttl) error |
SET key value [EX ttl]; ttl=0 = no expiry |
Del(ctx, keys...) error |
DEL one or more keys |
Exists(ctx, key) (bool, error) |
EXISTS key |
Expire(ctx, key, ttl) error |
EXPIRE key ttl |
IncrWithTTL(ctx, key, ttl) (int64, error) |
Atomic INCR + EXPIRE on first increment (Lua) |
Native() vk.Client |
Raw valkey-go client for operations not in Provider |
Adapters
| Constructor | Satisfies | Description |
|---|---|---|
NewPermissionCache(Provider) |
auth/rbac.Cache |
Stores int64 permission bitmasks as decimal strings |
NewRateLimiterStore(Provider, window, limit) |
web/mw.RateLimiterStore |
Fixed-window counter; Lua atomic increment |
NewBlacklist(Provider) |
auth-jwt.Blacklist |
JWT JTI revocation via SET/EXISTS |
Dependency graph
cache-valkey
├── contracts v1.0.0 (lifecycle.Component, observability.Checkable, logging.Logger)
├── core v1.0.0 (xerrors)
└── valkey-go v1.0.54 (native client)
No dependency on web, auth, or auth-jwt.
The three adapters satisfy interfaces in those modules via duck typing.