Documentation
¶
Overview ¶
Package recordengine is the shared storage engine for **record-shaped** signals — logs and traces (later, profiles' sample table). A record-shaped signal is a stream (a Resource+Scope identity, indexed by the postings layer) of rows that each carry a primary timestamp plus a fixed set of typed columns. The engine is the structural twin of package engine (metrics) but generic over a Schema: it owns the in-memory head, columnar flush to immutable parts, the durable bucket-index + identity-index stateless read path, append-only merge with retention, per-part column blooms, lazy column decode, and the fetch.Fetcher contract — none of which is signal-specific. A signal package supplies the column Schema and projects its model into the engine's column vectors; the engine treats the columns opaquely.
The timestamp (the sort key) and the int128 stream id (the Resource+Scope hash) are implicit and not part of the Schema; the schema lists only the per-record columns.
Index ¶
- func EncodeWAL(b *Batch) []byte
- type Batch
- type BloomMode
- type Column
- type Config
- type Engine
- func (e *Engine) AppendBatch(b *Batch) (accepted int, err error)
- func (e *Engine) ApplyPrimary(data []byte) (accepted []byte, rejected int, err error)
- func (e *Engine) ApplyReplicated(data []byte) error
- func (e *Engine) Close(ctx context.Context) error
- func (e *Engine) Fetch(ctx context.Context, r fetch.Request) (fetch.Iterator, error)
- func (e *Engine) Flush(ctx context.Context) error
- func (e *Engine) HeadRecordCount() int
- func (e *Engine) LoadParts(ctx context.Context) error
- func (e *Engine) Merge(ctx context.Context, retainFrom int64) error
- func (e *Engine) PartCount() int
- func (e *Engine) RefreshReplica(ctx context.Context) error
- func (e *Engine) Replay(dir string) error
- func (e *Engine) Reset(ctx context.Context) error
- func (e *Engine) Series(matchers []fetch.Matcher, start, end int64) []signal.Series
- func (e *Engine) SideSnapshot(ctx context.Context) (map[string][]byte, error)
- func (e *Engine) StreamCount() int
- func (e *Engine) SyncWAL() error
- type Kind
- type Schema
- type SideStore
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EncodeWAL ¶
EncodeWAL frames a batch as a replication/WAL payload — a stream-identity record followed by the batch's records — exactly the form Engine.ApplyPrimary and Engine.ApplyReplicated replay. The cluster write path builds a tenant's payload by concatenating EncodeWAL over its streams and routing it to the ring primary.
Types ¶
type Batch ¶
type Batch struct {
Stream signal.SeriesID
Identity func() signal.Series // materialized only when the stream is newly seen
Ts []int64
Ints [][]int64 // len == schema int count; Ints[k][row]
Bytes [][][]byte // len == schema byte count; Bytes[k][row]
// Side is an optional encoded side-store delta (the content-addressed symbols this batch's
// records reference) absorbed by [Config.SideStore]. nil when the engine has no side store.
Side []byte
}
Batch is one stream's projected records in the engine's column layout: the primary timestamps plus the int and byte column vectors in the schema's per-kind order. The signal package builds it; the engine treats the columns opaquely. Byte slices may alias the source batch (the head clones them on append).
type BloomMode ¶
type BloomMode uint8
BloomMode says whether and how a column feeds its per-part bloom for predicate pruning.
const ( // BloomNone builds no bloom for the column. BloomNone BloomMode = iota // BloomFullText tokenizes the column's value (lowercased words) so a `contains token` // condition can prune a part whose bloom lacks the token (e.g. a log body, a span name). BloomFullText // BloomAttrs treats the column as a serialized [signal.Attributes] blob and adds key-scoped // equality (`key‖value`) and full-text (`key‖word`) tokens, so per-record attribute equality // and contains conditions prune. BloomAttrs // BloomEquality adds the column's exact value as a token so a `column == value` condition can // prune (e.g. trace-by-id over the trace_id column). BloomEquality )
type Column ¶
Column is one per-record column of a Schema: its name, physical kind, on-disk codec (zero ⇒ the kind's default), and bloom contribution.
type Config ¶
type Config struct {
// Schema is the per-record column set this engine stores (required; the signal supplies it).
Schema *Schema
// OOOWindow rejects records older than newest-OOOWindow (nanoseconds). 0 disables.
OOOWindow int64
// WAL, when non-nil, durably logs streams and records for crash recovery. nil ⇒ ephemeral.
WAL *wal.SegmentWriter
// Backend stores flushed parts. Required for [Engine.Flush]; nil ⇒ head-only.
Backend backend.Backend
// Prefix is the backend key prefix under which this engine's parts are written.
Prefix string
// SideStore, when non-nil, is a signal-supplied content-addressed auxiliary store (e.g. the
// profiles symbol store) that the engine persists as part sidecars on flush and unions on merge.
// nil ⇒ no side data (logs, traces).
SideStore SideStore
}
Config configures an Engine.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is one tenant's record store for a signal. Safe for concurrent use.
func (*Engine) AppendBatch ¶
AppendBatch ingests one stream's records: it registers the stream on first sight, appends each record through the OOO check, and logs accepted records to the WAL. It returns how many records were accepted (the rest were out-of-order beyond the window). Safe for concurrent use.
func (*Engine) ApplyPrimary ¶
ApplyPrimary applies a write as the stream's **primary**: it appends each record through the out-of-order check (the single OOO decision for the shard) and re-frames the *accepted* records into a WAL payload to replicate to the secondary owners. It returns that accepted payload and the number of records rejected as out-of-order. Every replica converges on the same data. Safe for concurrent use.
func (*Engine) ApplyReplicated ¶
ApplyReplicated applies a replicated write from the primary verbatim (no OOO re-check — the primary already decided the accepted set), so all replicas hold identical data. Safe for concurrent use.
func (*Engine) Close ¶
Close flushes any buffered records to a part and closes the WAL. It does not stop a background loop — the owner ([storage.Storage]) does that before calling Close.
func (*Engine) Fetch ¶
Fetch implements fetch.Fetcher over head ∪ flushed parts: it resolves matchers to streams, gathers each stream's in-window records (decoding only the referenced columns), applies the column conditions and projection, and returns one batch per stream sorted by timestamp.
func (*Engine) Flush ¶
Flush writes the head's buffered records to a new immutable part and clears the buffers. No-op if the head is empty. Requires a Config.Backend.
func (*Engine) HeadRecordCount ¶
HeadRecordCount returns the number of records buffered in the head across all streams.
func (*Engine) LoadParts ¶
LoadParts reconstructs the engine's durable state from the object store: the part set from the bucket index and the stream identity index from the persisted object. A head-only engine is a no-op. Replaces current parts and advances the sequence.
func (*Engine) Merge ¶
Merge compacts every flushed part into a single new part, dropping records older than retainFrom (retention; retainFrom ≤ 0 disables it). No-op when there is nothing to gain — fewer than two parts and no retention cutoff. Records are append-only: a stream's records are concatenated across parts (no value dedup) and re-sorted by timestamp.
func (*Engine) RefreshReplica ¶
RefreshReplica brings a replica node's view up to date with the shared object store: it reconstructs the flushed parts and trims its head to the still-unflushed window. With no shared store, a safe no-op.
func (*Engine) Replay ¶
Replay rebuilds the head (and side store) from the WAL segments in dir (durable restart). It skips segments at or below the flush watermark recovered by Engine.LoadParts (call LoadParts first), so records already in a flushed part are not re-applied — exactly-once recovery.
func (*Engine) Reset ¶
Reset discards all data (head + parts) and deletes this engine's part objects, returning it to the empty state without reallocating. Safe for concurrent use.
func (*Engine) Series ¶
Series returns the identities of the streams matching matchers that hold at least one record in [start, end] — the enumeration primitive behind profile-type / label listing. A zero start AND end disables the time filter (return every matching stream). The time filter is part-overlap granular (a returned stream is guaranteed to match the matchers; its in-window records are a superset check). Safe for concurrent use.
func (*Engine) SideSnapshot ¶
SideSnapshot returns the engine's full side-store tables — the live head accumulator unioned with every flushed part's sidecars — as named payloads, for a signal to build a resolver over (e.g. the profiles symbol store). nil when the engine has no side store. Safe for concurrent use.
func (*Engine) StreamCount ¶
StreamCount returns the number of distinct streams in the head.
type Kind ¶
type Kind uint8
Kind is a column's physical type. Records use only int64 and byte-string columns (floats live in the metrics engine); a typed value is projected onto one of these at the language edge.
type Schema ¶
type Schema struct {
// contains filtered or unexported fields
}
Schema is the ordered set of per-record columns a signal stores. It is immutable after construction and shared by every engine, part, and record batch of that signal.
type SideStore ¶
type SideStore interface {
// Absorb merges one batch's encoded side delta ([Batch.Side]) into the live accumulator.
Absorb(delta []byte) error
// Encode serializes the accumulated side data into named sidecar payloads (name → bytes),
// written as {prefix}/sym-{name}.bin at flush.
Encode() map[string][]byte
// Reset clears the live accumulator (after a flush drains the head).
Reset()
// Names returns the sidecar names to read back for a part on merge (the keys [SideStore.Encode]
// may produce). A part missing a named sidecar is skipped.
Names() []string
// Union merges the loaded sidecars of the compacted parts (one map per part) and returns the
// merged named payloads to write under the new part. Pure; ignores the live accumulator.
Union(parts []map[string][]byte) (map[string][]byte, error)
}
SideStore is an optional per-engine auxiliary store that rides the part lifecycle. A signal whose records reference content-addressed side data (e.g. the profiles symbol store: strings, functions, locations, stacks) supplies one via Config.SideStore; the engine then absorbs each batch's delta (Batch.Side) into a live accumulator, persists the accumulator as part sidecars on flush, and unions the sidecars of compacted parts on merge. The engine treats the data opaquely — only the signal package knows the table formats.
Content-addressing is the load-bearing assumption: an entry's id is a hash of its content, so the same entry has the same id everywhere and SideStore.Union is a plain dedup with no id remap.
All methods are called under the engine's lock, so an implementation need not be safe for concurrent use. SideStore.Union must be a pure function of its arguments and must not read or mutate the live accumulator (it merges already-flushed part data, independent of the head).