Skip to main content

Observability

There are three ways to observe a Clustron Zaris store, from quickest to most integrated:

  • The live console. Watch-ZrStoreMetrics renders a store's throughput and activity live, per node, with no setup. It is the fastest way to confirm a store is serving traffic or to spot an imbalance during an incident. See Monitoring and metrics.
  • The metrics Zaris emits. Each node records a catalog of operation, replication, and memory metrics. Some are surfaced by the live console; a separate set of OpenTelemetry instruments is built for export.
  • OTLP export to your own stack. With telemetry enabled, a node exports its OpenTelemetry instruments over OTLP (gRPC/HTTP) to any collector — Grafana/Tempo, Prometheus (via the collector), Jaeger, Datadog, Azure Monitor, and so on.

This article is a reference for the second and third: what metrics exist, and how to turn on export.

note

Telemetry export is off by default and opt-in. A node with no telemetry configuration initializes no exporter and pays no overhead — the metric instruments stay inert until an OpenTelemetry MeterProvider subscribes to them. Metrics, unlike traces, are never sampled: when the catalog is on, counters are recorded at full fidelity.

Two metric planes

Zaris emits metrics on two independent planes, and it helps to know which is which:

  • The live-console plane. A set of counters and gauges (the short keys below — get, put, mem.*, repl.*, …) fed by the same internal accountant the node acts on, surfaced by Watch-ZrStoreMetrics and the web console. These are what you see in the console's Zaris metric category.
  • The OpenTelemetry plane. A System.Diagnostics.Metrics meter named Clustron.Zaris that publishes the clustron.zaris.* instruments below — the node registers this meter with the OpenTelemetry SDK for OTLP export. As of 1.1.0 the node also bridges the internal live-console catalog onto this meter, so the short keys below can be exported over OTLP alongside the semantic instruments.
Metric keys were shortened in 1.1.0

The live-console keys dropped the zaris. / memory. prefixes — for example zaris.get is now get, and memory.active.bytes is now mem.active. If you built dashboards or alerts against the old names, update them to the keys shown below.

Metrics catalog

The names below are the real metric keys emitted by the code. Grouped tables follow.

Throughput and store state

Per-operation counters and the store-size gauge, surfaced in the live console.

MetricWhat it measures
get / get.err / get.missGet operations served / failed / missed (key absent).
put / put.errPut operations served / failed.
del / del.errDelete operations served / failed.
clearClear operations.
reqsTotal client requests handled.
store.itemsItems held by this node (active + replica); a gauge.
store.scan / store.scan.errScan queries / failures.
store.search / store.search.errSearch queries / failures.
store.agg / store.agg.errAggregate queries / failures.
store.groupby / store.groupby.errGroup-by queries / failures.
migrPartition migrations.
migr.putPuts applied as part of a migration.
ttl.expiredItems removed by TTL expiration.
ttl.sweepsTTL sweep passes run.
ttl.dueItems found due for expiration in a sweep.

Replication

Per-node replication traffic and delivery health.

MetricWhat it measures
repl.sent / repl.recvReplication messages sent / received.
repl.errReplication sends that failed.
repl.puts.sent / repl.puts.recvPuts replicated outbound (primary → replica) / inbound.
repl.dels.sent / repl.dels.recvDeletes replicated outbound / inbound.
repl.clears.sentClears replicated (as per-key deletes) to replicas.
repl.locks.sent / repl.locks.recvLock state replicated outbound / inbound.
repl.revertedReplicated operations reverted.
repl.ack.sent / repl.ack.recv / repl.ack.errReplication ACKs sent / received / failed.
repl.sync.timeoutSynchronous-replication waits that timed out.
repl.unackedBatches sent but not yet ACKed by a replica (aggregate). Near zero in steady state; climbing means a replica is not acking (wedged / half-open); unbounded means delivery is failing silently.
sync.reqFull replica-sync operations requested.
sync.chunksSnapshot chunks received during a replica sync.
sync.done / sync.errReplica syncs that completed / failed.
sync.reads.blockedReads blocked on a replica because its sync is still pending.
sync.writes.rejWrites rejected by a node acting as a replica.
sync.surplusSyncs that finished with the replica ahead of the source's frozen snapshot (a live write landed mid-resync).

Memory and eviction

Footprint gauges published each reconcile tick, plus eviction counters. These are fed by the same accountant that enforces the ceiling, so the dashboard and the node never disagree. For the model behind these numbers, see Memory and eviction.

MetricWhat it measures
mem.activeAccounted bytes of active-owned data (compared against the ceiling).
mem.replicaAccounted bytes of replica mirrors held (not charged to the ceiling).
mem.indexBytes attributed to indexes and auxiliary structures.
mem.totalTotal accounted footprint (active + replica + index).
mem.ceilingThe configured per-process ceiling on active-owned data.
mem.headroomceiling − total footprint on this node.
mem.usageTotal footprint ÷ ceiling, expressed per-mille (0–1000 = 0.0–1.0).
mem.over1 while the node's total footprint is above the eviction trigger threshold, else 0.
mem.itemsCount of active-owned items.
mem.evictItems evicted to stay under the ceiling.
mem.evict.bytesBytes reclaimed by eviction.

The eviction metrics carry a policy attribute (lru, lfu, ttl-aware, no-eviction) and, on admission rejections, a reason attribute (ceiling_exceeded, max_item_size, tenant_ceiling, migration).

note

There is no dedicated client-connection-count metric in the emitted catalog. Connection counts and per-node age are surfaced in the management console's Nodes view, not as a store metric.

Instruments exported over OpenTelemetry

These are the instruments published on the Clustron.Zaris meter — the plane the node registers with the OpenTelemetry SDK for OTLP export.

InstrumentKindUnitWhat it measures
clustron.zaris.operation.countCounter{operation}Per-operation throughput (successes + failures).
clustron.zaris.operation.durationHistogrammsPer-operation latency.
clustron.zaris.operation.errorsCounter{error}Per-operation error count.
clustron.zaris.topology.zero_segment_owned_partitionsGauge{partition}Partitions with a live owner but owning 0 segments (zombie).
clustron.zaris.topology.wrong_owner_partitionsGauge{partition}Partitions whose live owner is not a member of the partition group.
clustron.zaris.topology.under_replicated_partitionsGauge{partition}Partitions with fewer live replicas than the map expects.
clustron.zaris.replication.replica_lag_batchesGauge{batch}Batches sent to a present replica but not yet acked, tagged by replica.
clustron.zaris.write.during_ownership_transitionCounter{write}Acked writes admitted while a segment's ownership was transitioning (the write-fence did not hold them).
clustron.zaris.mapless_mint.decisionsCounter{decision}Mapless-leader mint decisions by class.
clustron.zaris.mapless_mint.fresh_peer_digestsHistogram{peer}Fresh peer map-digests observed at each mapless mint decision.

The operation instruments are tagged with metadata only — never a stored key, value, or payload:

TagValues
clustron.operationThe operation / command name.
clustron.originnative (internal cluster control plane) or client (client-server request).
clustron.statusok or error.
clustron.error_categoryA short label for the failure class (present on errors).

Every exported metric also carries the resource attributes set at export time: service.name = clustron-zaris, service.instance.id = the node id, plus clustron.store and clustron.node.

Export via OpenTelemetry (OTLP)

Telemetry is configured in the node's Telemetry configuration section (for example, the Telemetry section of appsettings.json). The node's OpenTelemetry bootstrap reads these keys:

KeyTypeDefaultEffect
Telemetry:Tracing:EnabledboolfalseTurn trace export on.
Telemetry:Tracing:SampleRatiodouble1.0Head-based trace sample ratio (1.0 = all). Metrics are never sampled.
Telemetry:Tracing:ConsoleboolfalseAlso emit to the console exporter (local dev); applies to metrics too.
Telemetry:Metrics:Enabledbool?nullTurn the metrics catalog on. null follows the tracing decision — enabling telemetry gives you both — while an explicit true/false scopes metrics independently (metrics-only or traces-only).
Telemetry:Otlp:Endpointstring""OTLP endpoint (for example http://collector:4317). Empty means console-only when telemetry is enabled.

The standard OTEL_EXPORTER_OTLP_ENDPOINT environment variable also switches export on and is honored as the endpoint when Telemetry:Otlp:Endpoint is not set.

A node exports over OTLP when tracing is enabled in config or the OTEL_EXPORTER_OTLP_ENDPOINT env var is set; metrics follow the tracing decision unless scoped independently. If telemetry is on but no endpoint is resolved, the node falls back to the console exporter (the local-dev default).

Point a node at a collector

Enable metrics and send them to a collector on collector:4317:

{
"Telemetry": {
"Tracing": { "Enabled": true, "SampleRatio": 1.0 },
"Metrics": { "Enabled": true },
"Otlp": { "Endpoint": "http://collector:4317" }
}
}

Or, equivalently, with the standard environment variable:

OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
tip

For a metrics-only setup, set Telemetry:Metrics:Enabled to true and leave Telemetry:Tracing:Enabled at false. The metrics catalog exports without paying for trace spans. To scrape with Prometheus, point the node at an OpenTelemetry Collector and expose the collector's Prometheus endpoint.

The configuration reference summarizes the logging and telemetry sections alongside the rest of a store's settings — see Configuration reference.

What to watch

A short set of signals worth alerting on, tied to the real metrics above:

  • Memory near the ceiling. mem.usage approaching 1000, or mem.over = 1, means the node is at or past its eviction trigger; expect mem.evict and mem.evict.bytes to climb. Falling mem.headroom is the leading indicator.
  • Replication falling behind. A climbing or non-zero repl.unacked (or a sustained clustron.zaris.replication.replica_lag_batches for a specific replica) means a replica is not acking — a wedged or half-open connection dropping acks. repl.sync.timeout rising points the same way.
  • Errors and unavailability. A rising clustron.zaris.operation.errors (broken down by clustron.error_category), or growth in the *.err operation counters (get.err, put.err, del.err), signals timeouts or unavailable partitions. Correlate with the client-side retry behavior in Client resilience patterns.
  • Topology health. Non-zero clustron.zaris.topology.wrong_owner_partitions, ...zero_segment_owned_partitions, or ...under_replicated_partitions means the cluster's ownership or replication is not where the map expects it.
  • Write-fence gaps. A non-zero clustron.zaris.write.during_ownership_transition reveals an acked write admitted during an ownership handoff — the window in which an acknowledged write can be lost at cutover. Correlate a spike with any lost keys.

Next steps