Documentation
¶
Overview ¶
Package cache defines clicky's cache contracts and a dependency-free in-process backend for them:
- Store (store.go) is a minimal Redis-shaped key/value surface — the strings and sorted sets that the domain stores (prompt.Store, metrics.Timeseries) are written once against. NewMemory is the in-process implementation; valkey.NewStore is the cross-process one.
- Browser (this file) is a read/admin API over a whole cache keyspace (valkey/redis or anything shaped like it): a prefix tree, per-key detail, search, whole-keyspace stats, and key/prefix deletion.
The package mirrors the metrics split: the interfaces plus the HTTP handler live here in the root module, while the valkey-backed implementations live in their own module (github.com/flanksource/clicky/valkey) so the root module never pulls in client dependencies.
Index ¶
- Variables
- func Handler(b Browser, prefix string) http.Handler
- func RegisterRoutes(mux *http.ServeMux, b Browser, prefix string)
- type Bound
- type Browser
- type DeleteResponse
- type KeyDetail
- type ScoredMember
- type SearchRequest
- type SearchResponse
- type Stats
- type Store
- type TreeNode
- type TreeRequest
- type TreeResponse
Constants ¶
This section is empty.
Variables ¶
var ( NegInf = Bound{Inf: -1} PosInf = Bound{Inf: 1} )
NegInf and PosInf are the unbounded endpoints, used when a query side is open.
var ErrKeyNotFound = errors.New("cache: key not found")
ErrKeyNotFound is returned by Browser.Key when the key does not exist. The HTTP handler maps it to a 404.
Functions ¶
func Handler ¶
Handler returns the cache-browser endpoints as a standalone http.Handler for callers that compose their own mux.
func RegisterRoutes ¶
RegisterRoutes mounts the cache-browser endpoints on mux under prefix:
GET {prefix}/cache/tree?prefix=&max=
GET {prefix}/cache/key?key=
GET {prefix}/cache/search?q=&limit=
GET {prefix}/cache/stats
DELETE {prefix}/cache/key?key=
DELETE {prefix}/cache/prefix?prefix=
Keys travel in query parameters, not path segments, because cache keys routinely contain ":" and "/". prefix is the leading path segment shared with the rest of the API (e.g. "/api/v1"); pass it without a trailing slash.
Types ¶
type Bound ¶ added in v1.21.34
type Bound struct {
Score float64
Inclusive bool
// Inf selects an unbounded endpoint: -1 for -inf, +1 for +inf, 0 to use
// Score.
Inf int8
}
Bound is one endpoint of a sorted-set score range. It models Redis's three forms: inclusive (12), exclusive ("(12") and unbounded (-inf / +inf).
func Exclusive ¶ added in v1.21.34
Exclusive is the open bound at score s ("(s" in Redis), excluding s itself.
func (Bound) Redis ¶ added in v1.21.34
Redis renders the bound as a ZRANGEBYSCORE-family argument: "-inf"/"+inf" for the unbounded ends, "s" for an inclusive score, "(s" for an exclusive one. It is exported so the valkey adapter in the sibling module renders bounds the same way the in-memory backend interprets them.
type Browser ¶
type Browser interface {
Tree(ctx context.Context, req TreeRequest) (TreeResponse, error)
Key(ctx context.Context, key string) (KeyDetail, error)
Search(ctx context.Context, req SearchRequest) (SearchResponse, error)
Stats(ctx context.Context) (Stats, error)
DeleteKey(ctx context.Context, key string) (DeleteResponse, error)
DeletePrefix(ctx context.Context, prefix string) (DeleteResponse, error)
}
Browser is the backend contract the HTTP handler serves. All keys and prefixes are logical: implementations strip any physical namespace prefix before returning and re-apply it on lookups.
type DeleteResponse ¶
type DeleteResponse struct {
Deleted int64 `json:"deleted"`
}
DeleteResponse reports how many keys a delete removed.
type KeyDetail ¶
type KeyDetail struct {
Key string `json:"key"`
Type string `json:"type"`
TTLSeconds int64 `json:"ttlSeconds"`
// Bytes is MEMORY USAGE when supported, otherwise omitted.
Bytes int64 `json:"bytes,omitempty"`
// Length is the full value length: STRLEN, HLEN, LLEN, SCARD or ZCARD.
Length int64 `json:"length"`
Value string `json:"value,omitempty"`
Fields map[string]string `json:"fields,omitempty"`
Items []string `json:"items,omitempty"`
Members []ScoredMember `json:"members,omitempty"`
// Truncated is set when the returned value was capped (string bytes or
// collection items); Length still reports the full size.
Truncated bool `json:"truncated,omitempty"`
}
KeyDetail is the full detail for a single key. Exactly one of Value, Fields, Items, Members is populated according to Type.
type ScoredMember ¶
ScoredMember is one zset member with its score.
type SearchRequest ¶
type SearchRequest struct {
Query string
// Limit caps the number of returned keys. Zero uses the backend default.
Limit int
}
SearchRequest is a substring search over the whole keyspace.
type SearchResponse ¶
type SearchResponse struct {
Keys []TreeNode `json:"keys"`
Truncated bool `json:"truncated,omitempty"`
}
SearchResponse lists matching keys as leaf nodes.
type Stats ¶
type Stats struct {
Keys int64 `json:"keys"`
// KeysTruncated is set when Keys was counted by a capped scan (namespaced
// keyspaces) and is therefore a lower bound.
KeysTruncated bool `json:"keysTruncated,omitempty"`
UsedMemoryBytes int64 `json:"usedMemoryBytes,omitempty"`
MaxMemoryBytes int64 `json:"maxMemoryBytes,omitempty"`
Hits int64 `json:"hits,omitempty"`
Misses int64 `json:"misses,omitempty"`
EvictedKeys int64 `json:"evictedKeys,omitempty"`
ExpiredKeys int64 `json:"expiredKeys,omitempty"`
ConnectedClients int64 `json:"connectedClients,omitempty"`
Version string `json:"version,omitempty"`
UptimeSeconds int64 `json:"uptimeSeconds,omitempty"`
InfoError string `json:"infoError,omitempty"`
}
Stats is a whole-keyspace overview. Keys is always populated; the server-info fields are populated only when the backend's INFO command succeeded — when it failed or is unsupported, InfoError carries the reason so the caller can render the gap honestly instead of showing zeros.
type Store ¶ added in v1.21.34
type Store interface {
// Set writes value at key. A ttl <= 0 persists the key with no expiry; a
// positive ttl sets an expiry, replacing any existing one.
Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
// Get returns the value at key, or ErrKeyNotFound if the key is absent or
// expired. Any other error is transient (backend unavailable, decode, …) and
// must be distinguishable from a genuine miss — callers prune index entries
// only on ErrKeyNotFound.
Get(ctx context.Context, key string) ([]byte, error)
// Del removes key (of any type). Removing a missing key is not an error.
Del(ctx context.Context, key string) error
// Expire sets a ttl on an existing key of any type; it does not change the
// value. A missing key is a no-op.
Expire(ctx context.Context, key string, ttl time.Duration) error
// ZAdd inserts or updates member with score in the sorted set at key. It
// does not affect any TTL already set on the key.
ZAdd(ctx context.Context, key string, score float64, member string) error
// ZRem removes member from the sorted set at key.
ZRem(ctx context.Context, key, member string) error
// ZRevRange returns members ranked by score high→low over the inclusive rank
// range [start, stop]; negative indices count from the end (Redis
// semantics). Used for newest-first indexes.
ZRevRange(ctx context.Context, key string, start, stop int64) ([]string, error)
// ZRangeByScore returns members whose score falls within [lower, upper],
// ascending by score.
ZRangeByScore(ctx context.Context, key string, lower, upper Bound) ([]string, error)
// ZRemRangeByScore removes members whose score falls within [lower, upper].
ZRemRangeByScore(ctx context.Context, key string, lower, upper Bound) error
// ZRemRangeByRank removes members in the inclusive rank range [start, stop],
// ranked by score low→high; negative indices count from the end. Trimming a
// sorted set to its newest N members is ZRemRangeByRank(key, 0, -(N+1)).
ZRemRangeByRank(ctx context.Context, key string, start, stop int64) error
}
Store is a minimal, Redis-shaped key/value surface: enough string and sorted-set commands to back clicky's domain stores (prompt.Store, metrics.Timeseries) without exposing a full client. The in-process backend (NewMemory) lives here in the dependency-free root module; the valkey-backed backend (valkey.NewStore) lives in the sibling submodule. Domain stores are written once against this interface and run unchanged on either backend.
Keys are opaque and fully qualified: Store applies no namespacing. Callers own key naming (e.g. prompt builds "prefix:prompt:<id>"). Implementations must be safe for concurrent use.
type TreeNode ¶
type TreeNode struct {
// Name is the display segment: the next path segment for groups, the
// remaining key tail for leaves.
Name string `json:"name"`
// Prefix is set on group nodes only: the full logical prefix including
// the trailing separator, ready to feed back into TreeRequest.Prefix.
Prefix string `json:"prefix,omitempty"`
// Key is set on leaf nodes only: the full logical key.
Key string `json:"key,omitempty"`
// Keys is the total number of keys under this node (1 for a leaf).
Keys int `json:"keys"`
// Children is the number of distinct child segments under a group node.
Children int `json:"children,omitempty"`
// Type is the value type of a leaf: string|hash|list|set|zset|stream.
Type string `json:"type,omitempty"`
// TTLSeconds is the leaf's remaining TTL; -1 means no expiry.
TTLSeconds int64 `json:"ttlSeconds,omitempty"`
// Bytes is the MEMORY USAGE of the node when the server supports it: the
// leaf's own size, or for a group the aggregated size of every key
// beneath it.
Bytes int64 `json:"bytes,omitempty"`
}
TreeNode is one entry in a tree level: either a group of keys sharing the next path segment, or a single leaf key.
type TreeRequest ¶
type TreeRequest struct {
// Prefix is the logical prefix to expand, e.g. "tx:". Empty expands the
// root. It must include the trailing separator when pointing at a group.
Prefix string
// MaxChildren caps the number of nodes returned for the level. Zero uses
// the backend default.
MaxChildren int
}
TreeRequest asks for one level of the key tree under Prefix. Keys are split on the backend's separator (":" for valkey); each distinct next segment becomes either a group node (more segments follow) or a leaf (an actual key).
type TreeResponse ¶
type TreeResponse struct {
Prefix string `json:"prefix"`
Nodes []TreeNode `json:"nodes"`
// Keys is the total number of keys scanned under Prefix, including keys
// rolled up into group nodes.
Keys int `json:"keys"`
// Truncated is set when the node list or the underlying scan hit a cap;
// counts are then lower bounds.
Truncated bool `json:"truncated,omitempty"`
// BytesSupported reports whether per-key byte sizes were available
// (MEMORY USAGE); when false every Bytes field is absent, not zero.
BytesSupported bool `json:"bytesSupported"`
}
TreeResponse is one expanded tree level.