data

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package data provides typed Redis hashes, sets, sorted sets and lists. Declare contracts once and bind them to an explicitly owned adapter. Constructors perform no I/O. These data structures are independent of cache invalidation and cache envelopes.

Index

Constants

View Source
const (
	MaxEntries    = 4096
	MaxReplyBytes = 16 << 20
	MaxBatchKeys  = 256
)

Variables

This section is empty.

Functions

func IncrementField

func IncrementField[K, F any](ctx context.Context, h Hash[K, F, int64], key K, field F, delta int64) (int64, error)

IncrementField atomically adds a signed delta to an int64-valued hash field and returns the result. A missing field starts at zero; existing expiry is retained. A stored value that is not a canonical integer, or overflow, fails without mutation. It is a function because only int64 hashes support it.

func ValidateBatch

func ValidateBatch(keys []Key) error

ValidateBatch requires a canonical, bounded batch within one namespace.

func ValidateIndexRange

func ValidateIndexRange(start, stop int64) error

ValidateIndexRange bounds list indices; negative values count from the tail.

func ValidateScore

func ValidateScore(score float64) error

ValidateScore rejects NaN, which Redis cannot order.

Types

type Backend

type Backend interface {
	DataExists(context.Context, Key) (bool, error)
	DataExpire(context.Context, Key, cache.TTL) (bool, error)
	DataDeleteMany(context.Context, []Key) (uint64, error)
	DataCount(context.Context, Key, Limits) (uint64, error)
}

Backend owns data entry operations. Validate every key and complete batch before mutation. DeleteMany is one atomic operation; error returns zero and may hide a completed remote write. Never retry uncertain mutations. The caller owns adapter lifecycle; stores borrow it. Expire preserves bytes, missing keys return false, and Forever returns true even for an already persistent key.

type Config

type Config struct {
	Namespace                                                 keyspace.Namespace
	MaxDeclarations, MaxKeyBytes, MaxConcurrent, MaxBatchKeys int
	Timeout                                                   time.Duration
	Limits                                                    Limits
}

func DefaultConfig

func DefaultConfig(ns keyspace.Namespace) Config

func (Config) Validate

func (c Config) Validate() error

type End

type End uint8

End selects the head (Front) or tail (Back) of a list.

const (
	Front End = iota
	Back
)

func (End) Validate

func (e End) Validate() error

type Handle added in v0.3.0

type Handle[K any] struct {
	// contains filtered or unexported fields
}

Handle supplies shared operations for a bound Hash, Set, SortedSet or List. Declaration Bind methods construct the containers with an embedded Handle, promoting Name, Version, Exists, Expire, Count, Delete, DeleteMany and AdapterKey. Its zero value is unbound: Name and Version return zero values, and operations requiring a store return an error.

func (Handle[K]) AdapterKey added in v0.3.0

func (h Handle[K]) AdapterKey(ctx context.Context, input K) (Key, error)

AdapterKey explicitly exports this declaration's resolved address for advanced Redis adapters. It preserves key typing and callback ownership; ordinary callers should prefer the typed Hash/Set operations.

func (Handle[K]) Count added in v0.3.0

func (h Handle[K]) Count(ctx context.Context, key K) (uint64, error)

Count returns the number of fields, members or elements at the resource key.

func (Handle[K]) Delete added in v0.3.0

func (h Handle[K]) Delete(ctx context.Context, key K) (bool, error)

Delete removes the resource key and reports whether it existed.

func (Handle[K]) DeleteMany added in v0.3.0

func (h Handle[K]) DeleteMany(ctx context.Context, keys ...K) (uint64, error)

DeleteMany validates all encoded keys before one atomic batch. The input bound applies before deduplication; returned count excludes duplicates and missing keys.

func (Handle[K]) Exists added in v0.3.0

func (h Handle[K]) Exists(ctx context.Context, key K) (bool, error)

Exists checks the key's declared Redis type, without decoding payloads.

func (Handle[K]) Expire added in v0.3.0

func (h Handle[K]) Expire(ctx context.Context, key K, ttl cache.TTL) (bool, error)

Expire changes expiry without replacing data. New hash/set writes are persistent and later writes retain TTL. Setting expiry is a separate explicit operation; a write followed by Expire does not claim atomic initialization with expiry.

func (Handle[K]) Name added in v0.3.0

func (h Handle[K]) Name() Name

Name returns the bound declaration name, or an empty name for an unbound handle.

func (Handle[K]) Version added in v0.3.0

func (h Handle[K]) Version() Version

Version returns the bound declaration version, or zero for an unbound handle.

type Hash

type Hash[K, F, V any] struct {
	Handle[K]
	// contains filtered or unexported fields
}

Hash is a typed collection of fields at each resource key. Point writes preserve expiry and return whether a field was newly inserted. Values are canonical JSON snapshots; use DTOs that explicitly select model getters for presentation data.

func (Hash[K, F, V]) DeleteField

func (h Hash[K, F, V]) DeleteField(ctx context.Context, key K, field F) (bool, error)

func (Hash[K, F, V]) Get

func (h Hash[K, F, V]) Get(ctx context.Context, key K, field F) (V, bool, error)

func (Hash[K, F, V]) GetAll

func (h Hash[K, F, V]) GetAll(ctx context.Context, key K) ([]HashEntry[F, V], error)

GetAll returns every field of one resource, ordered by encoded field bytes. The declaration must supply WithFieldDecoder; each decoded field must encode back to its stored bytes. Cardinality and reply bounds are checked before the adapter transfers payloads. A missing hash returns an empty slice.

func (Hash[K, F, V]) GetMany

func (h Hash[K, F, V]) GetMany(ctx context.Context, key K, fields ...F) ([]value.Optional[V], error)

GetMany reads several fields of one resource in one atomic operation. The result has one entry per requested field, in input order; missing fields are unset. Repeated fields repeat their value. The field count is bounded by Limits.Entries and the returned payload by Limits.ReplyBytes.

func (Hash[K, F, V]) Set

func (h Hash[K, F, V]) Set(ctx context.Context, key K, field F, input V) (bool, error)

type HashBackend

type HashBackend interface {
	HashGet(context.Context, Key, string, Limits) (string, bool, error)
	HashSet(context.Context, Key, string, string, Limits) (bool, error)
	HashDelete(context.Context, Key, string, Limits) (bool, error)
}

HashBackend supplies point operations. Get distinguishes missing from JSON null. Set returns true only for a new field. Writes retain existing key TTL; a newly created key is persistent. Deleting the last field removes the key. Adapters validate kind, cardinality and requested field/value sizes before I/O or mutation. Stored values are opaque bytes; typed handles validate their JSON.

type HashCounterBackend

type HashCounterBackend interface {
	HashIncrement(context.Context, Key, string, int64, Limits) (int64, error)
}

HashCounterBackend optionally adds an exact signed delta to one integer field. A missing field starts at zero. Non-integer values, overflow and growth past the cardinality bound fail without mutation. Writes retain the key's expiry.

type HashDeclaration

type HashDeclaration[K, F, V any] struct {
	// contains filtered or unexported fields
}

HashDeclaration preserves the resource key, field identity and value types. Keep one declaration per name/version; increment version for schema changes.

func DefineHash

func DefineHash[K, F, V any](name Name, version Version, keys keyspace.Codec[K], fields keyspace.Codec[F]) HashDeclaration[K, F, V]

func (HashDeclaration[K, F, V]) Bind

func (d HashDeclaration[K, F, V]) Bind(s *Store) (Hash[K, F, V], error)

func (HashDeclaration[K, F, V]) WithFieldDecoder

func (d HashDeclaration[K, F, V]) WithFieldDecoder(parse func(string) (F, error)) HashDeclaration[K, F, V]

WithFieldDecoder adds the inverse of the field codec, which Hash.GetAll needs to return typed fields. Every decoded field must encode back to the exact stored bytes. The returned declaration keeps the same name/version identity.

type HashEntry

type HashEntry[F, V any] struct {
	Field F
	Value V
}

HashEntry is one typed field and value read by GetAll.

type HashReadBackend

type HashReadBackend interface {
	HashGetMany(context.Context, Key, []string, Limits) ([]value.Optional[string], error)
	HashGetAll(context.Context, Key, Limits) ([]StoredField, error)
}

HashReadBackend optionally reads several fields in one operation. GetMany returns one entry per requested field, in order; a missing field is unset. GetAll returns every field sorted by field bytes after checking cardinality and the reply budget before transferring payloads.

type Key

type Key struct {
	// contains filtered or unexported fields
}

Key is an opaque adapter address, separate from cache and other feature keys. Application calls preserve their concrete resource types through Hash, Set, SortedSet or List. The kind is part of the address and matches Redis TYPE.

func NewKey

func NewKey(namespace keyspace.Namespace, name Name, version Version, kind Kind, logical string) (Key, error)

func (Key) Kind

func (k Key) Kind() Kind

func (Key) Namespace

func (k Key) Namespace() keyspace.Namespace

func (Key) String

func (k Key) String() string

func (Key) Validate

func (k Key) Validate() error

type Kind

type Kind string
const (
	HashKind      Kind = "hash"
	SetKind       Kind = "set"
	SortedSetKind Kind = "zset"
	ListKind      Kind = "list"
)

type Limits

type Limits struct{ Entries, FieldBytes, ValueBytes, ReplyBytes int }

Limits bound each collection and each encoded field/value. The cardinality times value bound must fit ReplyBytes, so every framework-written set can be enumerated within that reply budget. External writes must obey the same schema. Server allocations while inspecting externally oversized members are not bounded.

func DefaultLimits

func DefaultLimits() Limits

func (Limits) Validate

func (l Limits) Validate() error

func (Limits) ValidateField

func (l Limits) ValidateField(field string) error

func (Limits) ValidateValue

func (l Limits) ValidateValue(text string) error

type List

type List[K, V any] struct {
	Handle[K]
	// contains filtered or unexported fields
}

List preserves resource/element types in insertion order. Its length is bounded by Limits.Entries: a push that would exceed it inserts nothing. New lists persist; writes retain TTL. Removing the last element removes it.

func (List[K, V]) PopBack

func (l List[K, V]) PopBack(ctx context.Context, key K) (V, bool, error)

PopBack removes and returns the tail element, or false for an empty list.

func (List[K, V]) PopFront

func (l List[K, V]) PopFront(ctx context.Context, key K) (V, bool, error)

PopFront removes and returns the head element, or false for an empty list.

func (List[K, V]) Push

func (l List[K, V]) Push(ctx context.Context, key K, values ...V) (uint64, error)

Push appends values at the tail in order and returns the new length.

func (List[K, V]) PushFront

func (l List[K, V]) PushFront(ctx context.Context, key K, values ...V) (uint64, error)

PushFront inserts values at the head one at a time, like Redis LPUSH, so the last argument becomes the first element. It returns the new length.

func (List[K, V]) Range

func (l List[K, V]) Range(ctx context.Context, key K, start, stop int64) ([]V, error)

Range returns elements from start through stop inclusive, using Redis index semantics (negative indices count from the tail). The reply is bounded by Limits.Entries and Limits.ReplyBytes; a missing list returns an empty slice.

func (List[K, V]) Trim

func (l List[K, V]) Trim(ctx context.Context, key K, start, stop int64) error

Trim keeps only elements from start through stop inclusive (Redis LTRIM).

type ListBackend

type ListBackend interface {
	ListPush(context.Context, Key, []string, End, Limits) (uint64, error)
	ListPop(context.Context, Key, End, Limits) (string, bool, error)
	ListRange(context.Context, Key, int64, int64, Limits) ([]string, error)
	ListTrim(context.Context, Key, int64, int64, Limits) error
}

ListBackend supplies bounded list operations. Push checks the resulting length against Limits.Entries before inserting anything and returns the new length. Pop checks the element's size before removing it. Range/Trim use Redis index semantics (negative indices count from the tail). Writes preserve TTL, new lists are persistent and removing the last element removes the key.

type ListDeclaration

type ListDeclaration[K, V any] struct {
	// contains filtered or unexported fields
}

ListDeclaration preserves resource and element types. Elements are canonical JSON snapshots in insertion order; duplicates are allowed.

func DefineList

func DefineList[K, V any](name Name, version Version, keys keyspace.Codec[K]) ListDeclaration[K, V]

func (ListDeclaration[K, V]) Bind

func (d ListDeclaration[K, V]) Bind(s *Store) (List[K, V], error)

type Name

type Name string

type Order

type Order uint8

Order selects ascending or descending score order for sorted-set reads.

const (
	Ascending Order = iota
	Descending
)

func (Order) Validate

func (o Order) Validate() error

type ScoreRange

type ScoreRange struct {
	Min, Max               float64
	ExcludeMin, ExcludeMax bool
}

ScoreRange bounds sorted-set scores, inclusive unless excluded. Use math.Inf for an open end; AllScores selects every score. NaN is invalid.

func AllScores

func AllScores() ScoreRange

AllScores selects every member regardless of score.

func Between

func Between(min, max float64) ScoreRange

Between selects scores from min through max inclusive.

func (ScoreRange) Validate

func (r ScoreRange) Validate() error

type Scored

type Scored[V any] struct {
	Member V
	Score  float64
}

Scored is one typed member and its score.

type Set

type Set[K, V any] struct {
	Handle[K]
	// contains filtered or unexported fields
}

Set preserves resource/member types and canonical JSON membership identity. Members returns an owned native slice ordered by encoded JSON bytes. This is deterministic, not numeric/domain sorting. New sets persist; writes retain TTL.

func (Set[K, V]) Add

func (s Set[K, V]) Add(ctx context.Context, key K, input V) (bool, error)

func (Set[K, V]) Contains

func (s Set[K, V]) Contains(ctx context.Context, key K, input V) (bool, error)

func (Set[K, V]) Members

func (s Set[K, V]) Members(ctx context.Context, key K) ([]V, error)

func (Set[K, V]) Remove

func (s Set[K, V]) Remove(ctx context.Context, key K, input V) (bool, error)

type SetBackend

type SetBackend interface {
	SetAdd(context.Context, Key, string, Limits) (bool, error)
	SetRemove(context.Context, Key, string, Limits) (bool, error)
	SetContains(context.Context, Key, string, Limits) (bool, error)
	SetMembers(context.Context, Key, Limits) ([]string, error)
}

SetBackend uses exact encoded member equality. Writes preserve TTL, new sets are persistent, and removing the last member removes the key. Members returns sorted, unique, owned strings within the bounds, including an empty slice for missing sets. Every method validates type and cardinality before mutation.

type SetDeclaration

type SetDeclaration[K, V any] struct {
	// contains filtered or unexported fields
}

SetDeclaration preserves resource and member types. Members use canonical JSON equality, rather than Go pointer identity or the spelling of numeric JSON.

func DefineSet

func DefineSet[K, V any](name Name, version Version, keys keyspace.Codec[K]) SetDeclaration[K, V]

func (SetDeclaration[K, V]) Bind

func (d SetDeclaration[K, V]) Bind(s *Store) (Set[K, V], error)

type SortedSet

type SortedSet[K, V any] struct {
	Handle[K]
	// contains filtered or unexported fields
}

SortedSet preserves resource/member types with a float64 score per member. Members use canonical JSON identity (as in Set). Reads order by score, then by encoded member bytes. New sorted sets persist; writes retain TTL.

func (SortedSet[K, V]) Add

func (s SortedSet[K, V]) Add(ctx context.Context, key K, input V, score float64) (bool, error)

Add inserts or re-scores a member and reports whether it was newly added. A full sorted set can re-score existing members but rejects growth.

func (SortedSet[K, V]) CountByScore

func (s SortedSet[K, V]) CountByScore(ctx context.Context, key K, scores ScoreRange) (uint64, error)

CountByScore counts members whose scores fall within scores.

func (SortedSet[K, V]) Increment

func (s SortedSet[K, V]) Increment(ctx context.Context, key K, input V, delta float64) (float64, error)

Increment atomically adds delta to a member's score (a missing member starts at zero) and returns the new score. A NaN result fails without mutation.

func (SortedSet[K, V]) Range

func (s SortedSet[K, V]) Range(ctx context.Context, key K, window Window) ([]Scored[V], error)

Range returns members by rank within window, ordered by score.

func (SortedSet[K, V]) RangeByScore

func (s SortedSet[K, V]) RangeByScore(ctx context.Context, key K, scores ScoreRange, window Window) ([]Scored[V], error)

RangeByScore returns members whose scores fall within scores, ordered and limited by window (Window.Offset skips matching members).

func (SortedSet[K, V]) Rank

func (s SortedSet[K, V]) Rank(ctx context.Context, key K, input V, order Order) (uint64, bool, error)

Rank returns a member's zero-based position in order, or false when missing.

func (SortedSet[K, V]) Remove

func (s SortedSet[K, V]) Remove(ctx context.Context, key K, input V) (bool, error)

func (SortedSet[K, V]) Score

func (s SortedSet[K, V]) Score(ctx context.Context, key K, input V) (float64, bool, error)

Score returns a member's score, distinguishing a missing member.

type SortedSetBackend

type SortedSetBackend interface {
	SortedSetAdd(context.Context, Key, string, float64, Limits) (bool, error)
	SortedSetIncrement(context.Context, Key, string, float64, Limits) (float64, error)
	SortedSetRemove(context.Context, Key, string, Limits) (bool, error)
	SortedSetScore(context.Context, Key, string, Limits) (float64, bool, error)
	SortedSetRank(context.Context, Key, string, Order, Limits) (uint64, bool, error)
	SortedSetRange(context.Context, Key, Window, Limits) ([]StoredMember, error)
	SortedSetRangeByScore(context.Context, Key, ScoreRange, Window, Limits) ([]StoredMember, error)
	SortedSetCountByScore(context.Context, Key, ScoreRange, Limits) (uint64, error)
}

SortedSetBackend orders members by float64 score, then by member bytes, as Redis does. Scores are never NaN. Writes preserve TTL, new sorted sets are persistent and removing the last member removes the key. Range replies are bounded by Window.Count, Limits.Entries and Limits.ReplyBytes before payloads are returned. Every method validates type and cardinality before mutation.

type SortedSetDeclaration

type SortedSetDeclaration[K, V any] struct {
	// contains filtered or unexported fields
}

SortedSetDeclaration preserves resource and member types. Members use canonical JSON identity, as in sets, and each carries a float64 score.

func DefineSortedSet

func DefineSortedSet[K, V any](name Name, version Version, keys keyspace.Codec[K]) SortedSetDeclaration[K, V]

func (SortedSetDeclaration[K, V]) Bind

func (d SortedSetDeclaration[K, V]) Bind(s *Store) (SortedSet[K, V], error)

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store borrows its adapter. Key/JSON/adapter callbacks retain an operation slot until they actually exit, including after cancellation. No detached retries or implicit fallback run. Inputs must stay unchanged until their operation returns.

func NewStore

func NewStore(backend Backend, c Config) (*Store, error)

type StoredField

type StoredField struct{ Field, Value string }

StoredField is one adapter hash field and its encoded value.

type StoredMember

type StoredMember struct {
	Member string
	Score  float64
}

StoredMember is one adapter sorted-set member with its score.

type Version

type Version uint32

type Window

type Window struct {
	Offset, Count int
	Order         Order
}

Window selects at most Count members after skipping Offset, in Order. Count must be positive and within the collection's Limits.Entries.

func First

func First(count int) Window

First returns the first count members in ascending score order.

func Last

func Last(count int) Window

Last returns the first count members in descending score order.

Jump to

Keyboard shortcuts

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