Documentation
¶
Index ¶
- Constants
- Variables
- type CFState
- func (c *CFState) Allow(ctx context.Context, key string, limit int64, window time.Duration) (RateLimitResult, error)
- func (c *CFState) AllowOpts(ctx context.Context, key string, limit int64, window time.Duration, ...) (RateLimitResult, error)
- func (c *CFState) CacheGet(ctx context.Context, dst any, parts ...string) error
- func (c *CFState) CacheGetOrLoad(ctx context.Context, ttl time.Duration, load LoadFunc, parts ...string) (val []byte, shared bool, err error)
- func (c *CFState) CacheSet(ctx context.Context, v any, ttl time.Duration, parts ...string) error
- func (c *CFState) Client() valkey.Client
- func (c *CFState) CreateSession(ctx context.Context, id string, v any, ttl time.Duration, ...) error
- func (c *CFState) GetDependencies() []string
- func (c *CFState) GetInitOrderStage() cf.Stage
- func (c *CFState) GetSession(ctx context.Context, id string, dst any) (found bool, err error)
- func (c *CFState) Health(ctx context.Context) error
- func (c *CFState) Init(ctx context.Context, fw *cf.CaerusFramework) error
- func (c *CFState) Key(parts ...string) string
- func (c *CFState) ListSessionsForUser(ctx context.Context, userID string) ([]string, error)
- func (c *CFState) Metrics() []cf_observability.Metric
- func (c *CFState) Name() string
- func (c *CFState) OnConfigReload(source string, cfg any)
- func (c *CFState) Peek(ctx context.Context, key string) (RateLimitResult, error)
- func (c *CFState) PeekOpts(ctx context.Context, key string, opts CounterOpts) (RateLimitResult, error)
- func (c *CFState) RateLimit(ctx context.Context, key string, limit int64, window time.Duration) (RateLimitResult, error)
- func (c *CFState) RegisterConfigSources(conf any) error
- func (c *CFState) Reset(ctx context.Context, key string) error
- func (c *CFState) RevokeAllForUser(ctx context.Context, userID string, keep ...string) error
- func (c *CFState) RevokeSession(ctx context.Context, id string) error
- func (c *CFState) SessionExists(ctx context.Context, id string) (bool, error)
- func (c *CFState) Shutdown(ctx context.Context) error
- func (c *CFState) TouchSession(ctx context.Context, id string, ttl time.Duration) error
- type CounterOpts
- type LoadFunc
- type MapFullPolicy
- type MissingTTLPolicy
- type Option
- func WithCacheTTL(d time.Duration) Option
- func WithConfig(cfg StateConfig) Option
- func WithConfigSource(name, path string, opts ...SourceOption) Option
- func WithForceMemory(enabled bool) Option
- func WithLogger(logger *slog.Logger) Option
- func WithMemoryMapFullPolicy(policy string) Option
- func WithName(name string) Option
- func WithSessionTTL(d time.Duration) Option
- func WithUseMemoryFallback(enabled bool) Option
- func WithValkeyName(name string) Option
- func WithoutValkeyPeer() Option
- type RateLimitConfig
- type RateLimitResult
- type SessionOption
- type SourceOption
- type StateConfig
Constants ¶
const ( // ComponentName is the framework component name for the valkey-state // component. It is the identifier other components use in GetDependencies // to require it. ComponentName = "valkey-state" // 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 ¶
var ErrInvalidLimit = errors.New("cf_valkey_state: limit must be > 0")
ErrInvalidLimit is returned when limit is not positive (the HTTP limiter may disable limiting before calling the machine; the store never treats limit 0 as “always allow”).
var ErrInvalidWindow = errors.New("cf_valkey_state: window must be > 0")
ErrInvalidWindow is returned when window is not positive.
var ErrMissingTTL = errors.New("cf_valkey_state: counter missing TTL")
ErrMissingTTL is returned when a Valkey counter exists with count > 0 but no usable TTL, after the key was scrubbed, and the missing-TTL policy is error.
Functions ¶
This section is empty.
Types ¶
type CFState ¶
type CFState struct {
// contains filtered or unexported fields
}
CFState is the caerus-framework-valkey-state component. It is a stateless consumer of a cf_valkey.CFValkey peer: it holds the peer component (never a client snapshot) and builds every command through the peer's live Client() and prefix-aware Key(), so reconnects and key prefixes stay consistent. It provides three keyed state services over that client:
- Sessions: opaque JSON values with a TTL and sliding-window touch.
- Cache: JSON cache-aside plus a singleflight get-or-load.
- Rate limit: atomic fixed-window counters.
func (*CFState) Allow ¶ added in v0.0.7
func (c *CFState) Allow(ctx context.Context, key string, limit int64, window time.Duration) (RateLimitResult, error)
Allow increments the fixed-window counter for the logical key.
func (*CFState) AllowOpts ¶ added in v0.0.7
func (c *CFState) AllowOpts(ctx context.Context, key string, limit int64, window time.Duration, opts CounterOpts) (RateLimitResult, error)
AllowOpts is Allow with a missing-TTL override.
func (*CFState) CacheGet ¶
CacheGet reads a JSON value at cache:<parts...> into dst. A miss returns nil and leaves dst untouched. Cache hit/miss counters are updated.
func (*CFState) CacheGetOrLoad ¶
func (c *CFState) CacheGetOrLoad(ctx context.Context, ttl time.Duration, load LoadFunc, parts ...string) (val []byte, shared bool, err error)
CacheGetOrLoad returns the bytes cached at cache:<parts...>, or runs load once for concurrent callers in this process (singleflight), then stores the result with ttl. A ttl <= 0 uses the configured default.
WARNING: singleflight is process-local only; other pods still stampede on a cold key. shared=true when this caller waited on another goroutine's load. Failure modes: a load error is returned to waiters and nothing is cached; a SET failure after a successful load returns the loaded bytes along with the error so callers can log without losing the value.
func (*CFState) CacheSet ¶
CacheSet marshals v as JSON and stores it at cache:<parts...> with the given TTL. A ttl <= 0 uses the configured default.
func (*CFState) Client ¶
Client returns the peer's live valkey client (nil before Init or after Shutdown). It follows the peer-pointer convention: the peer is re-read per use, so a client swap on the valkey side is picked up immediately.
func (*CFState) CreateSession ¶
func (c *CFState) CreateSession(ctx context.Context, id string, v any, ttl time.Duration, opts ...SessionOption) error
CreateSession stores a JSON session value at session:<id> with the given TTL. A ttl <= 0 uses the configured default. The session id is chosen by the caller (typically an opaque token); it is never derived from the value. Pass WithSessionUser to maintain the per-user SET (Choice A). Without it, the session exists by id only — ListSessionsForUser will not see it.
func (*CFState) GetDependencies ¶
GetDependencies implements cf.Dependencies. The component depends on the valkey component it consumes (the actual peer name when WithValkeyName is set, the default ComponentName otherwise), logs through the framework logs component, and depends on configuration when WithConfigSource is set. Peer names are fixed at construction, so the graph is stable before Init.
func (*CFState) GetInitOrderStage ¶
GetInitOrderStage implements cf.CaerusComponent.
func (*CFState) GetSession ¶
GetSession reads the session at session:<id> and unmarshals it into dst. found is false when the session does not exist or has expired. The session TTL is not extended here; call TouchSession for a sliding window.
func (*CFState) Health ¶
Health implements cf.HealthProvider. It reports healthy while the peer's valkey client is initialized; real connectivity is owned by the valkey component's own Health (aggregated by observability's /readyz).
func (*CFState) Init ¶
Init implements cf.CaerusComponent. It resolves the valkey peer component (by name or the default "valkey"), failing fast when it is missing or not yet initialized. No connection is opened here; the peer owns its client.
func (*CFState) Key ¶
Key builds a prefix-aware key through the valkey peer. Useful for composing with caerus-framework-valkey/patterns helpers (e.g. patterns.NewMutex) against the same key space.
func (*CFState) ListSessionsForUser ¶ added in v0.0.8
ListSessionsForUser returns live session ids for userID (SMEMBERS on the per-user SET, then EXISTS on each payload). Ghost members (expired payload) are SREM'd. This is O(sessions of that user), not KEYS. Sessions created without WithSessionUser are not listed. Empty userID is an error. Errors do not include the user id.
func (*CFState) Metrics ¶
func (c *CFState) Metrics() []cf_observability.Metric
Metrics implements cf_observability.MetricsProvider. It reports operation counters while the peer's client is initialized; before Init or after Shutdown it returns nil, so the observability component skips it (lazy pickup). Counters are cumulative for the process lifetime and are emitted (zero until first fired) so the series are always present.
func (*CFState) Name ¶
Name implements cf.CaerusComponent. Returns the custom name set via WithName, or the default ComponentName ("valkey-state") if no custom name was set.
func (*CFState) OnConfigReload ¶
OnConfigReload implements cf.ConfigReloader. It re-reads the behavior tunables (session/cache TTLs) from the bound configuration source. No connection is rebuilt: the valkey peer owns client rotation, and this component is stateless over it.
func (*CFState) Peek ¶ added in v0.0.7
Peek reads the counter without incrementing. Allowed is always false.
func (*CFState) PeekOpts ¶ added in v0.0.7
func (c *CFState) PeekOpts(ctx context.Context, key string, opts CounterOpts) (RateLimitResult, error)
PeekOpts is Peek with a missing-TTL override.
func (*CFState) RateLimit ¶
func (c *CFState) RateLimit(ctx context.Context, key string, limit int64, window time.Duration) (RateLimitResult, error)
RateLimit is sugar for Allow with default CounterOpts (fixed window).
func (*CFState) RegisterConfigSources ¶
RegisterConfigSources implements cf.ConfigSourceRegistrar. The framework calls it during argv absorption; it registers this component's configuration source (name, path, env prefix, format, Owner) with the configuration component. No-op when no source is bound.
func (*CFState) RevokeAllForUser ¶ added in v0.0.8
RevokeAllForUser deletes every indexed session for userID except keep. keep is typically the current session id ("revoke others"). Not KEYS. Empty userID is an error. Errors do not include the user id.
func (*CFState) RevokeSession ¶
RevokeSession deletes session:<id> and SREM from the user SET when the session was created with WithSessionUser. Deleting a missing session is a no-op success.
func (*CFState) SessionExists ¶
SessionExists reports whether session:<id> exists and is unexpired.
type CounterOpts ¶ added in v0.0.7
type CounterOpts struct {
// MissingTTL overrides the default proceed policy for this call.
MissingTTL MissingTTLPolicy
}
CounterOpts is optional per-call store hygiene. The app does not pick Valkey vs memory here — that is the nested rate_limit settings.
type LoadFunc ¶
LoadFunc fetches the canonical value on a cache miss. Called at most once per key per in-flight wave inside this process.
type MapFullPolicy ¶ added in v0.0.7
type MapFullPolicy int
MapFullPolicy is what happens when the counter sticky-note map hits MaxEntries for a new logical key.
const ( // MapFullAllow allows a new key without counting when the map is full. MapFullAllow MapFullPolicy = iota // MapFullDeny rejects Allow for a new key when the cap is reached. MapFullDeny )
type MissingTTLPolicy ¶ added in v0.0.7
type MissingTTLPolicy int
MissingTTLPolicy selects what happens after scrubbing a counter that has count > 0 but no usable TTL. Zero value means proceed.
const ( // MissingTTLProceed continues after delete: Allow retries Lua once; Peek // returns empty. This is the default when CounterOpts leaves the field unset. MissingTTLProceed MissingTTLPolicy = iota // MissingTTLError returns ErrMissingTTL after delete. MissingTTLError )
type Option ¶
type Option func(*options)
Option configures the state component at construction time.
func WithCacheTTL ¶
WithCacheTTL sets the default cache TTL used when a cache call leaves ttl at zero (default 5m).
func WithConfig ¶
func WithConfig(cfg StateConfig) Option
WithConfig sets a static 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, the default EnvPrefix and its Owner (Name(), so named instances reload correctly). main only points the instance at where the config lives.
cf_valkey_state.New(cf_valkey_state.WithConfigSource("valkey-state", "config/valkey-state.json"))
cf_valkey_state.New(cf_valkey_state.WithConfigSource("sess", "/etc/app/sess.yaml",
cf_valkey_state.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 WithForceMemory ¶ added in v0.0.7
WithForceMemory prefers the counter map even when a live Valkey client exists.
func WithLogger ¶
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 WithMemoryMapFullPolicy ¶ added in v0.0.7
WithMemoryMapFullPolicy sets "allow" or "deny" when the counter map is full.
func WithName ¶
WithName sets a custom component name, allowing multiple state instances in the same process. The default name is "valkey-state" (ComponentName). Retrieve named instances with GetByName[*CFState](fw, "sessions").
func WithSessionTTL ¶
WithSessionTTL sets the default session TTL used when a session call leaves ttl at zero (default 24h).
func WithUseMemoryFallback ¶ added in v0.0.7
WithUseMemoryFallback enables the counter sticky-note map when Valkey Eval fails or Client() is nil.
func WithValkeyName ¶
WithValkeyName binds the state component to a valkey component with the given name (WithName on the valkey side). The default is the valkey ComponentName ("valkey").
func WithoutValkeyPeer ¶ added in v0.0.7
func WithoutValkeyPeer() Option
WithoutValkeyPeer omits valkey from GetDependencies (GH App / counters-only sticky notes). Init then requires rate_limit memory (use_memory_fallback or force_memory) plus memory_map_full_policy. Sessions and cache error: there is no fridge.
type RateLimitConfig ¶ added in v0.0.7
type RateLimitConfig struct {
UseMemoryFallback *bool `json:"use_memory_fallback,omitempty" yaml:"use_memory_fallback,omitempty"`
ForceMemory *bool `json:"force_memory,omitempty" yaml:"force_memory,omitempty"`
MemoryMaxEntries int `json:"memory_max_entries,omitempty" yaml:"memory_max_entries,omitempty"`
MemoryMapFullPolicy string `json:"memory_map_full_policy,omitempty" yaml:"memory_map_full_policy,omitempty"`
}
RateLimitConfig is the counter store (not HTTP policy). Only this nested block may use an in-process map — sessions and cache stay Valkey-only.
type RateLimitResult ¶
type RateLimitResult struct {
// Allowed is true when Count does not exceed the limit for the window.
// Peek always leaves this false (Peek has no limit).
Allowed bool
// Count is the (incremented, for Allow) request count within the window.
Count int64
// ResetIn is the time remaining until the window resets (for Retry-After).
ResetIn time.Duration
}
RateLimitResult reports the outcome of Allow or Peek.
type SessionOption ¶ added in v0.0.8
type SessionOption func(*sessionOpts)
SessionOption configures CreateSession (user index binding).
func WithSessionUser ¶ added in v0.0.8
func WithSessionUser(userID string) SessionOption
WithSessionUser indexes this session under userID so ListSessionsForUser and RevokeAllForUser can find it. Empty userID is ignored (no index, same as omitting the option). This is not inferred from the JSON value.
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 StateConfig ¶
type StateConfig struct {
// SessionTTLSec is the default session TTL in seconds when a call leaves
// ttl at zero (default 24h).
SessionTTLSec int64 `json:"session_ttl_sec,omitempty" yaml:"session_ttl_sec,omitempty" env:"SESSION_TTL_SEC"`
// CacheTTLSec is the default cache TTL in seconds when a call leaves ttl
// at zero (default 5m).
CacheTTLSec int64 `json:"cache_ttl_sec,omitempty" yaml:"cache_ttl_sec,omitempty" env:"CACHE_TTL_SEC"`
// RateLimit is the nested counter-store section (Lua vs sticky-note map).
// HTTP limit/window numbers do not live here.
RateLimit RateLimitConfig `json:"rate_limit,omitempty" yaml:"rate_limit,omitempty" env:"-"`
// Env overlay cannot recurse into nested structs; these aliases merge
// into RateLimit in applyConfig (VALKEY_STATE_RATE_LIMIT_*).
RateLimitUseMemoryFallback *bool `json:"-" yaml:"-" env:"RATE_LIMIT_USE_MEMORY_FALLBACK"`
RateLimitForceMemory *bool `json:"-" yaml:"-" env:"RATE_LIMIT_FORCE_MEMORY"`
RateLimitMemoryMaxEntries int `json:"-" yaml:"-" env:"RATE_LIMIT_MEMORY_MAX_ENTRIES"`
RateLimitMapFullPolicy string `json:"-" yaml:"-" env:"RATE_LIMIT_MEMORY_MAP_FULL_POLICY"`
}
StateConfig is the file/env-drivable behavior configuration. Load it through the configuration component (caerus-framework-configuration) and pass it via WithConfigSource; both JSON and YAML tags are provided.