OpenTelemetry

The zero-cache service embeds the JavaScript OTLP Exporter and can send logs, traces, and metrics to any standard otel collector.

To enable otel, set the following environment variables then run zero-cache as normal:

OTEL_EXPORTER_OTLP_ENDPOINT="<your otel endpoint>"
OTEL_EXPORTER_OTLP_HEADERS="<auth headers from your otel collector>"
OTEL_RESOURCE_ATTRIBUTES="<resource attributes from your otel collector>"
OTEL_NODE_RESOURCE_DETECTORS="env,host,os"

Grafana Cloud Walkthrough

Here are instructions to setup Grafana Cloud, but the setup for other otel collectors should be similar.

  1. Sign up for Grafana Cloud (Free Tier)
  2. Click Connections > Add Connection in the left sidebar add-connection
  3. Search for "OpenTelemetry" and select it
  4. Click "Quickstart" quickstart
  5. Select "JavaScript" javascript
  6. Create a new token
  7. Copy the environment variables into your .env file or similar copy-env
  8. Start zero-cache
  9. Look for logs under "Drilldown" > "Logs" in left sidebar

Distributed Tracing

You can enable end-to-end trace correlation from your frontend through zero-cache to your API server. This allows you to see the full request flow in your tracing UI.

To enable this, provide a getTraceparent callback when creating your Zero client:

import {ZeroProvider} from '@rocicorp/zero/react'
import {propagation, context} from '@opentelemetry/api'
 
function getTraceparent() {
  const carrier: Record<string, string> = {}
  propagation.inject(context.active(), carrier)
  return carrier.traceparent
}
 
return (
  <ZeroProvider
    /* ... other options ... */
    getTraceparent={getTraceparent}
  >
    <App />
  </ZeroProvider>
)

This callback is called before sending WebSocket messages that trigger API server calls (push, changeDesiredQueries, initConnection). The returned W3C traceparent header is forwarded through zero-cache to your API server, where it can be used to continue the trace.

Metrics Reference

zero.server

MetricTypeUnitDescription
zero_server_uptimeGaugesCumulative uptime, starting from when requests are served
zero_server_api_requestsCounterCalls to user mutate and query APIs, including cleanup and auth-validation operations
zero_server_api_request_durationHistogramsEnd-to-end user API request duration, including retries
zero_server_api_attemptsCounterHTTP fetch attempts made while calling user API endpoints
zero_server_api_attempt_durationHistogramsDuration of each API HTTP attempt, excluding retry delays
zero_server_api_in_flightUpDownCounterAPI requests currently in flight
zero_server_startup_durationHistogramsTime from starting zero-cache until it is ready
zero_server_worker_startup_durationHistogramsTime from starting a worker until it is ready

zero.replica

MetricTypeUnitDescription
zero_replica_db_sizeGaugebytesSize of the replica's main db file (excludes WAL)
zero_replica_wal_sizeGaugebytesSize of the replica's WAL file
zero_replica_wal2_sizeGaugebytesSize of the replica's WAL2 file (only if using wal2 mode)
zero_replica_backup_lagGaugemsTime since last litestream backup. Expected to sawtooth from 0 to ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES
zero_replica_purge_blockedCounterNumber of change-log purges blocked because the actual backup state could not be verified or is stale
zero_replica_litestream_restore_runsCounterLitestream restore runs
zero_replica_litestream_restore_attemptsCounterLitestream restore subprocess attempts
zero_replica_litestream_restore_db_bytesCounterbytesSQLite database bytes restored by successful Litestream restores
zero_replica_litestream_restore_durationHistogramsWall-clock duration of Litestream restore runs
zero_replica_litestream_restore_wait_durationHistogramsTime spent waiting for replication-manager snapshot status before restoring
zero_replica_litestream_restore_process_durationHistogramsWall-clock duration of Litestream restore subprocesses
zero_replica_litestream_restore_validation_durationHistogramsTime spent validating restored replica databases
zero_replica_litestream_backup_process_runsCounterLitestream backup process exits
zero_replica_litestream_backup_process_durationHistogramsRuntime of Litestream backup subprocesses before exit
zero_replica_litestream_backup_list_durationHistogramsTime to list the Litestream backup destination
zero_replica_litestream_backup_verification_durationHistogramsTime to verify backup state in the destination
zero_replica_litestream_snapshot_reservation_durationHistogramsTime snapshot reservations are held while view-syncers restore and subscribe

zero.replication

MetricTypeUnitDescription
zero_replication_upstream_lagGaugemsLatency from sending a replication report to receiving it in the stream
zero_replication_replica_lagGaugemsLatency from receiving a replication report to it reaching the replica
zero_replication_total_lagGaugemsMeasured end-to-end latency of the most recently received replication report; does not grow if reports stop arriving
zero_replication_last_total_lagGaugemsAlias of zero_replication_total_lag, retained for dashboards that explicitly use the non-extrapolated metric
zero_replication_upstream_clock_skewGaugemsEstimated offset of the upstream database clock relative to zero-cache; positive values mean upstream is ahead
zero_replication_lag_report_retriesCounterReplication lag reports retried because an expected report did not arrive before the next report interval
zero_replication_eventsCounterNumber of replication events processed
zero_replication_transactionsCounterCount of replicated transactions
zero_replication_changesCounterCount of replicated changes, including DML and DDL statements
zero_replication_slot_healthGauge1One-hot status for the active logical replication slot: ok, unreserved, lost, missing, or unknown
zero_replication_slot_retained_wal_bytesGaugebytesWAL bytes retained by the active logical replication slot
zero_replication_slot_safe_wal_bytesGaugebytesRemaining WAL capacity before the active logical replication slot is lost; omitted when Postgres reports no value
zero_replication_initial_sync_runsCounterNumber of initial-sync runs
zero_replication_initial_sync_durationHistogramsWall-clock duration of an initial-sync run
zero_replication_initial_sync_copy_durationHistogramsWall-clock duration of the COPY phase for a successful initial-sync run
zero_replication_initial_sync_copy_other_durationHistogramsInitial-sync duration excluding SQLite flush and index time for a successful run
zero_replication_initial_sync_flush_durationHistogramsTotal SQLite flush time for a successful initial-sync run
zero_replication_initial_sync_index_durationHistogramsSQLite index creation time for a successful initial-sync run
zero_replication_initial_sync_rowsCounterRows copied during successful initial-sync runs
zero_replication_initial_sync_copy_streamCounterbytesPostgreSQL COPY stream bytes, including failed runs; reported in approximately 8 MiB batches and flushed when the stream ends
zero_replication_initial_sync_completed_copy_streamCounterbytesPostgreSQL COPY stream bytes processed during successful initial-sync runs
zero_replication_initial_sync_copy_chunksCounterPostgreSQL COPY stream chunks processed during initial sync; batched with COPY-stream updates and flushed when the stream ends
zero_replication_shadow_sync_runsCounterNumber of shadow initial-sync runs, labeled by result
zero_replication_shadow_sync_durationHistogramsWall-clock duration of a shadow initial-sync run, labeled by result
zero_replication_flow_control_active_subscribersGaugeActive change-stream subscribers receiving live changes
zero_replication_flow_control_queued_subscribersGaugeChange-stream subscribers waiting for the current transaction to finish before activation
zero_replication_flow_control_pending_messagesGaugeDownstream change-stream messages not yet acknowledged by subscribers
zero_replication_flow_control_backlog_messagesGaugeLive change-stream messages buffered while subscribers catch up
zero_replication_flow_control_backlog_bytesGaugebytesLive change-stream bytes buffered while subscribers catch up
zero_replication_flow_control_max_backlog_bytesGaugebytesMaximum live change-stream bytes buffered by a single subscriber
zero_replication_flow_control_waitsCounterCompleted flow-control checkpoints
zero_replication_flow_control_wait_durationHistogramsTime replication waits at flow-control checkpoints

zero_replication_total_lag and zero_replication_last_total_lag now report the same latest measured round trip and do not grow when reports stop arriving. Use zero_replication_lag_report_retries to detect a stalled or missing report stream.

zero.sync

MetricTypeUnitDescription
zero_sync_max_protocol_versionGaugeHighest sync protocol version seen from connecting clients
zero_sync_active_clientsUpDownCounterNumber of currently connected sync clients
zero_sync_active_client_groupsGaugeNumber of active ViewSyncerService instances in a syncer worker
zero_sync_queriesGaugeActive IVM pipelines across all client groups in a syncer worker
zero_sync_rowsGaugeCVR-tracked rows across all client groups in a syncer worker
zero_sync_serving_lagGaugemsLongest time locally ready replica changes have remained unserved across eligible active client groups
zero_sync_serving_lag_statsGaugemsDistribution of serving lag across eligible active client groups
zero_sync_serving_lagging_client_groupsGaugeEligible active client groups with locally ready replica changes not yet served to clients
zero_sync_view_syncer_lagHistogramsTime from replica changes becoming ready to ViewSyncer output, sampled once per minute per eligible group
zero_sync_view_syncer_hydrationHistogramsTime from a ViewSyncer query sync requiring hydration until output, per client group
zero_sync_e2e_serving_lagHistogramsCompletion latency from the upstream transaction commit through ViewSyncer output
zero_sync_e2e_serving_lag_clampsCounterNegative end-to-end lag observations clamped to zero because the upstream clock was ahead
zero_sync_lock_wait_timeHistogramsTime spent waiting to acquire the ViewSyncerService lock per operation
zero_sync_pipeline_resetsCounterCount of pipeline resets, labeled by reason
zero_sync_hydrationCounterNumber of query hydrations
zero_sync_hydration_timeHistogramsTime to hydrate a query
zero_sync_advance_timeHistogramsTime to advance all queries for a client group after applying a transaction
zero_sync_poke_timeHistogramsTime per poke transaction (excludes canceled/noop pokes)
zero_sync_poke_transactionsCounterCount of poke transactions
zero_sync_poke_rowsCounterCount of poked rows
zero_sync_cvr_load_attemptsCounterCVR load attempts
zero_sync_cvr_load_durationHistogramsTime to load a CVR
zero_sync_cvr_flush_attemptsCounterCVR flush attempts
zero_sync_cvr_flush_timeHistogramsTime to flush a CVR transaction
zero_sync_cvr_rows_flushedCounterNumber of changed rows flushed to a CVR
zero_sync_websocket_open_connectionsUpDownCounterOpen client WebSocket connections
zero_sync_websocket_connection_attemptsCounterClient WebSocket connection attempts
zero_sync_websocket_connection_successesCounterClient WebSocket connections successfully initialized
zero_sync_websocket_connection_failuresCounterClient WebSocket connection attempts that failed before initialization
zero_sync_websocket_errorsCounterClient WebSocket error events
zero_sync_ivm_advance_timeHistogramsTime to advance IVM queries in response to a single change
zero_sync_ivm_conflict_rows_deletedCounterRows deleted because they conflicted with an added row
zero_sync_query_transformationsCounterNumber of query transformations performed
zero_sync_query_transformation_timeHistogramsTime to transform custom queries via API server
zero_sync_query_transformation_hash_changesCounterTimes a query transformation hash changed
zero_sync_query_transformation_no_opsCounterTimes a query transformation was a no-op
zero_sync_query_row_set_signature_driftsCounterUnchanged query rehydrations whose row-set signature differs from the CVR, forcing a config-version bump
zero_sync_query_same_hash_rehydrations_forced_bumpCounterSame-hash query rehydrations that force a config-version bump so changed rows are delivered

Serving-lag metrics include only client groups with at least one connected client and a validated background connection context. Retained groups without an eligible connection do not contribute lag.

zero.mutation

MetricTypeUnitDescription
zero_mutation_crudCounterNumber of CRUD mutations processed
zero_mutation_customCounterNumber of custom mutations processed
zero_mutation_pushesCounterNumber of pushes processed