docstore

package
v0.26.21 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package docstore — range 0.3.80.* (ADR 0110 service/data/docstore block; ADR 0139 added 16 and 17, ADR 0143 18 to 20 and, for its trash, 21, and ADR 0178 22, for a document written with its versions). Both engines answer every one of them alike, and the range keeps the LL = 3 its engine allocated it under now that it is declared here (ADR 0160).

Package docstore declares the document-store domain: typed, keyed JSON documents with unique and multi-valued secondary indexes, kept by two engines under one contract — the file engine in memory or through core/data/vfs (ADR 0110), the SQL engine in a database the caller owns (ADR 0139), both keeping a document's versions in its own write when asked (ADR 0143).

It holds what the two engines share and nothing only one of them has:

There are two ports and not one because the engines differ on the one thing a single interface would have to fix: the file engine waits on nothing a caller could abandon and takes no context (ADR 0110 §D6), while every call of the SQL engine waits on a database and takes one. A single port would give the file engine a context it cannot honour, or take the SQL engine's away (ADR 0139 §D2). So each port is the contract of one shape of call, with the same methods, the same documents and the same refusals.

The engines, their configurations, the file engine's statistics and every statement live in internal/service/data/docstore.

Package docstore — the siblings of Collection (ADR 0039): a document's versions, its trash, the keys alone (ADR 0178), a document written with its versions as they are (ADR 0178), what the versions weigh, and the same ports with a context on every call, which the SQL engine implements.

Package docstore — declares the sentinel *errs.Error outcomes both engines answer. Each var's name equals its errs.Define Reason in SCREAMING_SNAKE form.

No Public and no Private below carries a key or a document. A store key is routinely an e-mail address or the hash of a token, and an index key is usually exactly the value a caller must not learn back from a refusal: the fields name the index and the operation, never what was looked up.

The HTTP statuses are the sentinels' http: fields in the design, plain integers: the core imports no net/http to read five numbers (ADR 0160, ADR 0176).

Package docstore — the declaration of a secondary index, which both engines take at construction and file by the same rule.

Package docstore — the values both engines read and return: a stored document as JSON, one version of a document, what a write says about the version it makes, a document in the trash, and what the versions weigh.

Index

Constants

View Source
const CodeDocumentExists errs.Code = 0x00_03_50_02 // 0.3.80.2

CodeDocumentExists identifies an insertion under a key the store already holds a document under.

View Source
const CodeDocumentKeyChanged errs.Code = 0x00_03_50_05 // 0.3.80.5

CodeDocumentKeyChanged identifies an update whose function changed the key of the document it was given.

View Source
const CodeDocumentKeyEmpty errs.Code = 0x00_03_50_04 // 0.3.80.4

CodeDocumentKeyEmpty identifies a document whose key function returned the empty string.

View Source
const CodeDocumentNotFound errs.Code = 0x00_03_50_01 // 0.3.80.1

CodeDocumentNotFound identifies a read, an update, a replacement or a deletion of a key the store holds no document under, and a unique-index lookup that finds nobody.

View Source
const CodeDocumentNotTrashed errs.Code = 0x00_03_50_15 // 0.3.80.21

CodeDocumentNotTrashed identifies a restoration or a purge of a key the store holds no trashed document under.

View Source
const CodeDocumentUndecodable errs.Code = 0x00_03_50_08 // 0.3.80.8

CodeDocumentUndecodable identifies a stored document that no longer decodes into the store's type.

View Source
const CodeDocumentUnencodable errs.Code = 0x00_03_50_09 // 0.3.80.9

CodeDocumentUnencodable identifies a value encoding/json cannot encode.

View Source
const CodeIndexBroken errs.Code = 0x00_03_50_0C // 0.3.80.12

CodeIndexBroken identifies stored documents that break a unique index, or an index key function that panicked while the store was opening.

View Source
const CodeIndexNotUnique errs.Code = 0x00_03_50_07 // 0.3.80.7

CodeIndexNotUnique identifies a single-document lookup in an index that may file several documents under one key.

View Source
const CodeIndexUnknown errs.Code = 0x00_03_50_06 // 0.3.80.6

CodeIndexUnknown identifies a lookup in an index the store never declared.

View Source
const CodeKeyTooLong errs.Code = 0x00_03_50_11 // 0.3.80.17

CodeKeyTooLong identifies a write whose store key, or one of whose index keys, is longer than the SQL store's key columns hold.

View Source
const CodeLoadFailed errs.Code = 0x00_03_50_0B // 0.3.80.11

CodeLoadFailed identifies a store whose files could not be read or are not a store's files.

View Source
const CodePersistFailed errs.Code = 0x00_03_50_0A // 0.3.80.10

CodePersistFailed identifies a write the filesystem did not make durable.

View Source
const CodeStatementFailed errs.Code = 0x00_03_50_10 // 0.3.80.16

CodeStatementFailed identifies a call the SQL store's database did not complete: it could not be reached, or it refused a statement for a reason that is not one of the store's own refusals. The driver's error travels beside it, withheld from its text.

View Source
const CodeStoreClosed errs.Code = 0x00_03_50_0D // 0.3.80.13

CodeStoreClosed identifies a call on a store that has been closed.

View Source
const CodeStoreMisconfigured errs.Code = 0x00_03_50_0E // 0.3.80.14

CodeStoreMisconfigured identifies a configuration no store could honour.

View Source
const CodeUniqueKeyTaken errs.Code = 0x00_03_50_03 // 0.3.80.3

CodeUniqueKeyTaken identifies a write a unique index refuses: another document already holds one of its keys in that index.

View Source
const CodeVersionNotFound errs.Code = 0x00_03_50_13 // 0.3.80.19

CodeVersionNotFound identifies a read of a version its document does not keep: never made, or pruned.

View Source
const CodeVersionsInvalid errs.Code = 0x00_03_50_16 // 0.3.80.22

CodeVersionsInvalid identifies a document whose versions a store refuses to write as they are given: none, a number 0, numbers not strictly decreasing, an unknown instant on a version other than 1, a version that is not JSON, or a document whose own key is another (ADR 0178).

View Source
const CodeVersionsNotKept errs.Code = 0x00_03_50_12 // 0.3.80.18

CodeVersionsNotKept identifies a call on a document's versions to a store that keeps none: its configuration's Versions is zero.

View Source
const CodeVersionsRewriteRefused errs.Code = 0x00_03_50_14 // 0.3.80.20

CodeVersionsRewriteRefused identifies a rewrite of a document's versions whose function returned what a rewrite may not: a version it was not given, out of order, with another instant, or that is not JSON.

View Source
const CodeWriteUnconfirmed errs.Code = 0x00_03_50_0F // 0.3.80.15

CodeWriteUnconfirmed identifies a write the filesystem published but could not confirm durable: the directory flush after the rename failed. The write took effect — every reader of the files sees it, and so does the store.

Variables

View Source
var (
	// DocumentNotFound is a miss: no document under that key, or no document
	// holding that key in a unique index.
	DocumentNotFound = errs.Static(CodeDocumentNotFound, "DOCUMENT_NOT_FOUND",
		"No document has this key",
		"service/data/docstore: the store holds no document under the key, or the unique index files nobody under it; the fields name the store and the index, never the key",
		404,
		0)

	// DocumentExists refuses an insertion under a key already taken.
	DocumentExists = errs.Static(CodeDocumentExists, "DOCUMENT_EXISTS",
		"A document already has this key",
		"service/data/docstore: Insert refuses a key the store already holds; Put replaces, Insert never does",
		409,
		0)

	// UniqueKeyTaken refuses a write a unique index cannot file: another
	// document already holds one of its keys in that index. Nothing changed.
	UniqueKeyTaken = errs.Static(CodeUniqueKeyTaken, "UNIQUE_KEY_TAKEN",
		"Another document already has this key in a unique index",
		"service/data/docstore: the write was refused before anything changed; the index field names the unique index, never the key",
		409,
		0)

	// DocumentKeyEmpty refuses a document whose key is the empty string.
	DocumentKeyEmpty = errs.Static(CodeDocumentKeyEmpty, "DOCUMENT_KEY_EMPTY",
		"A document's key cannot be empty",
		"service/data/docstore: the store's key function returned the empty string",
		400,
		0)

	// DocumentKeyChanged refuses an update whose function changed the key of
	// the document it was handed. Nothing changed.
	DocumentKeyChanged = errs.Static(CodeDocumentKeyChanged, "DOCUMENT_KEY_CHANGED",
		"An update cannot change a document's key",
		"service/data/docstore: the update function returned a document whose key differs from the one it was given; delete and insert to rename",
		400,
		0)

	// IndexUnknown refuses a lookup in an index the store never declared.
	IndexUnknown = errs.Static(CodeIndexUnknown, "INDEX_UNKNOWN",
		"The store has no index of this name",
		"service/data/docstore: Lookup or Find named an index absent from the store's Config; the index field names it",
		400,
		0)

	// IndexNotUnique refuses a single-document lookup in an index that may
	// file several documents under one key.
	IndexNotUnique = errs.Static(CodeIndexNotUnique, "INDEX_NOT_UNIQUE",
		"The index is not unique: read it with Find",
		"service/data/docstore: Lookup returns one document and was given a multi-valued index; the index field names it",
		400,
		0)

	// DocumentUndecodable reports a stored document that no longer fits the
	// store's type — the type changed under data an older program wrote. The
	// decoder's own error travels as a field, never as the origin.
	DocumentUndecodable = errs.Static(CodeDocumentUndecodable, "DOCUMENT_UNDECODABLE",
		"A stored document does not match its type",
		"service/data/docstore: encoding/json could not decode a stored document into the store's type; the fields name the store and the cause",
		500,
		65)

	// DocumentUnencodable refuses a value encoding/json cannot encode — a
	// channel, a function, a cycle — or cannot encode unchanged: a store
	// key or an index key that is not valid UTF-8, which JSON would
	// rewrite into another key. Nothing changed.
	DocumentUnencodable = errs.Static(CodeDocumentUnencodable, "DOCUMENT_UNENCODABLE",
		"The document cannot be encoded",
		"service/data/docstore: the value cannot be written as JSON unchanged; the fields name the store, the cause and, for an index key, the index",
		500,
		0)

	// PersistFailed reports a write the filesystem did not make durable. The
	// store is unchanged: memory and disk still agree on the previous state.
	PersistFailed = errs.Static(CodePersistFailed, "PERSIST_FAILED",
		"The document could not be written to storage",
		"service/data/docstore: the filesystem refused a publication; nothing changed in memory, and the fields name the store and the step",
		500,
		74)

	// WriteUnconfirmed reports a write the filesystem published but could
	// not confirm durable — vfs's DirectorySyncFailed: the rename happened,
	// the flush of the directory that records it did not. It is not
	// PersistFailed, because the write TOOK EFFECT: the file shows it, so the
	// store applies it too rather than disagree with its own files. What is
	// in doubt is only whether it survives a power loss.
	WriteUnconfirmed = errs.Static(CodeWriteUnconfirmed, "WRITE_UNCONFIRMED",
		"The document was stored, but its durability could not be confirmed",
		"service/data/docstore: the publication's rename succeeded and the directory flush failed; the write stands in memory and on disk, and may not survive a power loss",
		500,
		74)

	// LoadFailed refuses to open a store whose files cannot be read, are not
	// a store's files, or keep versions the store was not opened to keep.
	LoadFailed = errs.Static(CodeLoadFailed, "LOAD_FAILED",
		"The store's files could not be loaded",
		"service/data/docstore: Open could not read or parse the snapshot, the versions file or an overlay entry, or found versions it does not keep; the fields name the file and the problem",
		500,
		65)

	// IndexBroken refuses to open a store whose documents break a unique
	// index — the index was added after the data, or a file was edited — or
	// whose index key function panicked. A unique index that does not hold
	// would be a lie every Lookup tells.
	IndexBroken = errs.Static(CodeIndexBroken, "INDEX_BROKEN",
		"The stored documents break a unique index",
		"service/data/docstore: two stored documents share a key in a unique index, or a key function panicked while the indexes were rebuilt; the fields name the index",
		500,
		65)

	// StoreClosed refuses a call on a store that has been closed.
	StoreClosed = errs.Static(CodeStoreClosed, "STORE_CLOSED",
		"The store is closed",
		"service/data/docstore: Close was called; open the store again to use it",
		503,
		0)

	// StoreMisconfigured refuses a configuration no store could honour: no
	// key function, an index without a name, a function or a unique one, a
	// filesystem without a path, a negative Versions, a Held without
	// Versions; for the SQL store, a transactor that cannot join or defer, an
	// unknown dialect, a table name that is not one.
	StoreMisconfigured = errs.Static(CodeStoreMisconfigured, "STORE_MISCONFIGURED",
		"The document store cannot be opened as configured",
		"service/data/docstore: Open or OpenSQL refused its Config; the fields name the setting and the problem",
		0,
		78)

	// StatementFailed reports a call the SQL store's database did not
	// complete: it could not be reached, its context ended, or it refused a
	// statement for a reason that is none of the store's own refusals. What
	// the call was writing did not take effect.
	//
	// The driver's error travels beside it under errors.Join, reachable by
	// errors.Is and errors.As — a context's deadline included — and WITHHELD
	// from its text: a driver quotes the row a constraint refused, and that
	// row holds a key (ADR 0139).
	StatementFailed = errs.Static(CodeStatementFailed, "STATEMENT_FAILED",
		"The store's database did not complete the request",
		"service/data/docstore: a SQL statement failed; the driver's error is joined beside this one, its text withheld, and the fields name the table and the step",
		503,
		75)

	// KeyTooLong refuses a write to the SQL store whose store key, or one of
	// whose index keys, is longer than its key columns hold. It is refused
	// before any statement, because MySQL outside strict mode would truncate
	// the key rather than refuse it — and a truncated key is another key.
	KeyTooLong = errs.Static(CodeKeyTooLong, "KEY_TOO_LONG",
		"A key is longer than the store can keep",
		"service/data/docstore: a store key or an index key exceeds MaxSQLKeyLen bytes; the fields name the store, the index and the limit, never the key",
		400,
		0)

	// VersionsNotKept refuses a call on a document's versions — Versions,
	// Version, RewriteVersions — to a store whose configuration keeps none.
	// Answering the current document as its only version would tell the
	// caller a history exists and is empty, which is not what happened.
	VersionsNotKept = errs.Static(CodeVersionsNotKept, "VERSIONS_NOT_KEPT",
		"This store keeps no versions",
		"service/data/docstore: the store was opened with Versions zero, so it records no version and reads none; the fields name the store",
		400,
		0)

	// VersionNotFound is a miss on a version: the document holds no version of
	// that number — it was never made, or a write pruned it.
	VersionNotFound = errs.Static(CodeVersionNotFound, "VERSION_NOT_FOUND",
		"The document keeps no version of this number",
		"service/data/docstore: the document exists and keeps no version of the number asked for, never made or pruned; the fields name the store and the number, never the key",
		404,
		0)

	// VersionsRewriteRefused refuses what a rewrite's function returned: a
	// rewrite may drop a former version and change what one holds and what
	// its writer said, never add one, reorder them or change when one was
	// made. Nothing changed.
	VersionsRewriteRefused = errs.Static(CodeVersionsRewriteRefused, "VERSIONS_REWRITE_REFUSED",
		"A rewrite of versions may only drop or rewrite the versions it was given",
		"service/data/docstore: the rewrite function returned a version it was not given, out of order, with another instant, or that is not JSON; the fields name the store and the problem",
		400,
		0)

	// DocumentNotTrashed is a miss on the trash: no document is trashed under
	// that key — it never was, it was restored, purged, or written again.
	DocumentNotTrashed = errs.Static(CodeDocumentNotTrashed, "DOCUMENT_NOT_TRASHED",
		"No document with this key is in the trash",
		"service/data/docstore: Restore or Purge named a key the store holds no trashed document under; the fields name the store, never the key",
		404,
		0)

	// VersionsInvalid refuses a document PutDocument cannot write with its
	// versions as they are given: no version, a number 0, numbers that do
	// not strictly decrease from the newest, an unknown instant on a version
	// other than 1, a version that is not JSON, or a current document whose
	// own key is another. The "problem" field names which. Nothing changed.
	//
	// It refuses rather than corrects: a copy that renumbered, re-stamped or
	// re-keyed what it copied would no longer be a copy.
	VersionsInvalid = errs.Static(CodeVersionsInvalid, "VERSIONS_INVALID",
		"The document's versions cannot be written as they were given",
		"service/data/docstore: PutDocument was given versions no store keeps as given; the fields name the store and the problem, never a key or a version",
		400,
		0)
)

Functions

This section is empty.

Types

type Announcer

type Announcer interface {
	// OnWrite registers fn to be told the key of every document Put, Insert,
	// Replace, Update or Restore stored, and returns the function that removes
	// the registration. A nil fn registers nothing. A panic in fn reaches the
	// writer after its write stood.
	OnWrite(fn func(key string)) (remove func())
	// OnDelete registers fn to be told the key of every document Delete or
	// Trash removed, on the same terms as OnWrite.
	OnDelete(fn func(key string)) (remove func())
}

Announcer is the pair of hooks a document store answers: both engines implement it exactly as it is. A registered function is told the key of every document a write stored or a deletion removed once that write stands — durable for the file engine, committed for the SQL engine, and never for a write rolled back — on the writer's goroutine and outside every lock of the store.

type Collection

type Collection[T any] interface {
	Announcer
	// Get returns the document stored under key, or DOCUMENT_NOT_FOUND.
	Get(key string) (T, error)
	// List returns every document, in key order.
	List() ([]T, error)
	// Filter returns the documents keep accepts, in key order. It decodes
	// every document; an index is the way not to.
	Filter(keep func(T) bool) ([]T, error)
	// Entries returns up to limit stored documents as JSON, in key order —
	// every one when limit is not positive — each a copy the caller owns.
	Entries(limit int) ([]EntryValue, error)
	// Lookup returns the document holding key in the unique index named
	// index: DOCUMENT_NOT_FOUND when none does, INDEX_UNKNOWN for an index the
	// store never declared, INDEX_NOT_UNIQUE for one that may file several.
	Lookup(index, key string) (T, error)
	// Find returns the documents filed under key in the index named index, in
	// store-key order. It reads any index, unique or not, and no document is
	// an empty slice, not an error.
	Find(index, key string) ([]T, error)
	// Put stores v under its key, creating it or replacing whatever was there.
	Put(v T) error
	// Insert stores v, which must be new: DOCUMENT_EXISTS when its key is
	// taken.
	Insert(v T) error
	// Replace stores v over the document already under its key:
	// DOCUMENT_NOT_FOUND when there is none, so a document deleted meanwhile
	// is never brought back.
	Replace(v T) error
	// Update applies fn to a copy of the document stored under key and stores
	// the result, atomically with respect to every other write, and returns
	// it. An error from fn is returned as it is and changes nothing, and so
	// does a result whose key is not key (DOCUMENT_KEY_CHANGED). fn may read
	// the store and must not write to it.
	Update(key string, fn func(*T) error) (T, error)
	// Delete removes the document stored under key, or returns
	// DOCUMENT_NOT_FOUND. Its versions go with it.
	Delete(key string) error
}

Collection is a typed, keyed collection of JSON documents with secondary indexes whose calls take no context — the port the file engine, internal/service/data/docstore.Store, implements. Every read returns a copy and every write stores one: a document is kept as its JSON encoding, so what a caller holds never aliases what the store holds. No refusal quotes a key.

The versions of a document are Versioned, its sibling; the file engine's Stats, Fold and Close are the engine's own, because a double of the port has no files to fold.

type CollectionContext

type CollectionContext[T any] interface {
	Announcer
	// Get is Collection.Get under ctx.
	Get(ctx context.Context, key string) (T, error)
	// List is Collection.List under ctx.
	List(ctx context.Context) ([]T, error)
	// Filter is Collection.Filter under ctx.
	Filter(ctx context.Context, keep func(T) bool) ([]T, error)
	// Entries is Collection.Entries under ctx.
	Entries(ctx context.Context, limit int) ([]EntryValue, error)
	// Lookup is Collection.Lookup under ctx.
	Lookup(ctx context.Context, index, key string) (T, error)
	// Find is Collection.Find under ctx.
	Find(ctx context.Context, index, key string) ([]T, error)
	// Put is Collection.Put under ctx.
	Put(ctx context.Context, v T) error
	// Insert is Collection.Insert under ctx.
	Insert(ctx context.Context, v T) error
	// Replace is Collection.Replace under ctx.
	Replace(ctx context.Context, v T) error
	// Update is Collection.Update under ctx: fn runs inside the write's
	// transaction, and must not write the document it updates.
	Update(ctx context.Context, key string, fn func(*T) error) (T, error)
	// Delete is Collection.Delete under ctx.
	Delete(ctx context.Context, key string) error
}

CollectionContext is Collection with a context on every call — the port the SQL engine, internal/service/data/docstore.SQLStore, implements. A call runs on the transaction its context carries for the store's database, and on the pool otherwise; a write is atomic on its own either way, and its hooks run once the transaction that holds it has committed (ADR 0139). Every method answers as its Collection counterpart does, and may also fail as a database fails.

The SQL engine's Count and Reindex are the engine's own: a double of the port has no tables to count or to file again.

type EntryValue

type EntryValue struct {
	// Key is the document's key.
	Key string
	// JSON is the document, a copy the caller owns.
	JSON json.RawMessage
}

EntryValue is one stored document as the store holds it: its key and its JSON, for a caller that shows documents rather than decoding them.

type IndexSpec

type IndexSpec[T any] struct {
	// Keys returns a document's keys in the index. Empty keys are not
	// indexed, and a key listed twice is filed once.
	Keys func(T) []string
	// Name reads the index in Lookup and Find, and names it in a refusal.
	Name string
	// Unique refuses a write that would file two documents under one key.
	Unique bool
}

IndexSpec declares one secondary index: a name to read it by, whether it is unique, and the function that gives a document its keys in it. Build one with Unique or Index, and give it to an engine's constructor — Open or OpenSQL in internal/service/data/docstore, which refuse the same declarations alike. The file engine rebuilds its indexes when the store opens; both keep them in step with every write.

func Index

func Index[T any](name string, keys func(T) []string) IndexSpec[T]

Index declares an index where a document may have several keys and a key several documents — the members a task is shared with. Find reads it.

func Unique

func Unique[T any](name string, key func(T) string) IndexSpec[T]

Unique declares a unique index over the one key key returns: no two documents may share it, and a write that would is refused with UniqueKeyTaken. An empty key is not indexed, so any number of documents may have none.

type KeyLister added in v0.6.0

type KeyLister interface {
	// Keys yields the key of every document the store holds that sorts after
	// after — every key when after is empty — in key order, byte order as Go
	// compares strings, and no document. Stopping early is allowed, and
	// after is where a later call resumes. A failure is yielded once, with
	// an empty key, and ends the listing; a closed store yields
	// STORE_CLOSED.
	Keys(after string) iter.Seq2[string, error]
}

KeyLister is the keys of a store's documents and nothing else, on a store whose calls take no context: the sibling of Collection the file engine implements beside it (ADR 0178). It reads no document, so what it costs does not grow with what the documents weigh. A document the store no longer holds — deleted, or in the trash (Trashable) — is not listed: Keys lists what Get finds.

type KeyListerContext added in v0.6.0

type KeyListerContext interface {
	// Keys is KeyLister.Keys under ctx: each step that reads runs on the
	// transaction ctx carries for the store's database, and on the pool
	// otherwise.
	Keys(ctx context.Context, after string) iter.Seq2[string, error]
}

KeyListerContext is KeyLister with a context on every call: the sibling of CollectionContext the SQL engine implements beside it.

type StampValue

type StampValue struct {
	// Meta is kept with the version the write makes — who made it, which
	// command — and read back with it. The store copies it and never reads
	// it.
	Meta map[string]string
	// InPlace makes the write change the document without making a version:
	// the current version keeps its number, its instant and its Meta, and
	// holds the new document. A framework stamps so a workflow's transition,
	// which its journal already records. A creation is version 1 all the
	// same.
	InPlace bool
}

StampValue is what a write says about the version it makes, for the Stamped writes. Its zero value is what Put, Insert, Replace and Update say: nothing, and a new version.

type TombstoneValue added in v0.5.0

type TombstoneValue struct {
	// Key is the trashed document's key.
	Key string
	// At is when the document was trashed, in UTC, read from the store's
	// clock and kept to the nanosecond, as a version's instant is.
	At time.Time
}

TombstoneValue is a document in the trash, as Trashable.Tombstones lists it: its key and when it was trashed. The document and its versions are read through Versioned.Versions.

type Trashable added in v0.5.0

type Trashable interface {
	// Trash moves the document stored under key to the trash: it is gone from
	// every read and its index keys are freed, while its versions are kept
	// and the trash records the instant. It calls OnDelete.
	// DOCUMENT_NOT_FOUND when no document is stored under key.
	Trash(key string) error
	// Restore brings the document trashed under key back, exactly as it was
	// — the same JSON, the same current version — and files it again. It
	// calls OnWrite. DOCUMENT_NOT_TRASHED when no document is trashed under
	// key, UNIQUE_KEY_TAKEN when another document took one of its unique
	// keys meanwhile, which then stays in the trash.
	Restore(key string) error
	// Purge empties the trash of the document trashed under key: the
	// trashed document and every version it kept go, and a document written
	// later under the key starts again at version 1. It calls no hook — the
	// document already left every read — and asks no hold, as Delete does
	// not. DOCUMENT_NOT_TRASHED when no document is trashed under key.
	Purge(key string) error
	// Tombstones returns every document in the trash, in key order: its key
	// and when it was trashed, for a caller that empties the trash after a
	// while.
	Tombstones() ([]TombstoneValue, error)
}

Trashable is a document's trash, on a store whose calls take no context: the sibling of Collection and Versioned the file engine implements beside them (ADR 0143, amended for ADR 0178). Only a store that keeps versions has a trash; one that keeps none answers every call with VERSIONS_NOT_KEPT.

A document in the trash is gone from every read — Get, List, Lookup, Find — and its unique keys are free, as a former version's are; its versions stay, readable through Versioned, and its number is kept: a creation under its key is a write over the trashed document, which takes it out of the trash and continues its versions. Delete never reaches the trash, and a trashed document is not Delete's to find: Delete takes a document and its versions at once, which is what an erasure needs. How long a document stays in the trash is the caller's to decide, through Tombstones and Purge.

type TrashableContext added in v0.5.0

type TrashableContext interface {
	// Trash is Trashable.Trash under ctx.
	Trash(ctx context.Context, key string) error
	// Restore is Trashable.Restore under ctx.
	Restore(ctx context.Context, key string) error
	// Purge is Trashable.Purge under ctx.
	Purge(ctx context.Context, key string) error
	// Tombstones is Trashable.Tombstones under ctx.
	Tombstones(ctx context.Context) ([]TombstoneValue, error)
}

TrashableContext is Trashable with a context on every call: the sibling of CollectionContext and VersionedContext the SQL engine implements beside them. Each call is one transaction, a savepoint of the caller's when the context carries one.

type VersionValue

type VersionValue struct {
	// At is when the write that made the version ran, in UTC, read from the
	// store's clock and kept to the nanosecond — by the file engine for the
	// years 0 to 9999, which RFC 3339 writes, and by the SQL engine for any
	// instant. It is zero for the first version of a document the store held
	// before it kept versions: when that one was written is not known.
	At time.Time
	// Meta is what the write's caller said about it — who made it, which
	// command — as its [StampValue] gave it; nil when it said nothing. The
	// store keeps it and never reads it.
	Meta map[string]string
	// JSON is the document as the version holds it, compact, a copy the
	// caller owns. The newest version's is the document itself.
	JSON json.RawMessage
	// Number counts a document's versions from 1. No number is given twice
	// while the document exists or is in the trash, pruning included; a
	// document deleted, or purged from the trash, and written again is a new
	// document, and starts again at 1.
	Number uint64
}

VersionValue is one version of a document: its number, when the write that made it ran, what that write's caller said about it, and the document as the version holds it.

type VersionWeigher added in v0.9.0

type VersionWeigher interface {
	// VersionWeight reports what the store keeps beyond its documents: how
	// many former versions, how many documents in the trash, and the bytes
	// of the JSON they hold. A closed store answers STORE_CLOSED.
	VersionWeight() (VersionWeightValue, error)
}

VersionWeigher is what a store's versions weigh, on a store whose calls take no context: the sibling of Collection and Versioned the file engine implements beside them (ADR 0143, amended for kit's Studio). It reads no document: the file engine keeps the weight as a counter every write moves, so asking costs the same whatever the store holds. Only a store that keeps versions has it; one that keeps none answers VERSIONS_NOT_KEPT.

type VersionWeigherContext added in v0.9.0

type VersionWeigherContext interface {
	// VersionWeight is VersionWeigher.VersionWeight under ctx, read in one
	// statement on the transaction ctx carries for the store's database, and
	// on the pool otherwise.
	VersionWeight(ctx context.Context) (VersionWeightValue, error)
}

VersionWeigherContext is VersionWeigher with a context on every call: the sibling of CollectionContext and VersionedContext the SQL engine implements beside them. It is one aggregate statement over the versions table, which the database answers without sending a version.

type VersionWeightValue added in v0.9.0

type VersionWeightValue struct {
	// Former is how many former versions the store keeps, every document's
	// — a trashed one's included.
	Former int
	// Trashed is how many documents are in the trash ([Trashable]).
	Trashed int
	// Bytes is what the former versions and the trashed documents weigh:
	// the length of the JSON each holds, compact, as both engines keep it.
	// The instants and the metadata they carry are not counted.
	Bytes int64
}

VersionWeightValue is what a store's versions weigh, as VersionWeigher.VersionWeight reports it: what the store keeps beyond the documents it holds. A document's current version is the document itself, and counts in none of its fields. Both engines count the same versions the same way, so the same writes weigh the same on either.

type VersionWriter added in v0.8.0

type VersionWriter interface {
	// PutDocument writes the document key with versions, newest first as
	// [Versioned].Versions returns them: the current version — the document
	// itself — then the former ones, each kept as given, its number, its
	// instant (zero for a version 1 made before its store kept versions),
	// its metadata and its JSON, compacted and HTML-escaped as json.Marshal
	// writes a document. With trashedAt zero the document is stored and
	// filed in every index, and OnWrite is told; with trashedAt set, the
	// document is put in the trash at that instant, filed nowhere, and no
	// hook is told. Read back, Versions and Tombstones answer what was
	// given.
	//
	// Nothing may be under key: a document, a trashed one, or versions —
	// DOCUMENT_EXISTS otherwise. VERSIONS_INVALID, its "problem" field
	// naming which, refuses no version, a number 0, numbers that do not
	// strictly decrease, an unknown instant on a version other than 1, a
	// version that is not JSON, and a current document whose own key is not
	// key; DOCUMENT_UNDECODABLE one the store's type does not decode, and
	// UNIQUE_KEY_TAKEN a stored document whose unique key another holds. It
	// asks Held nothing and prunes nothing: the versions are written as
	// given, and the next write prunes as every write does. A refusal
	// writes nothing.
	PutDocument(key string, versions []VersionValue, trashedAt time.Time) error
}

VersionWriter writes a document with its versions as they are given, on a store whose calls take no context: the sibling of Collection, Versioned and Trashable the file engine implements beside them (ADR 0178). It exists for a copy — a store moved from one engine to another — that must keep each version's number, instant and metadata, and the trash's instant: no other write can, since a write numbers the version it makes after the current one and stamps it with the store's clock. Only a store that keeps versions has it; one that keeps none answers VERSIONS_NOT_KEPT.

type VersionWriterContext added in v0.8.0

type VersionWriterContext interface {
	// PutDocument is VersionWriter.PutDocument under ctx, in one transaction
	// — a savepoint of the one ctx carries for the store's database — so the
	// document, its index rows and its versions exist if and only if that
	// transaction commits; OnWrite is told once it has.
	PutDocument(ctx context.Context, key string, versions []VersionValue, trashedAt time.Time) error
}

VersionWriterContext is VersionWriter with a context on every call: the sibling of CollectionContext the SQL engine implements beside it.

type Versioned

type Versioned[T any] interface {
	// PutStamped is Put, with what the write says about the version it makes.
	PutStamped(v T, stamp StampValue) error
	// InsertStamped is Insert, with what the write says about the version it
	// makes: version 1, whatever stamp.InPlace says — or, under the key of a
	// document in the trash, a write over that document.
	InsertStamped(v T, stamp StampValue) error
	// ReplaceStamped is Replace, with what the write says about the version it
	// makes.
	ReplaceStamped(v T, stamp StampValue) error
	// UpdateStamped is Update, with what the write says about the version it
	// makes.
	UpdateStamped(key string, stamp StampValue, fn func(*T) error) (T, error)
	// Versions returns the versions kept of the document stored under key,
	// newest first: the current one — the document as it is now — then the
	// former ones. A document in the trash answers the versions it kept, the
	// trashed document first. DOCUMENT_NOT_FOUND when no document is stored
	// or trashed under key.
	Versions(key string) ([]VersionValue, error)
	// Version returns the version numbered number of the document stored
	// under key: VERSION_NOT_FOUND when the document keeps none of that
	// number — never made, or pruned.
	Version(key string, number uint64) (VersionValue, error)
	// RewriteVersions rewrites the former versions of the document stored
	// under key in one durable write: fn receives copies of them, newest
	// first, and returns those to keep, in the same order. It may drop a
	// version and change what one holds and what its writer said, and nothing
	// else — VERSIONS_REWRITE_REFUSED otherwise. An error from fn is returned
	// as it is and changes nothing. It calls no hook: the document did not
	// change.
	RewriteVersions(key string, fn func(former []VersionValue) ([]VersionValue, error)) error
}

Versioned is a document's versions, on a store whose calls take no context: the sibling of Collection the file engine implements beside it (ADR 0143). A store that keeps no versions answers every call with VERSIONS_NOT_KEPT, except the Stamped writes, which then store as their plain forms do.

type VersionedContext

type VersionedContext[T any] interface {
	// PutStamped is Versioned.PutStamped under ctx.
	PutStamped(ctx context.Context, v T, stamp StampValue) error
	// InsertStamped is Versioned.InsertStamped under ctx.
	InsertStamped(ctx context.Context, v T, stamp StampValue) error
	// ReplaceStamped is Versioned.ReplaceStamped under ctx.
	ReplaceStamped(ctx context.Context, v T, stamp StampValue) error
	// UpdateStamped is Versioned.UpdateStamped under ctx.
	UpdateStamped(ctx context.Context, key string, stamp StampValue, fn func(*T) error) (T, error)
	// Versions is Versioned.Versions under ctx.
	Versions(ctx context.Context, key string) ([]VersionValue, error)
	// Version is Versioned.Version under ctx.
	Version(ctx context.Context, key string, number uint64) (VersionValue, error)
	// RewriteVersions is Versioned.RewriteVersions under ctx.
	RewriteVersions(ctx context.Context, key string, fn func(former []VersionValue) ([]VersionValue, error)) error
}

VersionedContext is Versioned with a context on every call: the sibling of CollectionContext the SQL engine implements beside it. A write's version rows travel in that write's own transaction.

Jump to

Keyboard shortcuts

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