Documentation
¶
Overview ¶
Package obs bundles the library's injected observability — a zap logger, an OTel tracer, and the metric instruments — built once from the embedder's configuration and handed to each subsystem.
As a library, oteldb/storage never owns a global logger, tracer, or meter: the embedder supplies them through Config (via storage.Options). Every handle is **no-op by default** — an unset logger becomes zap.Nop, an unset provider becomes the OTel noop provider — so an unconfigured store spans, logs, and counts nothing and pays no overhead. The library imports only the OTel API (never an SDK or exporter); the embedder owns the SDK, sampling, and pipelines.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExtractHTTP ¶
ExtractHTTP returns ctx augmented with the trace context read from the request headers, so a receiving handler's spans (and the engine spans below it) join the caller's trace. With the default no-op propagator it returns ctx unchanged.
func InjectHTTP ¶
InjectHTTP writes the trace context from ctx into the request headers, so a node-to-node RPC carries the distributed trace. It uses the globally-configured OTel propagator (set by the embedder, e.g. propagation.TraceContext{}); the default is a no-op that writes nothing, so an unconfigured store propagates nothing at no cost.
Types ¶
type Admission ¶
type Admission struct {
// contains filtered or unexported fields
}
Admission holds the ingest/admission meta-metrics (DESIGN §8a — observability is mandatory for overload control). They are recorded in **bulk** (one Add per write call per reason, never per-point), so they cost nothing on the hot inner loops. With the no-op meter every Add is a no-op.
func (*Admission) Accepted ¶
Accepted records n points accepted for the given signal. A zero n is ignored.
func (*Admission) Overflowed ¶ added in v0.10.0
Overflowed records n points routed to an overflow series past the soft cardinality budget for the given signal. A zero n is ignored.
type Backend ¶
type Backend struct {
// contains filtered or unexported fields
}
Backend instruments the L1 object-store operations.
type Cluster ¶ added in v0.40.0
type Cluster struct {
// contains filtered or unexported fields
}
Cluster instruments this node's standing in the cluster member set — the state that decides whether any peer routes anything here at all. self_absent is the alertable form of a node that is up and serving but missing from etcd: the failure this cannot be inferred from any other signal, because the node itself looks perfectly healthy while it is happening.
func (*Cluster) Record ¶ added in v0.40.0
Record publishes this node's membership standing: absent is whether it is currently missing from the member set, rejoins the re-registrations since the previous call.
func (*Cluster) Routed ¶ added in v0.43.0
Routed accounts n points or records this node routed to a shard primary, tagged with the signal and how the routing ended: "accepted" (a primary took them), "rejected" (a primary's admission refused them) or "failed" (the route errored, so nothing was stored).
It is recorded on the routing node, not the storing one. storage.ingest.accepted is per storing engine and storage.flush.* per flushing engine, so on a cluster the node that takes a write in, the node that flushes it and the node that ends up holding it are three different nodes, and none of those counters describes what a coordinator routed. A zero n is ignored.
type Config ¶
type Config struct {
Logger *zap.Logger
TracerProvider trace.TracerProvider
MeterProvider metric.MeterProvider
}
Config is the embedder-supplied observability configuration. A nil field selects the no-op implementation for that pillar.
type Disk ¶ added in v0.40.0
type Disk struct {
// contains filtered or unexported fields
}
Disk instruments the medium's ability to take what the node accepts. A full disk or an exhausted inode table is not a transient backend fault: the node keeps acking writes it cannot store, so the two states need separate signals — a gauge that stands while the engine refuses writes, and a counter of the writes it refused.
func (*Disk) Record ¶ added in v0.40.0
Record publishes a signal's disk-pressure state: whether its engine is currently refusing writes for want of room, and how many writes it refused since the previous call. Both are published together, from the flush that decides the state — the ingest path that counts a rejection carries no context of its own, and a counter is as useful at flush cadence as per write.
type Fetch ¶
type Fetch struct {
// contains filtered or unexported fields
}
Fetch instruments a fetch over the head ∪ parts.
func (*Fetch) ForcedAdmission ¶ added in v0.37.0
ForcedAdmission accounts one query admitted over the decode-memory ceiling because its wait made no progress. It is a liveness escape, so a non-zero rate means the budget is oversubscribed (or a caller holds several unscoped reads open) and the ceiling is not holding.
type Flush ¶
type Flush struct {
// contains filtered or unexported fields
}
Flush instruments a head→part flush.
type Head ¶ added in v0.43.0
type Head struct {
// contains filtered or unexported fields
}
Head is the unflushed side of the engine, which no storage.parts.* gauge covers: the rows, bytes and streams a flush has yet to make durable, and how long they have been waiting. It is the state flush backpressure and the process' resident memory both live in — a head that stops draining shows here first, and nowhere else until a part finally appears.
func (*Head) Record ¶ added in v0.43.0
func (h *Head) Record(ctx context.Context, sig string, bytes, items, series, identityBytes int64, age time.Duration)
Record publishes one signal's head, summed over the tenants this node holds (age is the oldest of them — the worst flush lag, not an average that a fresh head would hide). series and identityBytes span the head's all-time identity set, which a flush does not drain: they are the memory that only [storage.Storage] restart or an identity prune returns.
type Merge ¶
type Merge struct {
// contains filtered or unexported fields
}
Merge instruments a background merge (compaction/retention/downsample/recompress).
func (*Merge) Record ¶
func (m *Merge) Record(ctx context.Context, sig string, dur time.Duration, partsIn, bytesIn, bytesOut int64)
Record accounts one merge that compacted partsIn source parts of bytesIn bytes into bytesOut bytes in dur, for the given signal. The byte pair is what makes write amplification visible: bytesOut against storage.flush.bytes is how many times the engine rewrites what it ingests, and bytesOut/bytesIn is what one merge cycle gains.
type Obs ¶
type Obs struct {
Log *zap.Logger
Tracer trace.Tracer
Admission *Admission
Head *Head
Flush *Flush
Merge *Merge
Parts *Parts
Fetch *Fetch
Backend *Backend
WAL *WAL
RPC *RPC
Cluster *Cluster
Disk *Disk
}
Obs is the observability handle passed to each subsystem. Log and Tracer are always non-nil (no-op when unconfigured); Admission holds the ingest meta-metrics.
func New ¶
New builds the observability handle, defaulting each unset pillar to its no-op implementation. It returns an error only if the meter rejects an instrument name (it does not for valid names).
func NewNop ¶
func NewNop() *Obs
NewNop returns a fully no-op handle (the default for tests and unconfigured stores). It never errors.
func (*Obs) Base ¶
Base installs the injected logger as the zctx base in ctx, so any layer below can retrieve a trace-correlated logger via zctx.From without holding the obs handle. Seed it at each operation entry, before starting the span; zctx.From then attaches span_id/trace_id from the active span.
type Parts ¶ added in v0.37.0
type Parts struct {
// contains filtered or unexported fields
}
Parts reports the merge selector's view of a signal's flushed parts as gauges: how many parts there are, how many are sealed (no merge will reconsider them), how many a merge may still take, how many the next merge would select, and the seal threshold in effect. Gauges rather than a log line because the question they answer — is compaction ever going to reduce this part count? — is asked of a dashboard over time, not of one cycle.
func (*Parts) Record ¶ added in v0.37.0
func (p *Parts) Record(ctx context.Context, sig string, total, sealed, backlog, candidates, capBytes, bytes int64)
Record publishes one signal's part shape, summed over the tenants this node holds. The values are tagged by signal only: tenant ids are unbounded, and [storage.Storage.Inspect] is the per-tenant surface.
type RPC ¶
type RPC struct {
// contains filtered or unexported fields
}
RPC instruments the node-to-node cluster transport's reliability behavior: how often calls are attempted, retried, or hedged (an opportunistic concurrent attempt fired because the in-flight one was slow). Counts are tagged by op ("read"/"write"/"series"/"side"), so a rising retry/hedge rate localizes a degrading link or peer.
func (*RPC) Attempt ¶
Attempt accounts one completed transport attempt (including the first) for op, tagged with how it ended: "ok", "timeout" (the per-attempt deadline fired), "canceled" (the caller went away) or "error". It is recorded on completion rather than on launch precisely so the result is known — without it the retry and hedge counters describe reactions to failures the attempt counter never admits to, and no error rate can be computed.
func (*RPC) ShardAbsent ¶ added in v0.37.0
ShardAbsent accounts one read of a shard this node is a ring owner of but holds no data for, so the read failed over to another owner instead of answering empty. A sustained rate means the ring and the data disagree — a rebalance whose backfill has not caught up, or a lagging membership view.
func (*RPC) ShardIncomplete ¶ added in v0.38.0
ShardIncomplete accounts one read of a shard this node holds but cannot fully answer for: it came back from a restart (or a rebalance) without the head the shard held unflushed, so the query window overlaps data only another owner has, and the read failed over. It clears once the shard's parts cover that window; a sustained rate means no owner is flushing.
type WAL ¶
type WAL struct {
// contains filtered or unexported fields
}
WAL instruments the write-ahead log. The recording methods use a background context (the WAL writer carries none) and are called at append/fsync/rotation granularity, never per sample.
func (*WAL) Record ¶ added in v0.43.0
Record publishes a signal's WAL footprint: the segments on disk and the bytes they hold. It is the durability backlog — segments accumulate exactly while flushes are not retiring them, so a rising count is the same stall storage.head.age shows, seen from the disk.