Skip to main content

Performance diagnostics

Performance diagnostics show where a CamusDB node spends its time. They connect a slow result in your application to the server stage that caused it.

CamusDB can expose opt-in diagnostics for a standalone node. While diagnostics are enabled, the server exports Prometheus metrics, and it can also export sampled OpenTelemetry traces.

  • Prometheus is a monitoring system. It reads numeric metrics from an HTTP endpoint at a fixed interval, stores them, and uses them for charts and alerts. Grafana is a common dashboard tool on top of it.
  • OpenTelemetry is a standard for traces. A trace records the steps of one request and how long each step took. A collector receives the traces, and a tracing tool shows them as a timeline.

Why use performance diagnostics​

Use diagnostics when a workload is slower than you expect and you do not know which part is slow. They let you connect a workload result to a server stage: request handling, SQL execution, scans, transaction commits, or write-ahead log (WAL) activity.

Diagnostics also give you history. Prometheus keeps the metrics over time, so you can compare a slow hour with a normal one, or one build with the next.

You do not need them for a first look at one slow query. Use EXPLAIN (ANALYZE), the slow query log, or query activity for that.

For repeated statements, statement statistics reveal cumulative cost even below the slow-query threshold. Index usage shows whether reads and constraint checks justify each index's write cost.

For a quick inspection from SQL, use SHOW ENGINE STATS. It reports metrics from the embedded storage engine, Kahuna, and the embedded consensus library, Kommander. It has its own switch, engine_metrics_enabled, and works without the exporter on this page.

Diagnostics are off by default. With diagnostics.enabled: false, CamusDB registers no exporter, no endpoint, and no background collector. The current diagnostics path serves a standalone node.

Enable diagnostics​

Add a diagnostics section to the selected config.yml file:

diagnostics:
enabled: true
prometheus_enabled: true
prometheus_path: /metrics
otlp_endpoint:
trace_sample_ratio: 0.01
include_runtime_metrics: true
SettingDefaultMeaning
diagnostics.enabledfalseThe main switch. While it is false, CamusDB exports nothing.
diagnostics.prometheus_enabledfalseBind the Prometheus scrape endpoint on the HTTP listener.
diagnostics.prometheus_path/metricsThe scrape path. It must start with /.
diagnostics.otlp_endpointemptyAn optional absolute URL of an OTLP collector for traces.
diagnostics.trace_sample_ratio0.01The head sample ratio for traces, from 0 to 1.
diagnostics.include_runtime_metricstrueInclude .NET runtime metrics, such as garbage collector and thread pool metrics.

Protect the Prometheus endpoint, or bind it only on a trusted interface. It exposes operational metadata and adds no authentication of its own.

CAMUS_CONFIG_PATH=/path/to/config.yml points the server at another configuration file. CAMUS_DIAGNOSTICS_RUN_ID attaches a stable run id as the OpenTelemetry resource attribute camus.run_id.

Server metrics​

CamusDB exports these metrics from the CamusDB.Server meter:

InstrumentPrometheus nameTypeTags
camus.request.countcamus_request_count_totalcounteroperation, transport, outcome
camus.request.durationcamus_request_duration_millisecondshistogramoperation, transport, outcome
camus.request.in_flightcamus_request_in_flightup-downtransport
camus.execute.durationcamus_execute_duration_millisecondshistogramoperation, statement
camus.sql.parse.cachecamus_sql_parse_cache_totalcounterresult
camus.sql.parse.durationcamus_sql_parse_duration_millisecondshistogramnone
camus.query.rowscamus_query_rows_totalcounterscan, stage
camus.query.scan.durationcamus_query_scan_duration_millisecondshistogramscan
camus.query_cache.requestscamus_query_cache_requests_totalcounterresult
camus.transaction.countcamus_transaction_count_totalcounteroperation, outcome
camus.transaction.activecamus_transaction_activeup-downtransaction_mode
camus.transaction.commit.durationcamus_transaction_commit_duration_millisecondshistogramoutcome
camus.transaction.staged_mutationscamus_transaction_staged_mutationshistogramnone

Tag values come from a bounded set. CamusDB never uses SQL text, row keys, ids, messages, or user values as metric tags.

When kahuna.wal_storage: solarium, the metrics exporter also subscribes to Solarium's WAL meter. It exposes the same raft.wal.* instruments used to observe RocksDB WAL behavior, so WAL gauges remain available after changing storage engines.

Dependency metrics​

The diagnostics exporter also subscribes to the meters of the embedded dependencies and the runtime:

  • Kahuna metrics, including key/value write batches, write entries, durable transaction counters, and transaction admission gauges.
  • Kommander metrics, including batches written to the Raft log, WAL operations, batch size, executor duration, and client queue depth.
  • Row-level TTL counters, including expired rows, skips after a re-check, completed key spans, and planned runs.
  • .NET runtime metrics, including the garbage collector, heap, allocations, thread pool, and process.

These signals help when a run is limited by:

  • WAL fsync latency.
  • Storage batch density.
  • Runtime pressure.
  • Scan cost.
  • Request concurrency.

Range splitting also publishes counters through the Kahuna meter. When key-range sharding is enabled, inspect them with:

SHOW ENGINE STATS LIKE 'kahuna.range%';

The most useful rows are kahuna.range.splits, kahuna.range.split.indivisible_refusals, kahuna.range.split.no_relief_skips, kahuna.range.split.settle_skips, and kahuna.range.merge.warm_skips. Use them together with SHOW RANGES: SHOW RANGES shows the current placement, while these counters explain what the splitter attempted.

Traces​

While diagnostics.otlp_endpoint is set, CamusDB can emit sampled traces from the CamusDB.Server activity source. A typical request trace contains trace spans (one timed step each) for parsing, execution, storage reads, and the transaction commit.

diagnostics.trace_sample_ratio controls trace sampling. Error metadata uses stable attributes for the outcome and the error code. CamusDB adds no SQL text and no row values to a span.

The snapshot script​

The CamusDB source tree includes a helper script that gives you local diagnostics from one command:

scripts/bottleneck-snapshot.sh /tmp/camus-bottleneck \
--workers 64 \
--duration 5m \
--rows 100000 \
--mode closed

The script starts a standalone server in the Release configuration with diagnostics enabled, seeds a deterministic data set for the workload, runs CamusDB.Workload, scrapes /metrics, and then writes a bundle:

server-command.txt
config-used.yml
server.log
server-metrics.txt
workload/manifest.json
workload/summary.json
workload/summary.md
workload/intervals.csv
workload/errors.json
workload/reconciliation.json
workload/bottleneck-report.md

The generated bottleneck-report.md lines up client-side throughput and latency with the server stage and dependency metrics. Use it to pick the next thing to investigate before you change any configuration.

The overhead check​

The source tree also includes a second script:

scripts/diagnostics-overhead.sh /tmp/camus-diagnostics-overhead \
--runs 5 \
--duration 60s

It alternates runs with diagnostics disabled and enabled, then reports the median throughput difference. Measure the overhead on the same machine, data shape, and server build that you plan to compare.