feat: P3-7 ctrl_metrics — 7 atomic counters/gauges + descriptor table + persist wiring

Add operational metrics infrastructure for the XCE controller persistence
and HA subsystems (design doc P3-7, review A8-007).

- src/include/ctrl_metrics.h: declares 7 _Atomic uint64_t metrics with
  ctrl_ prefix, CTRL_METRIC_INC / CTRL_METRIC_SET macros, ctrl_metric_desc_t
  descriptor struct, ctrl_metric_descriptors[] table, and ctrl_metrics_init()
- src/controller/ctrl_metrics.c: defines the 7 counters/gauges, the
  descriptor table (7 entries + NULL sentinel), and ctrl_metrics_init()
- src/controller/persist.c: #include ctrl_metrics.h; call ctrl_metrics_init()
  at persist_start() entry; CTRL_METRIC_INC(ctrl_repl_degraded_count) when
  repl_wait_ack returns < 0 (degraded / local-only durability mode)
- src/controller/meson.build: add ctrl_metrics.c to xce_controller and
  controller_unit_test source lists; add test_ctrl_metrics.c to test sources
- src/controller/testsuite/test_ctrl_metrics.c: 5 TDD-first Unity tests
  covering init-zeros, increment, set/overwrite gauge, descriptor sentinel
  count, and transient-stuck counter accumulation — all PASS
This commit is contained in:
Rongsong Shen 2026-05-08 00:04:06 +00:00
parent b490c419ca
commit 2e8ed8cb4e
6 changed files with 213 additions and 0 deletions

View file

@ -0,0 +1,47 @@
#pragma once
/*
* ctrl_metrics.h — P3-7: Operational metrics for the XCE controller.
*
* All metrics use C11 _Atomic uint64_t for lock-free, thread-safe access.
* Metric names use the ctrl_ prefix.
*
* Design doc: Part P3-7, review A8-007.
*/
#include <stdatomic.h>
#include <stdint.h>
/* ── Counter/gauge globals ──────────────────────────────────────────── */
extern _Atomic uint64_t ctrl_repl_degraded_count; /* counter */
extern _Atomic uint64_t ctrl_split_brain_detected; /* counter */
extern _Atomic uint64_t ctrl_async_events_lost; /* counter */
extern _Atomic uint64_t ctrl_transient_jobs_stuck; /* counter */
extern _Atomic uint64_t ctrl_wal_bytes; /* gauge */
extern _Atomic uint64_t ctrl_checkpoint_duration_ms; /* gauge */
extern _Atomic uint64_t ctrl_repl_lag_lsn; /* gauge */
/* ── Macros ─────────────────────────────────────────────────────────── */
/* Increment a monotonic counter by 1 (relaxed ordering — counters are
* sampled, not used for synchronization). */
#define CTRL_METRIC_INC(name) \
atomic_fetch_add_explicit(&(name), 1, memory_order_relaxed)
/* Overwrite a gauge with a new absolute value. */
#define CTRL_METRIC_SET(name, val) \
atomic_store_explicit(&(name), (uint64_t)(val), memory_order_relaxed)
/* ── Descriptor table for metric discovery ──────────────────────────── */
typedef struct {
const char* name;
_Atomic uint64_t* value;
const char* description;
int is_counter; /* 1 = counter (monotonic), 0 = gauge */
} ctrl_metric_desc_t;
/* Terminated by a { NULL, NULL, NULL, 0 } sentinel entry. */
extern const ctrl_metric_desc_t ctrl_metric_descriptors[];
/* ── Lifecycle ──────────────────────────────────────────────────────── */
/* Reset all metrics to zero. Call from persist_start() and ha_init(). */
void ctrl_metrics_init(void);