store

package
v0.85.0 Latest Latest
Warning

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

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package store defines the backend's document datastore: a composite-key (pk/sk) record store with optimistic-concurrency conditional writes, prefix queries, TTL, and atomic counters. The shape is the portable intersection of serverless document stores -- DynamoDB first (store/dynamo), with Firestore/Cosmos-style stores implementable later. Deliberately absent: transactions and secondary indexes. Services compose conditional puts instead of transactions and maintain explicit index items (e.g. a STATUS#<status> partition mirroring an aggregate's status); aggregates are the source of truth and index items are repairable.

Single-table layout used by the backend services (one partition family per line; sk shapes are owned by each service):

WORK#<workId>    / SUGG#<scheme>#<term>#<ADD|REMOVE>   suggestion aggregates
WORK#<workId>    / SUPP#... (TTL)                      supporter dedup markers
WORK#<workId>    / REJ#<scheme>#<term>                 tombstones
STATUS#<status>  / <ts>#<workId>#...                   review-queue index items
FOLK#<normTerm>  / TERM                                folksonomy term lifecycle
AUDIT#<YYYY-MM>  / <ts>#<id>                           audit entries
RATE#<ipHash>    / <window> (counters, TTL)            rate limiting
USER#<email>     / PROFILE|CRED|ROLE#...               local users
DRAFT#<user>     / <draftId>                           editor drafts
JOB#EXPORT       / <jobId>                             export jobs
LEASE#ingest     / LOCK (TTL)                          advisory ingest lease

Index

Constants

This section is empty.

Variables

View Source
var ErrConditionFailed = errors.New("store: condition failed")

ErrConditionFailed reports that a conditional Put or Delete lost its race. Callers recover by re-reading the record and retrying.

View Source
var ErrNotFound = errors.New("store: not found")

ErrNotFound reports that no record exists at the requested key.

Functions

This section is empty.

Types

type Cond

type Cond int

Cond selects a Put/Delete precondition.

const (
	// CondNone applies the write unconditionally.
	CondNone Cond = iota
	// CondIfAbsent succeeds only if no record exists at the key.
	CondIfAbsent
	// CondIfVersion succeeds only if the stored record's Version equals the
	// given Record.Version (for Put/Delete of an existing record) or, when
	// Record.Version is 0, if no record exists (create).
	CondIfVersion
)

type Key

type Key struct {
	PK string
	SK string
}

Key is a record's composite key: PK selects the partition, SK orders and addresses records within it. Both must be non-empty.

type Mem

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

Mem is an in-memory Store with exact conditional semantics and strict TTL (expired records are invisible immediately) -- the reference implementation for tests and local development.

func NewMem

func NewMem() *Mem

NewMem returns an empty Mem.

func (*Mem) Delete

func (m *Mem) Delete(ctx context.Context, r Record, cond Cond) error

Delete removes the record at r.Key subject to cond.

func (*Mem) Get

func (m *Mem) Get(ctx context.Context, k Key) (Record, error)

Get returns the record at k, or ErrNotFound.

func (*Mem) Increment

func (m *Mem) Increment(ctx context.Context, k Key, delta int64, expireAt time.Time) (int64, error)

Increment atomically adds delta to the counter at k.

func (*Mem) Put

func (m *Mem) Put(ctx context.Context, r Record, cond Cond) (Record, error)

Put writes r subject to cond.

func (*Mem) Query

func (m *Mem) Query(ctx context.Context, pk, skPrefix string, opt QueryOpt) iter.Seq2[Record, error]

Query yields the partition's records in SK order.

func (*Mem) SetClock

func (m *Mem) SetClock(now func() time.Time)

SetClock overrides the store's clock; tests use it to step through TTL windows deterministically.

type QueryOpt

type QueryOpt struct {
	// Descending reverses the SK order (newest-first for timestamp SKs).
	Descending bool
	// Limit caps the number of records yielded; 0 means no cap.
	Limit int
	// StartAfter resumes a paginated query strictly after this SK.
	StartAfter string
}

QueryOpt tunes a Query.

type Record

type Record struct {
	Key      Key
	Data     []byte
	Version  int64
	ExpireAt time.Time
}

Record is one stored document. Data is an opaque payload (services JSON- encode their domain types). Version is the optimistic-concurrency token: it is 0 for a record that has never been stored and increments on every successful Put. A zero ExpireAt means no TTL; expiry is lazy in stores with native TTL (DynamoDB deletes within ~a day), so services must treat ExpireAt as a floor on invisibility, not an exact deadline.

type Store

type Store interface {
	// Get returns the record at k, or ErrNotFound.
	Get(ctx context.Context, k Key) (Record, error)
	// Put writes r subject to cond and returns the stored record with its
	// incremented Version. Violated conditions return ErrConditionFailed.
	Put(ctx context.Context, r Record, cond Cond) (Record, error)
	// Delete removes the record at r.Key subject to cond (CondIfVersion
	// compares r.Version). Deleting a missing record returns ErrNotFound.
	Delete(ctx context.Context, r Record, cond Cond) error
	// Query yields the partition pk's records whose SK starts with skPrefix,
	// in SK order (reversed by opt.Descending).
	Query(ctx context.Context, pk, skPrefix string, opt QueryOpt) iter.Seq2[Record, error]
	// Increment atomically adds delta to the counter at k and returns the
	// new value, creating the counter at delta if absent. A non-zero
	// expireAt sets the counter's TTL. Counter keys must not collide with
	// record keys.
	Increment(ctx context.Context, k Key, delta int64, expireAt time.Time) (int64, error)
}

Store is the document datastore. Implementations must be safe for concurrent use.

Directories

Path Synopsis
Package dynamo implements store.Store on a single DynamoDB table (hash key "pk", range key "sk", TTL attribute "expireAt").
Package dynamo implements store.Store on a single DynamoDB table (hash key "pk", range key "sk", TTL attribute "expireAt").
Package storetest is the conformance suite every store.Store implementation must pass.
Package storetest is the conformance suite every store.Store implementation must pass.

Jump to

Keyboard shortcuts

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