Cache API

Piko's cache provides in-memory, distributed, and multilevel caching with optional compression, encryption, and full-text search. Each cache instance carries generic key and value parameters and sits inside a namespace. For the design rationale see about caching. For task recipes see how to cache. Source file: wdk/cache/facade.go.

Service

FunctionReturns
cache.NewService(defaultProviderName string) ServiceConstructs a new service.
cache.GetDefaultService() (Service, error)Returns the service the bootstrap built.
cache.RegisterProviderFactory(name, factory)Registers a domain-specific factory. Use in init().

Cache construction

func CreateNamespace[K comparable, V any](ctx context.Context, service Service, providerName, namespace string, options Options[K, V]) (Cache[K, V], error)
func NewCache[K comparable, V any](service Service, options Options[K, V]) (Cache[K, V], error)
func NewCacheFromDefault[K comparable, V any](options Options[K, V]) (Cache[K, V], error)
func NewCacheBuilder[K comparable, V any](service Service) (*Builder[K, V], error)
func NewCacheBuilderFromDefault[K comparable, V any]() (*Builder[K, V], error)

CreateNamespace is the recommended entry point. It maps one namespace per value type, which isolates entries and keeps key spaces from colliding. NewCacheBuilderFromDefault is the fluent shortcut when Piko is already bootstrapped.

Note: Treat one namespace as one value type. Sharing a namespace across User and Product causes their key spaces to overlap, so a User{ID:42} collides with a Product{ID:42}.

Builder fluent API

The builder returned from NewCacheBuilder chains configuration before Build(ctx). Method names have no With prefix:

myCache, err := builder.
    Provider("otter").
    Namespace("products").
    MaximumSize(10000).
    Compression().
    Build(ctx)

Builder method groups:

GroupMethods
Provider / sourceProvider(name), Namespace(ns), FactoryBlueprint(name), MultiLevel(l1, l2), L1Options(any), L2Options(any), L2CircuitBreaker(maxFailures, openTimeout), Options(any)
CapacityMaximumSize(int), MaximumWeight(uint64), InitialCapacity(int), Weigher(func(K, V) uint32)
TransformersTransformer(name, configs...), Compression(), Encryption(), EncryptionWithService(any)
EncodersEncoder(AnyEncoder), TypedEncoder(EncoderPort[V]), DefaultEncoder(AnyEncoder)
TimeExpiration(time.Duration), WriteExpiration(time.Duration), AccessExpiration(time.Duration), ExpiryCalculator(...), RefreshCalculator(...)
HooksOnDeletion(func(DeletionEvent[K, V])), OnAtomicDeletion(func(DeletionEvent[K, V]))
Stats / observabilityStatsRecorder(StatsRecorder), Logger(Logger)
OtherExecutor(func(operation func())), Clock(Clock), Searchable(*SearchSchema)

Build(ctx) is the terminus.

Expiration is a convenience for WriteExpiration (a fixed TTL applied on creation and updates only). AccessExpiration sets a sliding TTL that resets on every access (read, write, compute).

The Cache interface

Cache[K, V] mirrors the maypok86/otter v2 API. Methods grouped by purpose:

Read

MethodPurpose
GetIfPresent(ctx, key) (V, bool, error)Returns the value if present and unexpired; (zero, false, nil) on miss. No loader call.
Get(ctx, key, loader) (V, error)Returns the value, calling loader on miss. Provides built-in stampede protection.
GetEntry(ctx, key) (Entry[K, V], bool, error)Returns an entry snapshot with metadata; resets the access timer.
ProbeEntry(ctx, key) (Entry[K, V], bool, error)Same as GetEntry but does NOT reset the access timer. Useful for monitoring.
All() iter.Seq2[K, V]Iterator over every key-value pair.
Keys() iter.Seq[K]Iterator over keys.
Values() iter.Seq[V]Iterator over values.

Write

MethodPurpose
Set(ctx, key, value, tags...) errorStores with optional tag groups.
SetWithTTL(ctx, key, value, ttl, tags...) errorPer-entry TTL override.
Compute(ctx, key, fn) (V, bool, error)Atomic update. fn receives (oldValue, found) and returns (newValue, ComputeAction).
ComputeIfAbsent(ctx, key, fn) (V, bool, error)Compute only when the key is missing.
ComputeIfPresent(ctx, key, fn) (V, bool, error)Compute only when the key exists.
ComputeWithTTL(ctx, key, fn) (V, bool, error)Compute with per-call TTL via ComputeResult[V].
BulkGet(ctx, keys, bulkLoader) (map[K]V, error)Batch retrieval; bulkLoader fills misses.
BulkSet(ctx, items, tags...) errorBatch store. On Redis/Valkey this uses pipeline and the multi-set (MSET) command.

Invalidate

MethodPurpose
Invalidate(ctx, key) errorRemove a single entry.
InvalidateByTags(ctx, tags...) (int, error)Remove every entry tagged with any of tags; returns the count removed. Variadic.
InvalidateAll(ctx) errorRemove every entry.

Refresh

MethodPurpose
Refresh(ctx, key, loader) <-chan LoadResult[V]Asynchronously reload one key. The cache serves the old value until the refresh completes.
BulkRefresh(ctx, keys, bulkLoader)Fire-and-forget batched refresh.

Time

MethodPurpose
SetExpiresAfter(ctx, key, duration) errorUpdate the expiry on an existing entry.
SetRefreshableAfter(ctx, key, duration) errorUpdate the refresh window on an existing entry.

Capacity / stats / lifecycle

MethodPurpose
EstimatedSize() intApproximate entry count.
WeightedSize() uint64Total weight (or the same as EstimatedSize when weights are off).
GetMaximum() uint64 / SetMaximum(size)Read or change the cache's max capacity.
Stats() StatsSnapshot counters (hits, misses, evictions, loads, load failures, size).
Close(ctx) errorRelease resources.

Cache[K, V] also exposes search when you configure the cache with a SearchSchema:

MethodPurpose
Search(ctx, query, *SearchOptions) (SearchResult[K, V], error)Full-text search across TEXT fields.
Query(ctx, *QueryOptions) (SearchResult[K, V], error)Structured filter / sort / pagination without full-text.
SupportsSearch() boolCapability check.
GetSchema() *SearchSchemaReturns the configured schema, or nil.

Search and Query return ErrSearchNotSupported on providers without search support.

Transactional providers

Providers that support multi-operation rollback (data access layer (DAL) transactions) implement the Transactional[K, V] interface in addition to Cache[K, V]:

type Transactional[K, V] interface {
    BeginTransaction(ctx context.Context) TransactionCache[K, V]
}

TransactionCache[K, V] embeds the cache plus Commit(ctx) error and Rollback(ctx) error. Providers that do not implement Transactional still participate in transactions via Piko's generic journal-based fallback.

Deletion-event hook

Use the builder's OnDeletion(func(DeletionEvent[K, V])) (or OnAtomicDeletion(...) for stricter ordering guarantees) to observe cache evictions, replacements, and explicit invalidations. The event's Cause field is one of the Cause* constants below.

Options and builders

TypePurpose
Options[K, V]Settings struct (provider, namespace, max size, TTL, encoder, transformers, stats recorder).
Builder[K, V]Fluent configurator, returns a Cache[K, V] from Build(ctx).
Entry[K, V]Immutable snapshot with key, value, TTL, and metadata.
DeletionEvent[K, V]Payload for OnDeletion / OnAtomicDeletion callbacks.
Loader[K, V]Load(ctx, key) (V, error). Called on miss by Get(ctx, key, loader).
LoaderFunc[K, V]Function adapter implementing Loader.
BulkLoader[K, V]Batch variant of Loader.
BulkLoaderFunc[K, V]Function adapter implementing BulkLoader.
LoadResult[V]Async load/refresh outcome.
ComputeResult[V]Compute outcome with optional TTL override.
ExpiryCalculator[K, V]Custom TTL resolver per entry.
RefreshCalculator[K, V]Custom refresh window per entry.
ClockInjectable time source for tests.
LoggerLogger port.
StatsSnapshot (hits, misses, evictions, loads, load failures, size).
StatsRecorderSink for stats events.

Search

Callers declare searchable fields with SearchSchema and FieldSchema, attach the schema to a cache (via builder option or provider config), and query with SearchOptions or QueryOptions.

schema := cache.NewSearchSchema(
    cache.TextField("title"),
    cache.TagField("category"),
    cache.SortableNumericField("price"),
)

Field constructors

FunctionTypePurpose
TextField(name)TEXTTokenised full-text.
TagField(name)TAGExact match.
NumericField(name)NUMERICRange queries.
SortableNumericField(name)NUMERIC sortableRange queries + sort.
SortableTextField(name)TEXT sortableFull-text + sort.
GeoField(name)GEOCoordinate-based queries.
VectorField(name, dimension)VECTORCosine similarity.
VectorFieldWithMetric(name, dimension, metric)VECTORCustom distance metric.
NewSearchSchema(fields...)(constructor)Bundles fields.
NewSearchSchemaWithAnalyser(analyser, fields...)(constructor)Bundles fields with a TextAnalyseFunc for stemming, stop words, normalisation.

Field type constants

FieldTypeText, FieldTypeTag, FieldTypeNumeric, FieldTypeGeo, FieldTypeVector.

Filter constructors

FunctionOperation
Eq(field, value)Field equals value.
Ne(field, value)Field does not equal value.
Gt(field, value)Greater than.
Ge(field, value)Greater than or equal.
Lt(field, value)Less than.
Le(field, value)Less than or equal.
In(field, values...)Field value in set.
Between(field, min, max)Inclusive range.
Prefix(field, prefix)Prefix match on TAG field.

Filter op constants

FilterOpEq, FilterOpNe, FilterOpGt, FilterOpGe, FilterOpLt, FilterOpLe, FilterOpIn, FilterOpBetween, FilterOpPrefix.

Sort order

SortAsc, SortDesc.

Search types

TypePurpose
SearchOptionsFull-text search configuration.
QueryOptionsStructured query without full-text.
SearchResult[K, V]Result set with total count and hits.
SearchHit[K, V]Single hit (key, value, score, matched fields).
FilterFilter condition (built via the filter constructors).
TextAnalyseFuncText analyser for stemming and tokenisation.

ErrSearchNotSupported surfaces when a provider does not ship search.

Transformers

Transformers apply compression or encryption to cached values. They chain, so the cache can compress a value then encrypt it before storage.

ConstantMeaning
TransformerCompressionCompression transformer.
TransformerEncryptionEncryption transformer.
TransformerCustomUser-supplied transformer.
TypePurpose
TransformerPortInterface (Transform, Reverse).
TransformerTypeEnum of the constants above.
TransformConfigAttachment point on Options / builder.

Shipped implementations:

Sub-packageRole
cache_transformer_zstdZstandard compression.
cache_transformer_cryptoAES-GCM encryption.

Chain order: Compression().Encryption() compresses first, then encrypts.

Deletion causes and compute actions

ConstantMeaning
CauseInvalidationExplicit delete or tag invalidation.
CauseReplacementValue overwritten by a later Set.
CauseOverflowEvicted because the cache was full.
CauseExpirationTTL elapsed.
ComputeActionSetCompute (or one of its variants) returned a value to store.
ComputeActionDeleteHandler returned a delete signal.
ComputeActionNoopHandler signalled no change.

Providers

Sub-packageRole
cache_provider_otterIn-memory, generational, uses the TinyLFU eviction policy. Default.
cache_provider_redisRedis via go-redis.
cache_provider_redis_clusterRedis Cluster.
cache_provider_valkeyValkey.
cache_provider_valkey_clusterValkey Cluster.
cache_provider_multilevelL1 plus L2 composition.
cache_provider_mockIn-memory test double.
cache_encoder_gob, cache_encoder_jsonValue encoders.
cache_transformer_crypto, cache_transformer_zstdStream transformers.
cache_linguisticsText analyser used by search.

Encoding

Some providers (Redis, Valkey) need values serialised. The encoding registry accepts a default encoder and type-specific overrides:

registry := cache.NewEncodingRegistry(defaultEncoder)

Types: EncoderPort[V], AnyEncoder, EncodingRegistry.

Bootstrap options

OptionPurpose
piko.WithCacheProvider(name, provider)Registers a provider under a name.
piko.WithDefaultCacheProvider(name)Marks a registered provider as default.
piko.WithCacheService(service)Registers a fully configured service.

Errors

A Loader returns ErrNotFound when a value is missing from the data source. Cache instances return ErrSearchNotSupported on providers without search.

See also