Help us shape the future of Go! Take the 2026 Go Developer Survey

cache

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 19, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package cache provides caching utilities for the foundation layer.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AccessFingerprint

func AccessFingerprint(scope ...string) string

AccessFingerprint hashes an access SCOPE — the exact resolved-context values the query's authz predicate applies (e.g. the accessible-workspace-set + clearance, or an org id) — into a compact fingerprint. It is order-insensitive (the scope is a set: the parts are sorted first), so the same access hashes equally regardless of argument order, and ANY change to the scope rotates the fingerprint — which is how an authz change invalidates cached entries (by key rotation, never reach-in). The caller MUST pass the same values the WHERE clause applies (never raw client input), so the key can never be spoofed narrower than the real access boundary.

func CountGenKey

func CountGenKey(tenant, resource string) string

CountGenKey is the key holding the current generation token for a (tenant, resource)'s count cache. A resource write rotates the token stored here (one Set), coarse-busting the whole bucket in a single O(1) operation. tenant and resource are identifiers with no ':' so the readable form is unambiguous.

func CountKey

func CountKey(tenant, resource, generation, accessFP, filterHash string) string

CountKey builds the total_count cache key: the "count:" namespace then a digest of (tenant, resource, generation, access_fp, filter_hash). The generation component is what makes a coarse bust O(1): rotating the generation (see CountGenKey) changes every count key for the (tenant, resource) at once, orphaning all prior entries across every access_fp/filter_hash.

func Decode

func Decode[T any](data []byte, version int) (val T, ok bool, err error)

Decode deserializes bytes into a versioned envelope. Returns (value, true, nil) on success, (zero, false, nil) on version mismatch (stale-schema miss), and (zero, false, err) on corrupt/invalid JSON.

func Encode

func Encode[T any](value T, version int) ([]byte, error)

Encode serializes value into a versioned envelope as JSON bytes.

func FilterHash

func FilterHash(filter types.Filter) string

FilterHash hashes a list-query filter into a stable key component, reusing the query-fingerprint (filter-only — no sort). Two structurally-identical filters hash equally (so a total is reused across page flips for one filter); a different filter hashes differently. The encoding is order-preserving, so two SEMANTICALLY-equal but differently-ordered composite filters hash differently — a cache-hit-rate cost, never a correctness bug (a distinct key is still scoped correctly).

func ReadThrough

func ReadThrough[T any](
	ctx context.Context,
	c interfaces.ByteCache,
	sf *singleflight.Group,
	key string,
	ttl time.Duration,
	version int,
	load func() (T, error),
) (T, error)

ReadThrough is the shared read-through (cache-aside) primitive for a value of type T, folding the versioned envelope (Encode/Decode) and single-flight (a stampede of concurrent misses on one key collapses to ONE load) into one reuse point for every SCOPED cache decorator (suggest, count — ). Its shape mirrors the repos-tier caching decorator, which predates this primitive and still holds its own copy of the envelope + stampede logic; consolidating that decorator onto ReadThrough is a future DRY cleanup, tracked separately from.

Flow: a version-matching hit returns the decoded value WITHOUT calling load; otherwise a single load (per key, across concurrent callers) runs, its result is encoded and stored with ttl, and returned. A load error propagates and nothing is cached (a cache Get/Set failure degrades to the live load, never an error).

T is stored and returned BY VALUE (the envelope round-trips a value), so concurrent callers that share the single-flight result never alias a mutable pointer — pass a value type (e.g. types.Page[...] or a small count struct), not a pointer.

func SuggestKey

func SuggestKey(tenant, resource, field, accessFP, prefix string, limit int) string

SuggestKey builds the value-suggestion cache key: the "suggest:" namespace then a digest of (tenant, resource, field, access_fp, prefix, limit). The limit is part of the key because the cached page is limit-bounded — two callers with the same prefix but different limits must NOT share an entry (a smaller-limit caller would otherwise read a larger-limit page, or vice-versa). The user-controlled prefix is folded into the digest, so it can never inject a delimiter to collide with another key.

Types

type Envelope

type Envelope[T any] struct {
	// Data is the cached value payload.
	Data T `json:"data"`

	// CachedAt records when the envelope was written, for observability and TTL reasoning.
	CachedAt time.Time `json:"cached_at"`

	// Version is the schema version the value was encoded with; a mismatch on
	// Decode is treated as a miss so stale-schema entries self-evict.
	Version int `json:"version"`
}

Envelope wraps a cached value with metadata for version-aware caching. When the envelope's version doesn't match the expected version, the cached entry is treated as a miss — stale-schema entries are silently evicted.

Serialization is JSON, deliberately, not proto/binary (#15): T is an arbitrary type parameter (repository entities: Ent models, domain structs), NOT a proto.Message, so proto.Marshal cannot encode it. The identity CachingClient (apps/go/server/api/clients/identity/caching.go) uses proto only because it caches a *concrete* proto response type; that pattern does not generalize to Envelope[T]. The companion cursor codec (repos/repository/cursor.go) drops reflection where it can (a fixed 2-field shape); the generic payload here cannot, and a binary-header-plus-JSON-payload variant would churn the cache wire format on a hot read path for a Low-rated, version-bump-only gain — so JSON stays.

Directories

Path Synopsis
Package decorators provides cache decorators for cross-cutting concerns.
Package decorators provides cache decorators for cross-cutting concerns.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL