Documentation
¶
Overview ¶
Package slots produces Redis Cluster keys whose slots are pre-planned to distribute evenly across the cluster's nodes.
A KeyGenerator is constructed with a hashtag template (a prefix and a suffix) and a TopologySource describing the cluster shape. At construction time it precomputes N*K hashtag strings of the form
{prefix<value>suffix}
such that the K slots within each node are distinct and one bucket lands on each of the N nodes. SlottedKey(seed) then deterministically maps a seed (typically a user ID or another stable identifier) to one of those precomputed hashtags via FNV-1a, so callers can build the full key as
key := gen.SlottedKey(userID) + userID
and trust that reads and writes for the same userID always route to the same slot.
The motivation is that CRC16 collides adjacent integers more often than callers expect: with a 3-node equal-split cluster, the keys {key:version0} and {key:version2} both hash onto the same node, so a naive 0..N-1 sequence does not spread load.
Index ¶
Examples ¶
Constants ¶
const SlotCount = 16384
SlotCount is the total number of hash slots in a Redis Cluster.
Variables ¶
This section is empty.
Functions ¶
func Slot ¶
Slot returns the Redis Cluster slot for s, treating s as already-extracted hashtag content. Equivalent to CRC16(s) mod 16384.
func SlotForKey ¶
SlotForKey returns the cluster slot for a full Redis key, applying Redis's hashtag extraction rule: if the key contains "{...}" with a non-empty body, the slot is computed over that body; otherwise it's computed over the whole key.
Types ¶
type ClusterShardsClient ¶
type ClusterShardsClient interface {
ClusterShards(ctx context.Context) *redis.ClusterShardsCmd
}
ClusterShardsClient is the slice of *redis.ClusterClient that FromClusterClient needs. It exists so tests can supply a fake without depending on a live cluster.
type KeyGenerator ¶
type KeyGenerator struct {
// contains filtered or unexported fields
}
KeyGenerator returns hashtagged Redis Cluster keys whose slots are pre-planned to spread evenly across the cluster's nodes. Construct one with NewKeyGenerator and call SlottedKey for each operation.
func NewKeyGenerator ¶
func NewKeyGenerator(prefix, suffix string, src TopologySource) (*KeyGenerator, error)
NewKeyGenerator resolves the topology and precomputes N*K hashtag strings of the form "{prefix<value>suffix}" such that the resulting slots are distributed one per node K times, with all K slots on a node distinct.
Returns an error if the topology source fails, if the topology is empty or has a non-positive SlotsPerNode, or if the search cannot find enough distinct slots within the search budget.
Example ¶
Builds a KeyGenerator for a 3-node cluster (one slot per node) and shows that the same seed always returns the same hashtag — so a caller that does gen.SlottedKey(userID) + userID can read back what they wrote.
package main
import (
"fmt"
"sort"
"github.com/primandproper/platform-go/cache/redis/slots"
)
func main() {
gen, err := slots.NewKeyGenerator("example:v1:", "", slots.FromClusterConfig(3, 1))
if err != nil {
panic(err)
}
tags := gen.Hashtags()
sort.Strings(tags)
fmt.Println("precomputed:", tags)
const user = "alice"
first := gen.SlottedKey(user) + user
second := gen.SlottedKey(user) + user
fmt.Printf("%s key: %s\n", user, first)
fmt.Printf("stable across calls: %t\n", first == second)
}
Output: precomputed: [{example:v1:0} {example:v1:1} {example:v1:2}] alice key: {example:v1:1}alice stable across calls: true
func (*KeyGenerator) Hashtags ¶
func (g *KeyGenerator) Hashtags() []string
Hashtags returns a copy of the precomputed hashtag strings, in node-major order. Exposed mainly for debugging and tests; callers wiring up real workloads should reach for SlottedKey.
func (*KeyGenerator) SlottedKey ¶
func (g *KeyGenerator) SlottedKey(seed string) string
SlottedKey returns one of the precomputed hashtagged keys. The choice is deterministic in seed: identical seeds always map to the same hashtag (and therefore the same slot), so callers can build the full key as
key := gen.SlottedKey(userID) + userID
and trust that subsequent reads for the same userID land on the same node.
type NodeSlots ¶
type NodeSlots []SlotRange
NodeSlots is the set of ranges owned by a single cluster node. A node may own multiple non-contiguous ranges after rebalancing.
type Topology ¶
Topology describes the cluster shape a KeyGenerator should plan against: per-node slot ownership and the desired number of distinct slots per node.
type TopologySource ¶
TopologySource is a deferred lookup of the cluster's slot layout. It is invoked once by NewKeyGenerator. The deferral lets callers express both static and dynamic topologies in a single expression without separately handling the lookup error at the call site.
func FromClusterClient ¶
func FromClusterClient(ctx context.Context, c ClusterShardsClient, slotsPerNode int) TopologySource
FromClusterClient returns a TopologySource that queries CLUSTER SHARDS against the given client at NewKeyGenerator time. The resulting Topology reflects whatever slot layout the cluster actually reports, including rebalanced or non-contiguous shards.
func FromClusterConfig ¶
func FromClusterConfig(nodeCount, slotsPerNode int) TopologySource
FromClusterConfig returns a TopologySource that assumes the cluster owns SlotCount slots evenly split across nodeCount nodes — the layout produced by `redis-cli --cluster create` on a fresh cluster. Useful for tests, local development, and any environment where the cluster is known to be in its default shape.