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 ¶
- Constants
- Variables
- func EncodeWAL(b *Batch) []byte
- type AppendLimits
- type AppendResult
- type Batch
- type BloomMode
- type CardinalityStat
- type Column
- type ColumnCostStat
- type ColumnStat
- type Config
- type Engine
- func (e *Engine) AppendBatch(b *Batch, limits AppendLimits) (AppendResult, error)
- func (e *Engine) ApplyPrimary(data []byte, limits AppendLimits) (accepted []byte, res AppendResult, err error)
- func (e *Engine) ApplyReplicated(data []byte) error
- func (e *Engine) Cardinality(topN int) CardinalityStat
- func (e *Engine) Close(ctx context.Context) error
- func (e *Engine) CloseWAL() 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) HeadBytes() int64
- func (e *Engine) HeadRecordCount() int
- func (e *Engine) IdentityBytes() int64
- func (e *Engine) Keys(start, end int64) []KeyInfo
- func (e *Engine) LoadParts(ctx context.Context) error
- func (e *Engine) Merge(ctx context.Context, retainFrom int64) error
- func (e *Engine) MergeBacklog() int
- func (e *Engine) MergeRunning() bool
- func (e *Engine) MergeShape() MergeShape
- func (e *Engine) MergeWith(ctx context.Context, opts MergeOptions) error
- func (e *Engine) PartCount() int
- func (e *Engine) Parts() []PartStat
- func (e *Engine) PartsDetailed(ctx context.Context) ([]PartDetailStat, error)
- func (e *Engine) PruneIdentities(ctx context.Context) (int, error)
- func (e *Engine) PruneIdentitiesWith(ctx context.Context, opts PruneOptions) (int, error)
- 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) Stats() Stats
- func (e *Engine) StreamCost(ctx context.Context, opts StreamCostOptions) ([]StreamCostStat, error)
- func (e *Engine) StreamCount() int
- func (e *Engine) SyncWAL() error
- func (e *Engine) WALState() (segments int, bytes int64, epoch uint64, ok bool)
- type KeyInfo
- type KeyScope
- type Kind
- type LabelCard
- type MergeOptions
- type MergeShape
- type PartDetailStat
- type PartStat
- type PruneOptions
- type Schema
- type SideStore
- type Stats
- type StreamCostOptions
- type StreamCostStat
Constants ¶
const DefaultStreamCostSketchGroups = 4096
DefaultStreamCostSketchGroups is StreamCostOptions.MaxSketchGroups's default: 4096 groups, i.e. 32 MiB of transient sketch state. It is set to cover a real store's group count (a corpus grouped by a pod-suffixed service.name produced 2256) rather than to ration the sketches — 8 KiB per group is negligible against the column decode the pass is already doing. The cap exists for the pathological case, grouping a million-stream store by raw stream id.
Variables ¶
var ErrCorruptKeys = errors.New("recordengine: corrupt record-keys footer")
ErrCorruptKeys is returned when a serialized record-keys footer fails to parse.
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 AppendLimits ¶ added in v0.4.0
type AppendLimits struct {
// MaxSeries caps the number of distinct streams buffered in the head. A record that would
// register a new stream once the head already holds MaxSeries is rejected (the whole batch,
// since a batch is one stream); known streams are unaffected. 0 ⇒ unlimited.
MaxSeries int64
// MaxInFlightBytes caps the head's buffered record bytes. A record arriving while the head is
// at or over the cap is rejected (memory backpressure) until a flush drains it. 0 ⇒ unlimited.
MaxInFlightBytes int64
}
AppendLimits are the per-call admission limits the record head enforces while buffering a batch. The zero value imposes no limit; they are passed per Engine.AppendBatch call so a consumer's hot-reloaded tenant policy takes effect on the next write. The engine stays policy-agnostic — it sees only these numbers. They mirror the metric engine's limits so the facade maps one tenant.Limits to both.
type AppendResult ¶ added in v0.4.0
type AppendResult struct {
Accepted int
RejectedOOO int // older than the out-of-order window
RejectedCardinality int // would exceed AppendLimits.MaxSeries (a new stream)
RejectedBytes int // head at or over AppendLimits.MaxInFlightBytes
}
AppendResult reports the disposition of an Engine.AppendBatch run by reason, so the caller can attribute an OTLP partial-success precisely.
func (AppendResult) Rejected ¶ added in v0.4.0
func (r AppendResult) Rejected() int
Rejected returns the total number of rejected records across all reasons.
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 CardinalityStat ¶ added in v0.12.0
type CardinalityStat struct {
TotalSeries int64 // distinct streams
DistinctLabelNames int
SymbolCount int
Top []LabelCard // sorted by Series desc, then Name; truncated to the requested top-N
}
CardinalityStat summarizes the engine's label cardinality (the head's index spans head ∪ flushed streams). TotalSeries and SymbolCount are exact; Top is the highest-cardinality label names.
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 ColumnCostStat ¶ added in v0.37.0
type ColumnCostStat struct {
Name string
RawBytes int64
DiskBytes int64 // approximate, as [StreamCostStat.DiskBytes]
// Distinct estimates the distinct values the group's rows hold in this column (HyperLogLog,
// ~1.6% standard error). 0 for an int column and for a group outside the sketch budget.
Distinct int64
// DistinctNormalized is Distinct over values with every run of ASCII digits collapsed to '#'.
// It is what separates a genuinely high-entropy column from a templated one carrying an embedded
// timestamp or id: a group whose Distinct is large and whose DistinctNormalized is tiny is not a
// storage problem but a parsing one — the same line, repeated, never turned into fields.
DistinctNormalized int64
}
ColumnCostStat is one column's share of a group's cost.
type ColumnStat ¶ added in v0.12.0
type ColumnStat struct {
Name string
Kind string // physical type: int64 / float64 / bytes / int128
Codec string // value codec
Compress string // block-compression algorithm
Level int // block-compression level (0 ⇒ algorithm default or uncompressed)
}
ColumnStat is one part column's physical description (from the manifest).
type Config ¶
type Config struct {
// Schema is the per-record column set this engine stores (required; the signal supplies it).
Schema *Schema
// OOOWindow is a per-stream lateness bound: a record older than OOOWindow (nanoseconds) behind
// the stream's own newest admitted record is rejected. 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
// Term reports this writer's current ownership term for Prefix — which tenure of the shard
// this engine is writing as. It is stamped into every bucket index written, so a reader can
// order two indexes of the same prefix even when neither the part names nor FlushedEpoch
// moved; see [github.com/oteldb/storage/backend/bucketindex.Generation]. nil is a writer with
// no cluster, whose generation is then a plain local counter.
Term func() uint64
// WriterID is this writer's stable identity — the cluster node id — under which its WAL flush
// watermark is kept in the bucket index. It matters because that index is *shared*: over a
// shared object store every replica of a shard commits one index object under one prefix, and
// the watermark is a per-node count of that node's own flushes, so one scalar in a shared
// object is meaningless to whichever node did not write it (see
// [github.com/oteldb/storage/backend/bucketindex.WriterEpoch]).
//
// Empty is the anonymous writer: a single-writer engine, which keeps the sole pre-v4 slot.
// Leaving it empty where two engines do share a prefix makes them share one slot, which is
// the defect the slots exist to prevent.
WriterID 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
// Obs is the observability handle (spans + metrics). nil ⇒ a no-op handle.
Obs *obs.Obs
// Signal is the signal label for this engine's metrics ("log"/"trace"/"profile"); the facade
// sets it per signal. Empty ⇒ "record".
Signal string
// MaxPartBytes bounds a flushed/merged part's approximate uncompressed size. It is what lets
// size-tiered compaction seal large parts and bound both part count and a single merge's decoded
// working set (see compact.go) — without it a continuously-ingesting engine's merge grows to
// re-materialize the whole dataset every cycle. 0 ⇒ unlimited (merge everything into one part;
// the legacy behavior, unbounded working set). The facade resolves it from the tenant policy.
MaxPartBytes int64
// MergeMemoryBytes is how much memory all concurrent merges together may hold. A merge holds its
// selected sources decoded plus the output buffer it is filling, so this bounds the merge cap
// (see mergecap.go) independently of MaxPartBytes — a tiering target sized for the disk must not
// size a working set the process cannot hold. 0 ⇒ a share of the process memory budget
// (GOMEMLIMIT, else the cgroup limit, else host memory); negative ⇒ unbounded.
MergeMemoryBytes int64
// MergeConcurrency reports how many merges may run concurrently in this process, dividing the
// merge memory allowance so they cannot collectively exceed it. nil or ≤ 1 ⇒ no division.
MergeConcurrency func() int
// MergeCompression block-compresses the columns of merged (compacted) parts on top of their chunk
// codecs — the cold, long-lived data. Flushed parts stay codec-only so ingest is cheap; the
// background merge is where recompression is amortized. Record byte columns are dict-coded but not
// entropy-coded, so ZSTD here is a large on-disk win (≈10× on log-shaped data). AlgorithmNone (the
// default) keeps the legacy uncompressed behavior.
MergeCompression compress.Algorithm
// MergeCompressionLevel is the level for MergeCompression (ZSTD only). 0 ⇒ the algorithm default.
MergeCompressionLevel compress.Level
// MinFreeBytes is the headroom the engine leaves unused on a backend that reports its free
// space: a flush is refused, and the ingest path starts rejecting, once the medium holds less
// than the pending part plus this. It leaves a merge room for its output — a merge must write
// before it can retire the inputs it frees. 0 ⇒ [diskguard.DefaultReserveBytes]; negative ⇒ the
// byte axis is not checked.
MinFreeBytes int64
// MinFreeInodes is the same headroom on the object-count axis, for a backend that reports free
// inodes. It is checked separately because a part is many small objects: an inode table can
// exhaust with the disk half empty, and byte accounting cannot see it. 0 ⇒
// [diskguard.DefaultReserveInodes]; negative ⇒ the inode axis is not checked.
MinFreeInodes int64
}
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 ¶
func (e *Engine) AppendBatch(b *Batch, limits AppendLimits) (AppendResult, error)
AppendBatch ingests one stream's records: it registers the stream on first sight, appends each record through the admission limits (OOO window, cardinality, in-flight bytes), and logs accepted records to the WAL. It returns an AppendResult breaking accepted/rejected down by reason, so the caller can report an exact OTLP partial-success. Safe for concurrent use.
func (*Engine) ApplyPrimary ¶
func (e *Engine) ApplyPrimary(data []byte, limits AppendLimits) (accepted []byte, res AppendResult, err error)
ApplyPrimary applies a write as the stream's **primary**: it runs each record through the admission-checked append path (the single OOO decision for the shard, plus the cardinality and in-flight-memory valves from limits) and re-frames the *accepted* records into a WAL payload to replicate to the secondary owners. It returns that accepted payload and an AppendResult breaking the disposition down by reason, so the clustered ingest path attributes OTLP partial-success exactly like the single-node path. 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) Cardinality ¶ added in v0.12.0
func (e *Engine) Cardinality(topN int) CardinalityStat
Cardinality summarizes the engine's label cardinality from the head's inverted index (which spans every stream ever seen, flushed or not). topN bounds the returned Top slice (≤0 returns all label names). It takes a read lock and does no backend I/O.
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) CloseWAL ¶ added in v0.4.0
CloseWAL closes the engine's open WAL segment file handle without flushing the head or checkpointing — modeling a process crash, where the OS reclaims open descriptors but the on-disk WAL segments survive for replay. The head is left as-is (and lost, as a crash would lose it). A crash-recovery test uses this to release the file handle so the WAL directory can be removed even on platforms that refuse to delete a file held open by a live process (Windows). No-op without a WAL.
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) HeadBytes ¶ added in v0.4.0
HeadBytes returns the engine's buffered record bytes — the in-flight memory measure for AppendLimits.MaxInFlightBytes. It counts the live head plus the buffers an in-flight flush has detached but not yet published: those stay resident, so the measure must not dip to zero (and let a second head in) for the duration of a slow flush.
func (*Engine) HeadRecordCount ¶
HeadRecordCount returns the number of records buffered in the head across all streams.
func (*Engine) IdentityBytes ¶ added in v0.37.0
IdentityBytes returns the resident bytes of the engine's identity state — the symbol table, the stream index, the postings lists and the per-stream out-of-order watermarks. It is reported separately from Engine.HeadBytes because a flush does not drain it: identities outlive their records and are cleared only by Engine.Reset, so this number tracks the engine's all-time stream count, not its buffered data.
func (*Engine) Keys ¶ added in v0.5.0
Keys enumerates the distinct attribute keys present across the engine's streams (head ∪ flushed parts) with at least one record in [start, end], each tagged with the scope(s) it appears in. A zero start AND end disables the time filter. Stream-identity keys (resource/scope) come from the authoritative series index; record-attribute keys come from the head buffers and each in-window part's persisted key footer. Window precision is part-granular and best-effort: a key whose records all fall outside the window may still be returned (harmless). Safe for concurrent use.
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. It assumes this node owns the prefix — it sweeps the part objects the index does not name (see [Engine.sweepOrphansLocked]).
func (*Engine) Merge ¶
Merge runs one size-tiered compaction cycle, dropping records older than retainFrom (retention; retainFrom ≤ 0 disables it). It compacts only a bounded group of similarly-sized parts plus any part retention must rewrite (see [selectMergeParts]) — not the whole part set — so a single merge's decoded working set is O(part size), not O(dataset). No-op when no tier has accumulated enough parts and no part needs retention. Records are append-only: a stream's records are concatenated across parts (no value dedup) and re-sorted by timestamp.
func (*Engine) MergeBacklog ¶ added in v0.12.0
MergeBacklog returns the parts a merge may still take — the flushed parts less the sealed ones, which no merge reconsiders. It is MergeShape.Backlog; use Engine.MergeShape for the rest of the selector's inputs.
func (*Engine) MergeRunning ¶ added in v0.12.0
MergeRunning reports whether a merge/compaction is currently executing on this engine (an in-memory liveness flag for introspection).
func (*Engine) MergeShape ¶ added in v0.37.0
func (e *Engine) MergeShape() MergeShape
MergeShape returns the selector's view of the engine's parts. It takes a brief read lock, does no backend I/O and decodes nothing, so it is safe to poll at dashboard cadence.
func (*Engine) MergeWith ¶ added in v0.37.0
func (e *Engine) MergeWith(ctx context.Context, opts MergeOptions) error
MergeWith is Engine.Merge with the merge parameterized: the one background-merge entry point, so compaction, retention, and a forced compaction are the same pass over the immutable parts.
func (*Engine) Parts ¶ added in v0.12.0
Parts returns an in-memory snapshot of the parts the engine can serve — its own, plus the ones it adopted from a rival writer's index — under a read lock, with no backend I/O and no decode, so it is safe to poll. It is the servable set rather than this writer's own, because that is what a read answers from and what the completeness accounting has to measure. For byte sizes, codecs, and chunk counts, use Engine.PartsDetailed.
func (*Engine) PartsDetailed ¶ added in v0.12.0
func (e *Engine) PartsDetailed(ctx context.Context) ([]PartDetailStat, error)
PartsDetailed augments Engine.Parts with each part's on-backend byte size, column/codec layout, and chunk (granule) count. It reads object sizes from the backend, so unlike Parts it is not hot-path-free — call it for a drill-down view, not a high-frequency poll. Each part is ref-held for the duration so a concurrent merge cannot reclaim its objects mid-read.
func (*Engine) PruneIdentities ¶ added in v0.37.0
PruneIdentities drops the stream identities no live data names any more — those the retention side of a merge left behind — rebuilding the resident index around the survivors. It returns the number removed (0 when nothing could have died, or when too little has).
Every node may call it, owner or replica: identity is scoped to the part that holds it, so the live set is derived from *this node's* parts and means exactly "what this node can still serve". A part a replica has not yet synced brings its identities with it when it arrives. It takes the same flush/merge exclusion as those paths, so no publish can add an identity underneath it.
func (*Engine) PruneIdentitiesWith ¶ added in v0.37.0
PruneIdentitiesWith is Engine.PruneIdentities with explicit options.
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, including the records an in-flight flush detached) and deletes this engine's objects, returning it to the empty state without reallocating. It waits for an in-flight flush or merge to finish first, so that operation cannot publish its part into the reset engine. Objects of parts a concurrent fetch is still reading are left for the deferred reclaim ([reclaimRetired], run by the next flush/merge cycle) rather than deleted underneath the reader. 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) Stats ¶ added in v0.10.0
Stats returns an in-memory snapshot of the engine's state under a single read lock (no backend I/O, no decode), safe to poll at dashboard cadence. Part byte sizes are not included.
func (*Engine) StreamCost ¶ added in v0.37.0
func (e *Engine) StreamCost(ctx context.Context, opts StreamCostOptions) ([]StreamCostStat, error)
StreamCost attributes the engine's flushed parts to streams (or, with StreamCostOptions.GroupBy, to a label's values): rows, decoded bytes, an approximate compressed share, and per-column distinct estimates.
It answers "which service is costing me, and why" — the diagnosis the label-cardinality and per-part views cannot give, since neither attributes bytes to a stream nor says anything about the cardinality of the values inside the columns, which is what drives the cost.
It is the heaviest introspection call in the engine: every accounted byte column of every live part is read and decoded once (int columns and the timestamp are accounted arithmetically, with no decode). Run it as an operator drill-down, not on a schedule, and narrow it with StreamCostOptions.Columns when only one column is in question. Each part is ref-held for the duration, so a concurrent merge cannot reclaim it mid-read.
func (*Engine) StreamCount ¶
StreamCount returns the number of distinct streams in the head.
func (*Engine) SyncWAL ¶ added in v0.3.0
SyncWAL fsyncs the engine's WAL, if any (the background WALSyncInterval path). No-op without a WAL.
type KeyInfo ¶ added in v0.5.0
KeyInfo is a distinct attribute key and the union of the scopes it was observed in. Key aliases engine-owned bytes (a head identity or a decoded part-footer entry); copy it to retain.
type KeyScope ¶ added in v0.5.0
type KeyScope uint8
KeyScope is a bitset of the scopes an attribute key was observed in. A key can appear in more than one — e.g. as a resource attribute on one stream and a per-record attribute on another — and the bitset records every scope, so a caller can tell a stream label from a record attribute (or both).
const ( // KeyScopeResource marks a resource attribute (part of the stream identity, postings-indexed). KeyScopeResource KeyScope = 1 << iota // KeyScopeScope marks an instrumentation-scope attribute (also stream identity). KeyScopeScope // KeyScopeRecord marks a per-record attribute (the serialized attrs column). KeyScopeRecord )
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 LabelCard ¶ added in v0.12.0
LabelCard is one label name's cardinality: how many streams carry it and how many distinct values it takes across them.
type MergeOptions ¶ added in v0.37.0
type MergeOptions struct {
// RetainFrom drops records older than this absolute unix-nanosecond cutoff (retention);
// ≤ 0 disables it.
RetainFrom int64
// Force compacts a bucket's unsealed parts whatever their tiers, instead of waiting for one tier
// to accumulate minTierParts of them — the operator escape from a part set the tier rule will
// never select. It bypasses the selection heuristic only: sealing, the time-bucket ladder, and
// the cumulative-bytes cap still bound what one merge decodes and holds.
Force bool
}
MergeOptions parameterizes a merge. The zero value is a plain compaction.
type MergeShape ¶ added in v0.37.0
type MergeShape struct {
// Parts is the flushed parts; Sealed those already at the cap, which no merge reconsiders;
// Backlog the rest — the parts a merge may still take.
Parts int
Sealed int
Backlog int
// Candidates is how many parts the next merge would select right now. 0 with a non-zero Backlog
// is the stuck state: parts remain mergeable but no tier of any time bucket holds minTierParts
// of them, which is what [MergeOptions.Force] exists to break.
Candidates int
// CapBytes is the seal threshold in effect, in decoded bytes (0 ⇒ sealing disabled).
CapBytes int64
// Tiers is how many distinct size tiers the unsealed parts fall into and LargestTierParts the
// count in the fullest of them — a LargestTierParts below minTierParts is why nothing merges.
// Tiers span the whole engine here, not one time bucket, so LargestTierParts is an upper bound
// on what the ladder can select.
Tiers int
LargestTierParts int
// MinTierParts is the same-tier count a merge waits for.
MinTierParts int
}
MergeShape is the merge selector's view of the flushed parts: the inputs to the decision the background merge makes each cycle. Without them an engine sitting on a part count it will never reduce is indistinguishable from an idle healthy one — the two differ only in whether the parts are sealed and whether one tier has accumulated enough of the rest.
type PartDetailStat ¶ added in v0.12.0
type PartDetailStat struct {
PartStat
Bytes int64 // sum of the part's backend object sizes
Chunks int // sparse-index granules: ceil(RowCount / GranuleSize)
Columns []ColumnStat // per-column physical layout
}
PartDetailStat augments PartStat with fields that need a backend read: the on-backend byte size (summed over the part's objects) and the column/codec layout and chunk count from the manifest (cached on the open part, so only Bytes incurs additional I/O).
type PartStat ¶ added in v0.12.0
type PartStat struct {
ID string // the part's backend key prefix
MinTime int64 // inclusive unix-ns bounds of the part's records
MaxTime int64
Series int // distinct streams in the part (len of its row-range index)
Rows int64 // total records (sum of the per-stream row spans)
// SizeBytes is the part's *decoded* footprint as recorded in its manifest — what the merge cap
// compares against, and so what explains why a part is or is not sealed. It needs no I/O. Unlike
// the metric engine's [engine.PartStat.SizeBytes], which is a size on disk, this is the size in
// memory: the record merge is bounded by what it holds, not by what it writes. 0 for a part
// written before the manifest recorded it (the row estimate then stands in internally).
SizeBytes int64
}
PartStat is one flushed part's in-memory shape (no backend I/O, no decode): identity, time bounds, and the stream/row counts from the part's in-memory row-range index.
type PruneOptions ¶ added in v0.37.0
type PruneOptions struct {
// Force runs the prune even when the background thresholds would skip it — an engine that has
// merged nothing away since the last pass, or one whose dead set is too small to pay for the
// rebuild. It is what an operator-triggered sweep wants.
Force bool
}
PruneOptions tunes an identity prune.
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()
// Restore merges an [SideStore.Encode] snapshot back into the live accumulator. The engine calls
// it when a flush fails after the snapshot+Reset: the records return to the head, so their side
// data must too. Content-addressing makes the merge a plain dedup with whatever the accumulator
// gained meanwhile.
Restore(snapshot map[string][]byte) error
// 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).
type Stats ¶ added in v0.10.0
type Stats struct {
Streams int64 // distinct streams ever seen (index span: head ∪ flushed)
HeadRecords int64 // records currently buffered in the head (unflushed)
HeadBytes int64 // buffered record bytes, head + in-flight flush (the in-flight memory measure)
// IdentityBytes is the resident identity state (symbols + stream index + postings + OOO
// watermarks) — memory a flush does not drain, and which no other counter here reports.
IdentityBytes int64
Parts int // flushed immutable parts
MinTime int64 // oldest flushed record time (unix ns); 0 when no parts
MaxTime int64 // newest record time across parts and the head (unix ns); 0 when empty
// OutOfSpace is set while the engine refuses writes because its backend is out of bytes or
// inodes. Reads still answer from what is on disk; it clears when a flush finds room again.
OutOfSpace bool
}
Stats is an in-memory snapshot of a record engine's state for introspection (no backend I/O).
type StreamCostOptions ¶ added in v0.37.0
type StreamCostOptions struct {
// GroupBy is the stream label (a resource or scope attribute name, e.g. "service.name") whose
// value keys the report. Empty groups by raw stream id. Grouping by label is the useful form:
// what an operator can act on is a service, and stream identity is a field policy that can
// change under them.
GroupBy string
// Columns restricts the byte columns that are decoded and attributed (nil ⇒ every byte column).
// The int columns and the timestamp are always accounted — they need no decode — so a narrowed
// request still reports complete row counts, and RawBytes/DiskBytes cover exactly the columns
// listed in each group's Columns.
Columns []string
// TopN keeps only the N costliest groups by RawBytes (≤0 ⇒ every group).
TopN int
// MaxSketchGroups bounds how many groups carry distinct estimates, which cost 8 KiB of sketch
// each for the duration of one column's pass (≤0 ⇒ [DefaultStreamCostSketchGroups]). The budget
// goes to the groups with the most rows; the rest report DistinctEstimated false.
MaxSketchGroups int
}
StreamCostOptions selects what Engine.StreamCost attributes and how much of it to estimate.
type StreamCostStat ¶ added in v0.37.0
type StreamCostStat struct {
Key string // the GroupBy label's value, or the stream id; empty ⇒ the label is absent
Streams int // distinct streams folded into this group
Rows int64
// RawBytes is the decoded footprint of the group's rows over the accounted columns.
RawBytes int64
// DiskBytes is APPROXIMATE. Compression is per column per frame and a frame spans whatever
// streams its rows fall in, so a group's compressed footprint is not directly measurable: each
// frame's compressed size is apportioned across the groups holding its rows by their raw-byte
// share. Rows are (stream, ts)-ordered, so most frames hold one stream and the estimate is
// close; a group narrower than a frame is the case where it is not.
DiskBytes int64
// DistinctEstimated reports whether the distinct counts below were computed for this group — see
// [StreamCostOptions.MaxSketchGroups]. False ⇒ Distinct/DistinctNormalized are 0, not zero.
DistinctEstimated bool
Columns []ColumnCostStat
}
StreamCostStat is one group's (one label value's, or one stream's) share of the engine's flushed parts. The head is not included — it holds no compressed bytes to attribute.
Source Files
¶
- admission.go
- bloom.go
- bytecol.go
- cols.go
- compact.go
- engine.go
- fetchcond.go
- fetcheval.go
- fetchlazy.go
- flush.go
- granule.go
- head.go
- index.go
- introspect.go
- keys.go
- limitscan.go
- merge.go
- mergecap.go
- mergedecode.go
- mergedict.go
- mergeshape.go
- part.go
- partidentity.go
- prune.go
- reclaim.go
- recordkeys.go
- recs.go
- replicate.go
- schema.go
- sidestore.go
- space.go
- streamcost.go
- timebucket.go
- walenc.go