slots

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: AGPL-3.0 Imports: 6 Imported by: 0

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

View Source
const SlotCount = 16384

SlotCount is the total number of hash slots in a Redis Cluster.

Variables

This section is empty.

Functions

func Slot

func Slot(s string) uint16

Slot returns the Redis Cluster slot for s, treating s as already-extracted hashtag content. Equivalent to CRC16(s) mod 16384.

func SlotForKey

func SlotForKey(key string) uint16

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.

func (NodeSlots) Contains

func (n NodeSlots) Contains(slot uint16) bool

Contains reports whether any range owned by the node contains slot.

type SlotRange

type SlotRange struct {
	Start uint16
	End   uint16
}

SlotRange is an inclusive [Start, End] range of Redis Cluster slots.

func (SlotRange) Contains

func (r SlotRange) Contains(slot uint16) bool

Contains reports whether slot lies within the range.

type Topology

type Topology struct {
	Nodes        []NodeSlots
	SlotsPerNode int
}

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

type TopologySource func() (Topology, error)

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.

Jump to

Keyboard shortcuts

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