pub const L_PIPELINE: &str = "pipeline";
pub const L_COMPONENT: &str = "component";
pub const L_COMPONENT_TYPE: &str = "component_type";
pub const L_PARTITION: &str = "partition";
pub const L_SHARD: &str = "shard";
pub const L_REPLICA: &str = "replica";
pub const L_REASON: &str = "reason";
pub const L_OUTCOME: &str = "outcome";
pub const L_OP: &str = "op";
pub const L_EVENT: &str = "event";
pub const L_ERROR_TYPE: &str = "error_type";
pub const L_QUEUE: &str = "queue";
pub const L_STATE: &str = "state";
pub const L_VERSION: &str = "version";
macro_rules! metric_names {
($(#[$list_doc:meta])* $list:ident { $( $(#[$doc:meta])* $id:ident = $name:literal, )* }) => {
$( $(#[$doc])* pub const $id: &str = $name; )*
$(#[$list_doc])* pub const $list: &[&str] = &[$($id),*];
};
}
metric_names! {
COUNTERS {
SOURCE_RECORDS_TOTAL = "spate_source_records_total",
SOURCE_BYTES_TOTAL = "spate_source_bytes_total",
SOURCE_REBALANCES_TOTAL = "spate_source_rebalances_total",
DESER_RECORDS_TOTAL = "spate_deser_records_total",
DESER_RECORDS_DROPPED_TOTAL = "spate_deser_records_dropped_total",
DESER_NOT_READY_TOTAL = "spate_deser_not_ready_total",
OPERATOR_RECORDS_IN_TOTAL = "spate_operator_records_in_total",
OPERATOR_RECORDS_OUT_TOTAL = "spate_operator_records_out_total",
OPERATOR_RECORDS_DROPPED_TOTAL = "spate_operator_records_dropped_total",
OPERATOR_ERRORS_TOTAL = "spate_operator_errors_total",
QUEUE_FULL_EVENTS_TOTAL = "spate_queue_full_events_total",
BACKPRESSURE_PAUSE_EVENTS_TOTAL = "spate_backpressure_pause_events_total",
SINK_RECORDS_TOTAL = "spate_sink_records_total",
SINK_BYTES_TOTAL = "spate_sink_bytes_total",
SINK_FLUSHES_TOTAL = "spate_sink_flushes_total",
SINK_RETRIES_TOTAL = "spate_sink_retries_total",
SINK_ERRORS_TOTAL = "spate_sink_errors_total",
SINK_BREAKER_OPENS_TOTAL = "spate_sink_breaker_opens_total",
SINK_REPLICA_ERRORS_TOTAL = "spate_sink_replica_errors_total",
SINK_ABANDONED_BATCHES_TOTAL = "spate_sink_abandoned_batches_total",
SINK_DRAIN_OVERRUN_TOTAL = "spate_sink_drain_overrun_total",
CHECKPOINT_COMMITS_TOTAL = "spate_checkpoint_commits_total",
COORDINATION_ACQUISITIONS_TOTAL = "spate_coordination_acquisitions_total",
COORDINATION_SPLIT_LOSSES_TOTAL = "spate_coordination_split_losses_total",
COORDINATION_RELEASES_TOTAL = "spate_coordination_releases_total",
COORDINATION_REVOCATIONS_TOTAL = "spate_coordination_revocations_total",
COORDINATION_SPLITS_PLANNED_TOTAL = "spate_coordination_splits_planned_total",
COORDINATION_REPLANS_TOTAL = "spate_coordination_replans_total",
COORDINATION_SPLIT_FAILURES_TOTAL = "spate_coordination_split_failures_total",
COORDINATION_QUARANTINES_TOTAL = "spate_coordination_quarantines_total",
COORDINATION_WRITES_TOTAL = "spate_coordination_writes_total",
}
}
metric_names! {
GAUGES {
SOURCE_LAG_RECORDS = "spate_source_lag_records",
SOURCE_LANES_ACTIVE = "spate_source_lanes_active",
QUEUE_DEPTH = "spate_queue_depth",
QUEUE_CAPACITY = "spate_queue_capacity",
BACKPRESSURE_PAUSED = "spate_backpressure_paused",
BACKPRESSURE_PAUSED_SECONDS_TOTAL = "spate_backpressure_paused_seconds_total",
BACKPRESSURE_INFLIGHT_BYTES = "spate_backpressure_inflight_bytes",
SINK_INFLIGHT_BATCHES = "spate_sink_inflight_batches",
SINK_RETRY_BACKOFF_SECONDS = "spate_sink_retry_backoff_seconds",
SINK_REPLICA_HEALTHY = "spate_sink_replica_healthy",
SINK_SHARD_HEALTHY = "spate_sink_shard_healthy",
CHECKPOINT_PENDING_BATCHES = "spate_checkpoint_pending_batches",
CHECKPOINT_WATERMARK_AGE_SECONDS = "spate_checkpoint_watermark_age_seconds",
COORDINATION_SPLITS_OWNED = "spate_coordination_splits_owned",
COORDINATION_SPLITS_COMPLETED = "spate_coordination_splits_completed",
COORDINATION_SPLITS_QUARANTINED = "spate_coordination_splits_quarantined",
COORDINATION_LIVE_WORKERS = "spate_coordination_live_workers",
COORDINATION_LEADER = "spate_coordination_leader",
COORDINATION_IDLE = "spate_coordination_idle",
COORDINATION_SPLITS_DRAINING = "spate_coordination_splits_draining",
PIPELINE_INFO = "spate_pipeline_info",
PIPELINE_STATE = "spate_pipeline_state",
PIPELINE_THREADS = "spate_pipeline_threads",
}
}
metric_names! {
HISTOGRAMS {
SOURCE_POLL_DURATION_SECONDS = "spate_source_poll_duration_seconds",
DESER_BATCH_DURATION_SECONDS = "spate_deser_batch_duration_seconds",
OPERATOR_BATCH_DURATION_SECONDS = "spate_operator_batch_duration_seconds",
SINK_BATCH_ROWS = "spate_sink_batch_rows",
SINK_BATCH_BYTES = "spate_sink_batch_bytes",
SINK_FLUSH_DURATION_SECONDS = "spate_sink_flush_duration_seconds",
SINK_WRITE_DURATION_SECONDS = "spate_sink_write_duration_seconds",
SINK_PERMIT_WAIT_DURATION_SECONDS = "spate_sink_permit_wait_duration_seconds",
CHECKPOINT_COMMIT_DURATION_SECONDS = "spate_checkpoint_commit_duration_seconds",
COORDINATION_WRITE_DURATION_SECONDS = "spate_coordination_write_duration_seconds",
COORDINATION_REPLAN_DURATION_SECONDS = "spate_coordination_replan_duration_seconds",
COORDINATION_RECONCILE_DURATION_SECONDS = "spate_coordination_reconcile_duration_seconds",
COORDINATION_STORE_OP_DURATION_SECONDS = "spate_coordination_store_op_duration_seconds",
COORDINATION_DRAIN_DURATION_SECONDS = "spate_coordination_drain_duration_seconds",
COORDINATION_ASSIGNMENT_LATENCY_SECONDS = "spate_coordination_assignment_latency_seconds",
COORDINATION_SPLIT_LEASE_HEADROOM_SECONDS = "spate_coordination_split_lease_headroom_seconds",
E2E_LATENCY_SECONDS = "spate_e2e_latency_seconds",
}
}
pub const PREFIX: &str = "spate_";
pub const RESERVED_ROOTS: &[&str] = &[
"source",
"deser",
"operator",
"queue",
"backpressure",
"sink",
"checkpoint",
"coordination",
"e2e",
"pipeline",
];
pub const CUSTOM_NAMESPACE: &str = "custom";
pub const ROLE_SEGMENTS: &[&str] = &["source", "sink"];
#[cfg(test)]
mod tests {
use super::*;
use std::collections::{BTreeSet, HashSet};
#[test]
fn names_follow_prometheus_conventions() {
for name in COUNTERS {
assert!(
name.ends_with("_total"),
"counter `{name}` must end in _total"
);
}
for name in HISTOGRAMS {
assert!(
name.ends_with("_seconds") || name.ends_with("_rows") || name.ends_with("_bytes"),
"histogram `{name}` must carry a unit suffix"
);
}
for name in GAUGES {
if *name == BACKPRESSURE_PAUSED_SECONDS_TOTAL {
continue;
}
assert!(
!name.ends_with("_total"),
"gauge `{name}` must not end in _total"
);
}
}
#[test]
fn names_are_prefixed_and_unique() {
let all: Vec<&str> = COUNTERS
.iter()
.chain(GAUGES)
.chain(HISTOGRAMS)
.copied()
.collect();
let unique: HashSet<&str> = all.iter().copied().collect();
assert_eq!(unique.len(), all.len(), "duplicate metric name");
for name in &all {
assert!(name.starts_with(PREFIX), "`{name}` must be spate_-prefixed");
}
}
#[test]
fn every_framework_name_is_under_a_reserved_root() {
for name in COUNTERS.iter().chain(GAUGES).chain(HISTOGRAMS) {
let rest = name
.strip_prefix(PREFIX)
.unwrap_or_else(|| panic!("`{name}` must be spate_-prefixed"));
let root = rest.split('_').next().unwrap_or("");
assert!(
RESERVED_ROOTS.contains(&root),
"`{name}` has root `{root}` not in RESERVED_ROOTS — add it so \
the Meter collision guard covers this family"
);
}
}
#[test]
fn every_name_literal_in_this_file_is_listed() {
let decls = include_str!("names.rs")
.split("#[cfg(test)]")
.next()
.expect("split yields a first part");
let mut literals = BTreeSet::new();
let mut rest = decls;
while let Some(i) = rest.find("\"spate_") {
let tail = &rest[i + 1..];
let end = tail.find('"').expect("closed literal");
if end > PREFIX.len() {
literals.insert(&tail[..end]);
}
rest = &tail[end + 1..];
}
let listed: BTreeSet<&str> = COUNTERS
.iter()
.chain(GAUGES)
.chain(HISTOGRAMS)
.copied()
.collect();
assert!(
!literals.is_empty(),
"no name literals found; the scan is vacuous"
);
assert_eq!(
literals, listed,
"a name declared in names.rs is missing from its list"
);
}
}