Valkey cache provider

Valkey cache provider implementing cache.Provider against a single-node Valkey server, with tag-based invalidation and optional Valkey Search indexing.

Overview

Valkey is the BSD-3-Clause fork of Redis. It speaks the same wire protocol and supports the same commands, so for cache purposes it is a drop-in replacement. The provider wires a Valkey server into the piko cache port using the valkey-go client.

Wiring is two lines plus a config struct. The provider satisfies the cache.Provider port directly, so you register it with piko.WithCacheProvider and piko owns the rest of the lifecycle. Every namespace shares one valkey.Client, and each namespace becomes a key prefix, so adding caches costs no extra connections. The provider reuses piko's stampede protection and optimistic-locking compute machinery instead of reimplementing them.

Reach for Valkey when permissive licensing matters. Reach for Redis when you need feature parity with the latest Redis Stack modules. Reach for Valkey Cluster when you outgrow a single node, and for Otter when in-process speed matters more than cross-replica sharing.

crypto/tls.Config carries optional TLS settings. The Username field accepts a Valkey Access Control List (ACL) username. Leave it empty to use the default user. You must supply Registry.

The provider is pure Go with no build tags and no CGO. It runs unchanged in interpreted dev mode (dev-i) and in compiled builds.

Requirements

  • A reachable Valkey server (single node, see Valkey Cluster for sharded deployments). Any Redis-compatible server (Redis OSS, Valkey, KeyDB) works at the protocol level.
  • Network egress to the configured Address.
  • A cache.EncodingRegistry describing how to encode the value types you cache.

Configuration

import (
    "os"
    "time"

    "piko.sh/piko/wdk/cache"
    "piko.sh/piko/wdk/cache/cache_encoder_json"
    "piko.sh/piko/wdk/cache/cache_provider_valkey"
)

jsonEncoder := cache_encoder_json.New[any]()
registry := cache.NewEncodingRegistry(jsonEncoder.(cache.AnyEncoder))

provider, err := cache_provider_valkey.NewValkeyProvider(cache_provider_valkey.Config{
    Address:          "localhost:6379",             // required: host:port
    Username:         "",                           // ACL username; empty for default user
    Password:         os.Getenv("VALKEY_PASSWORD"), // empty for servers without authentication
    DB:               0,                            // database number passed to SELECT
    Namespace:        "myapp:",                     // global key prefix
    ClientName:       "piko-app",                   // CLIENT SETNAME identifier
    IndexPrefix:      "index:",                     // search index prefix
    DefaultTTL:       1 * time.Hour,                // entry expiry; default 1 hour
    OperationTimeout: 2 * time.Second,              // standard ops; default 2s
    Registry:         registry,                     // required: value encoder registry
    // TLSConfig: &tls.Config{...},                 // optional: enable TLS
})
if err != nil {
    return err
}

cache_encoder_json.New[any]() returns a typed EncoderPort[any]. NewEncodingRegistry takes an AnyEncoder, so assert the encoder to cache.AnyEncoder before you pass it.

NewValkeyProvider pings the server during construction and fails fast if it is unreachable or Registry is nil. The constructor fills any zero-valued timeout or limit with shared piko cache defaults. Only Address and Registry need a value. The TTL defaults to 1 hour, the operation timeout to 2 seconds, the atomic timeout to 5 seconds, and the bulk timeout to 10 seconds. The flush timeout defaults to 30 seconds, the search timeout to 5 seconds, the compute retries to 10, and the index prefix to index:.

AllowUnsafeFLUSHDB is off by default. With it off, InvalidateAll cannot run FLUSHDB, and the call returns an error when Namespace is empty. Enable it only when you own the whole database and accept that FLUSHDB deletes every key, not just cache keys. With a Namespace set, InvalidateAll scans and deletes that prefix instead.

Bootstrap

ssr := piko.New(
    piko.WithCacheProvider("valkey", provider),
    piko.WithDefaultCacheProvider("valkey"),
)

Caching custom types

The provider auto-handles a fixed set of key and value combinations, such as string keys with []byte, string, int, or any values. Cache a custom struct type and creation returns an error directing you to register a factory. Register a blueprint with cache.RegisterProviderFactory that calls cache_provider_valkey.ValkeyProviderFactory for your type before you create that namespace.

See also

Other cache providers:

Cache encoders and transformers:

Framework docs:

External: