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 ¶
- Variables
- type Cond
- type Dir
- func (d *Dir) Close() error
- func (d *Dir) Delete(ctx context.Context, r Record, cond Cond) error
- func (d *Dir) Get(ctx context.Context, k Key) (Record, error)
- func (d *Dir) Increment(ctx context.Context, k Key, delta int64, expireAt time.Time) (int64, error)
- func (d *Dir) Put(ctx context.Context, r Record, cond Cond) (Record, error)
- func (d *Dir) Query(ctx context.Context, pk, skPrefix string, opt QueryOpt) iter.Seq2[Record, error]
- func (d *Dir) SetClock(now func() time.Time)
- type Key
- type Mem
- func (m *Mem) Delete(ctx context.Context, r Record, cond Cond) error
- func (m *Mem) Get(ctx context.Context, k Key) (Record, error)
- func (m *Mem) Increment(ctx context.Context, k Key, delta int64, expireAt time.Time) (int64, error)
- func (m *Mem) Put(ctx context.Context, r Record, cond Cond) (Record, error)
- func (m *Mem) Query(ctx context.Context, pk, skPrefix string, opt QueryOpt) iter.Seq2[Record, error]
- func (m *Mem) SetClock(now func() time.Time)
- type QueryOpt
- type Record
- type Store
Constants ¶
This section is empty.
Variables ¶
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.
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 Dir ¶ added in v0.202.0
type Dir struct {
// contains filtered or unexported fields
}
Dir is the persistent local document store: the in-memory store fronted by an append-only JSONL journal, replayed (and compacted) on open. It exists so a container/laptop deployment without DynamoDB keeps its moderation queue, promotions, review decisions, audit trail, drafts, and job records across restarts. Write rates on this surface are human-scale, so every append syncs; a crash loses at most the op whose append never landed.
func NewDir ¶ added in v0.202.0
NewDir opens (or creates) the journal-backed store rooted at dir. The journal is replayed into memory and rewritten compacted, so growth is bounded by live data times the churn since the last open.
func (*Dir) Increment ¶ added in v0.202.0
Increment atomically adds delta to the counter at k, journaling the resulting absolute value so replay never re-adds.
type Key ¶
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.
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 ¶
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. |