Metrics

Every Prometheus metric family the control plane exports, with its type, labels and meaning.

The control plane serves Prometheus metrics at /metrics on its metrics listener (server.metrics_addr, 0.0.0.0:9090 by default; see Configuration). Every role serves it, so a scheduler-only pod in a split install is scraped like the API pod.

Each dexaflow_* family below is also published under its name from before the rename, leoflow_*, with the same values, so dashboards and alerts written against either name keep working. Set observability.metrics.drop_legacy_names: true to publish each family once and halve the scrape.

Labels carry what the meaning names. dag_id and task_id grow with the DAGs you deploy; path on the HTTP families is the route template, not the raw URL, so it stays bounded.

For what to alert on and how to read the scheduler and dispatch families during an incident, see Troubleshooting & observability and Scheduler resilience.

The table is generated from the collectors the server registers: run make gen-metrics-reference after adding or changing a metric, and go test ./internal/tools/metricsref/ fails while the page is stale.

MetricTypeLabelsMeaning
dexaflow_agent_legacy_attempt_token_totalcounter(none)Agent RPCs authenticated by a task token without the attempt_epoch claim: one minted before the upgrade, or one an old replica renewed or exchanged during a rolling upgrade. Reports from such a token are read as epoch 0. It falls to zero once the rollout has finished and every pre-upgrade attempt has finished; a later release rejects such tokens.
dexaflow_agent_token_rejections_totalcounterreasonAgent RPCs refused for their bearer token, by reason: expired (the credential lapsed: the agent could not renew it for a whole TTL, or its pod started after it ran out), wrong_audience (a token for another surface), bad_signature (another signing key), wrong_issuer, not_yet_valid, malformed, invalid, or missing (no bearer at all).
dexaflow_alerts_dispatched_totalcounterdag_id, type, resultNative on-failure alerts dispatched, by channel type and outcome (sent, failed). Operators alert on rate of result=“failed” to catch a broken alert path (#424).
dexaflow_auth_failures_totalcounterreasonAuthentication failures by reason.
dexaflow_ci_oidc_exchange_totalcounterissuer, outcomeCI OIDC token exchanges (auth.ci_oidc, ADR 0092) by issuer entry (unknown when none matched) and outcome (issued, invalid_token, unknown_issuer, no_rule, role_missing, keys_unavailable, error).
dexaflow_db_pool_acquired_connsgaugepoolConnections currently lent out by the database pool, by pool (main, health, scheduler).
dexaflow_db_pool_canceled_acquires_totalcounterpoolAcquires that gave up waiting for a connection (their context ended), by pool (main, health, scheduler).
dexaflow_db_pool_empty_acquire_wait_seconds_totalcounterpoolTime acquires spent waiting for a connection, in seconds, by pool (main, health, scheduler).
dexaflow_db_pool_empty_acquires_totalcounterpoolAcquires that found no idle connection and had to wait for one, by pool (main, health, scheduler).
dexaflow_db_pool_idle_connsgaugepoolIdle connections in the database pool, by pool (main, health, scheduler).
dexaflow_db_pool_max_connsgaugepoolMaximum connections the database pool may open, by pool (main, health, scheduler).
dexaflow_db_pool_total_connsgaugepoolOpen connections in the database pool, idle, lent out and being opened, by pool (main, health, scheduler).
dexaflow_dispatch_at_capacity_totalcounter(none)Dispatch requests rejected because the buffer was full.
dexaflow_dispatch_inner_errors_totalcounter(none)Errors returned by the inner dispatcher inside a worker.
dexaflow_dispatch_latency_secondshistogram(none)End-to-end latency of one buffered dispatch (enqueue to worker completion).
dexaflow_dispatch_queue_depthgauge(none)Number of dispatch requests currently buffered.
dexaflow_http_request_duration_secondshistogrammethod, pathHTTP request duration.
dexaflow_http_requests_totalcountermethod, path, statusHTTP requests.
dexaflow_kubernetes_api_calls_totalcounteroperation, resultKubernetes API calls.
dexaflow_log_tail_replay_bytesgauge(none)Bytes of task-log lines the on_demand live-tail gates hold for a follower who may subscribe, across all log streams. Bounded by logs.tail.replay_budget_bytes when set.
dexaflow_log_tail_replay_evictions_totalcounter(none)Live-tail replay rings dropped to keep the total under logs.tail.replay_budget_bytes, the longest silent stream first. A follower of a dropped stream reads the dropped lines from the stored log.
dexaflow_pod_pending_duration_secondshistogram(none)Pod pending duration.
dexaflow_pods_created_totalcounterdag_id, resultPods created by dag and result.
dexaflow_reconcile_infra_override_totalcountermarkDurable SUCCESS records the reconciler settled over a reaper’s provisional infra mark (agent_lost, pod_lost, dispatch_lost), by the mark overridden: a task that finished while the control plane lost track of it, recovered instead of re-run.
dexaflow_redis_command_failures_totalcounterreasonRedis command failures classified by reason (timeout, connection_refused, auth, canceled, other). Alert on rate(…[5m]) to surface a degrading client-side path before user-visible XCom errors (#312 sibling).
dexaflow_redis_dial_duration_secondshistogram(none)Wall-clock time per Redis dial: TCP connect plus, with rediss://, the TLS handshake.
dexaflow_redis_dial_failures_totalcounterreasonFailures at TCP/TLS dial time to Redis, classified by reason (tls_handshake, connection_refused, dns, timeout, other).
dexaflow_redis_pool_active_connsgauge(none)Redis pool: connections currently checked out for a command.
dexaflow_redis_pool_idle_connsgauge(none)Redis pool: idle connections available for the next command.
dexaflow_redis_pool_timeouts_totalcounter(none)Redis pool checkout timeouts: a caller waited PoolTimeout for an idle connection. A non-zero rate means the pool is too small or commands are too slow.
dexaflow_redis_pool_total_connsgauge(none)Redis pool: total connections (active + idle). Saturating against PoolSize is the leading indicator of throughput issues.
dexaflow_resilience_backstop_disabledgaugeconfig_key1 when this process booted with the resilience backstop of config_key disabled, 0 when it is in place. Set once at boot by processes that run the scheduler; alert on max(…) > 0.
dexaflow_retention_cycle_duration_secondshistogram(none)Wall-clock duration of one retention janitor cycle, pauses included.
dexaflow_retention_rows_deleted_totalcountertableMetadata rows the retention janitor deleted, by table.
dexaflow_retention_rows_eligiblegaugetableRows the last retention dry run found eligible for deletion, by table, summed across all tenants.
dexaflow_scheduler_decisions_totalcounterdecision_typeScheduler decisions by type.
dexaflow_scheduler_loop_duration_secondshistogram(none)Duration of one scheduler loop iteration.
dexaflow_scheduler_reacquire_secondshistogram(none)Wall-clock seconds between a leader step-down and the same replica reacquiring leadership. A growing P99 signals leader churn that affects scheduling latency (#311).
dexaflow_scheduler_schedule_delay_secondshistogram(none)Time from a scheduled run’s slot to its creation, for the latest slot a tick created for a DAG.
dexaflow_scheduler_step_downs_totalcounterreasonScheduler leadership step-downs by reason (lock_released, check_timeout, shutdown). Operators alert on rate(…[5m]); a sudden uptick usually indicates a Postgres connection-stability issue (#311).
dexaflow_scheduler_woken_ticks_totalcounter(none)Scheduler ticks started early by a task completion instead of the loop interval (scheduler.eager_promotion).
dexaflow_schema_readygaugereason1 when the database schema is one this binary can serve, 0 while it waits for it, with the reason (schema absent, schema behind, schema dirty, schema unreadable). Alert on 0 for 5 minutes.
dexaflow_schema_wait_secondsgauge(none)Seconds since this process found its database schema not ready, 0 when it is ready.
dexaflow_task_attempts_totalcountertenant, stateFinished task attempts by tenant and the state each first ended in, from the usage events; with observability.usage_metrics on. Counted by the scheduler leader from the moment it took the lead.
dexaflow_task_cold_start_secondshistogramdag_idTask cold start time.
dexaflow_task_duration_secondshistogramdag_id, task_id, task_typeTask execution duration.
dexaflow_task_pod_creation_duration_secondshistogram(none)Time to create a task pod.
dexaflow_task_retries_totalcounterdag_id, task_idTask retries.
dexaflow_task_slot_seconds_totalcountertenantPool slots times run time of finished task attempts by tenant, from the usage events; with observability.usage_metrics on. Counted by the scheduler leader from the moment it took the lead.
dexaflow_task_state_transitions_totalcounterfrom_state, to_state, dag_idTask state transitions.
dexaflow_tasks_undispatchable_totalcounterreasonTasks queued with no executor to launch them, by reason.
dexaflow_tls_reload_failures_totalcounterlistenerRotated TLS certificate or key files that failed to load, by listener (api, agent_grpc). The listener keeps serving the last good certificate; alert on any increase (#637).
dexaflow_unit_misfit_totalcounterstageTasks accepted under executor.unit.enforce=warn although their resources exceed pool_slots x unit, by stage (register, set_current, dispatch).
dexaflow_unpinned_image_totalcounterstage, outcomeImages not pinned by digest that deploy.require_digest warned about (outcome=warned) or refused (outcome=refused), by stage (register, set_current). Dry runs are not counted.
dexaflow_usage_events_lag_secondsgauge(none)Age of the oldest usage event the service API cannot serve yet because an older transaction is still open; 0 while usage events are not recorded. Set by the scheduler leader.
dexaflow_xcom_pull_totalcounterdag_idXCom pulls.
dexaflow_xcom_push_totalcounterdag_idXCom pushes.
dexaflow_xcom_rejected_totalcounterreasonRejected XCom writes by reason.
dexaflow_xcom_size_byteshistogramdag_idXCom payload size in bytes.