1use prometheus::{
4 Gauge, Histogram, HistogramOpts, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, Opts,
5 Registry,
6};
7
8pub struct EngineMetrics {
13 pub events_ingested: IntCounter,
15 pub events_emitted: IntCounter,
17 pub events_dropped: IntCounter,
19 pub cycles: IntCounter,
21 pub batches: IntCounter,
23 pub queries_compiled: IntCounter,
25 pub queries_cached_plan: IntCounter,
27 pub cycles_backpressured: IntCounter,
29 pub mv_updates: IntCounter,
31 pub mv_bytes_stored: IntGauge,
33 pub pipeline_watermark: IntGauge,
35 pub source_watermark_ms: IntGaugeVec,
37 pub source_idle: IntGaugeVec,
40 pub stream_watermark_ms: IntGaugeVec,
42 pub input_buf_bytes: IntGaugeVec,
44 pub shed_records_total: IntCounterVec,
46 pub checkpoints_completed: IntCounter,
48 pub checkpoints_failed: IntCounter,
50 pub checkpoint_epoch: IntGauge,
52 pub checkpoint_size_bytes: IntGauge,
54 pub sink_write_failures: IntCounter,
56 pub sink_write_timeouts: IntCounter,
58 pub sink_task_channel_closed: IntCounter,
60 pub sink_filter_rejected_rows: IntCounterVec,
63 pub window_late_dropped: IntCounter,
66 pub events_null_timestamp: IntCounter,
68 pub temporal_filter_buffered: IntGauge,
70 pub temporal_filter_inserts: IntCounter,
72 pub temporal_filter_retracts: IntCounter,
74 pub temporal_filter_dropped: IntCounter,
76 pub cycle_duration: Histogram,
78 pub checkpoint_duration: Histogram,
80 pub checkpoint_pipeline_stall_duration: Histogram,
84 pub checkpoint_barrier_local_duration: Histogram,
87 pub checkpoint_aligned_resume_wait: Histogram,
90 pub checkpoint_restorable_gate_wait: Histogram,
96 pub checkpoint_unchanged_vnodes: IntCounter,
99 pub coordinated_committer_lag_epochs: IntGauge,
102 pub sink_precommit_duration: Histogram,
104 pub lookup_cache_hits: IntCounterVec,
106 pub lookup_cache_misses: IntCounterVec,
108 pub lookup_source_errors: IntCounterVec,
110 pub lookup_in_flight_rows: IntGaugeVec,
112 pub placement_vnodes_per_domain: IntGaugeVec,
114 pub placement_blast_radius_ratio: Gauge,
116 pub pipeline_faults_total: IntCounter,
118 pub pipeline_cycle_errors_total: IntCounter,
120 pub pipeline_restarts_total: IntCounter,
122 pub coordinated_recoveries_total: IntCounter,
124 pub coordinated_recovery_failures_total: IntCounter,
126 pub shuffle_delivery_loss_incidents_total: IntCounter,
128}
129
130impl EngineMetrics {
131 #[must_use]
137 #[allow(clippy::too_many_lines)]
138 pub fn new(registry: &Registry) -> Self {
139 macro_rules! reg {
140 ($m:expr) => {{
141 let m = $m;
142 registry.register(Box::new(m.clone())).unwrap();
143 m
144 }};
145 }
146
147 Self {
148 events_ingested: reg!(IntCounter::new(
149 "events_ingested_total",
150 "Events ingested from sources"
151 )
152 .unwrap()),
153 events_emitted: reg!(IntCounter::new(
154 "events_emitted_total",
155 "Events emitted to streams"
156 )
157 .unwrap()),
158 events_dropped: reg!(IntCounter::new("events_dropped_total", "Events dropped").unwrap()),
159 cycles: reg!(IntCounter::new("cycles_total", "Processing cycles completed").unwrap()),
160 batches: reg!(IntCounter::new("batches_total", "Batches processed").unwrap()),
161 queries_compiled: reg!(IntCounter::new(
162 "queries_compiled_total",
163 "Queries using compiled PhysicalExpr"
164 )
165 .unwrap()),
166 queries_cached_plan: reg!(IntCounter::new(
167 "queries_cached_plan_total",
168 "Queries using cached logical plan"
169 )
170 .unwrap()),
171 cycles_backpressured: reg!(IntCounter::new(
172 "cycles_backpressured_total",
173 "Cycles skipped by backpressure"
174 )
175 .unwrap()),
176 mv_updates: reg!(
177 IntCounter::new("mv_updates_total", "Materialized view updates").unwrap()
178 ),
179 mv_bytes_stored: reg!(
180 IntGauge::new("mv_bytes_stored", "Approximate MV bytes stored").unwrap()
181 ),
182 pipeline_watermark: reg!(IntGauge::new(
183 "pipeline_watermark",
184 "Global pipeline watermark"
185 )
186 .unwrap()),
187 source_watermark_ms: reg!(IntGaugeVec::new(
189 Opts::new("source_watermark_ms", "Per-source watermark (epoch-ms)"),
190 &["source"],
191 )
192 .unwrap()),
193 source_idle: reg!(IntGaugeVec::new(
194 Opts::new(
195 "source_idle",
196 "1 if source idle (excluded from watermark min)"
197 ),
198 &["source"],
199 )
200 .unwrap()),
201 stream_watermark_ms: reg!(IntGaugeVec::new(
202 Opts::new("stream_watermark_ms", "Per-stream watermark (epoch-ms)"),
203 &["stream"],
204 )
205 .unwrap()),
206 input_buf_bytes: reg!(IntGaugeVec::new(
207 Opts::new("input_buf_bytes", "Per-stream input buffer bytes"),
208 &["stream"],
209 )
210 .unwrap()),
211 shed_records_total: reg!(IntCounterVec::new(
212 Opts::new("shed_records_total", "Rows shed by ShedOldest policy"),
213 &["stream"],
214 )
215 .unwrap()),
216 checkpoints_completed: reg!(IntCounter::new(
217 "checkpoints_completed_total",
218 "Completed checkpoints"
219 )
220 .unwrap()),
221 checkpoints_failed: reg!(IntCounter::new(
222 "checkpoints_failed_total",
223 "Failed checkpoints"
224 )
225 .unwrap()),
226 checkpoint_epoch: reg!(
227 IntGauge::new("checkpoint_epoch", "Current checkpoint epoch").unwrap()
228 ),
229 checkpoint_size_bytes: reg!(IntGauge::new(
230 "checkpoint_size_bytes",
231 "Last checkpoint size"
232 )
233 .unwrap()),
234 sink_write_failures: reg!(IntCounter::new(
235 "sink_write_failures_total",
236 "Sink write errors"
237 )
238 .unwrap()),
239 sink_write_timeouts: reg!(IntCounter::new(
240 "sink_write_timeouts_total",
241 "Sink write timeouts"
242 )
243 .unwrap()),
244 sink_task_channel_closed: reg!(IntCounter::new(
245 "sink_task_channel_closed_total",
246 "Sink task channel closed"
247 )
248 .unwrap()),
249 sink_filter_rejected_rows: reg!(IntCounterVec::new(
250 Opts::new(
251 "sink_filter_rejected_rows_total",
252 "Rows dropped because the sink filter failed to compile",
253 ),
254 &["sink"],
255 )
256 .unwrap()),
257 window_late_dropped: reg!(IntCounter::new(
258 "window_late_dropped_total",
259 "Rows dropped by window operators past allowed_lateness"
260 )
261 .unwrap()),
262 events_null_timestamp: reg!(IntCounter::new(
263 "events_null_timestamp_total",
264 "Source rows dropped because the event-time column was null"
265 )
266 .unwrap()),
267 temporal_filter_buffered: reg!(IntGauge::new(
268 "temporal_filter_buffered",
269 "Rows buffered by retracting temporal-filter operators"
270 )
271 .unwrap()),
272 temporal_filter_inserts: reg!(IntCounter::new(
273 "temporal_filter_inserts_total",
274 "Z-set inserts emitted by temporal-filter operators"
275 )
276 .unwrap()),
277 temporal_filter_retracts: reg!(IntCounter::new(
278 "temporal_filter_retracts_total",
279 "Z-set retractions emitted by temporal-filter operators"
280 )
281 .unwrap()),
282 temporal_filter_dropped: reg!(IntCounter::new(
283 "temporal_filter_dropped_total",
284 "Rows dropped un-emitted by temporal-filter operators"
285 )
286 .unwrap()),
287 cycle_duration: reg!(Histogram::with_opts(
288 HistogramOpts::new("cycle_duration_seconds", "Per-cycle processing duration")
289 .buckets(vec![
290 1e-7, 5e-7, 1e-6, 5e-6, 1e-5, 5e-5, 1e-4, 5e-4, 1e-3, 5e-3, 1e-2, 5e-2,
291 1e-1, 5e-1, 1.0,
292 ]),
293 )
294 .unwrap()),
295 checkpoint_duration: reg!(Histogram::with_opts(
298 HistogramOpts::new("checkpoint_duration_seconds", "Checkpoint cycle duration")
299 .buckets(prometheus::exponential_buckets(0.01, 2.0, 15).unwrap()),
300 )
301 .unwrap()),
302 checkpoint_pipeline_stall_duration: reg!(Histogram::with_opts(
305 HistogramOpts::new(
306 "checkpoint_pipeline_stall_duration_seconds",
307 "Pipeline stall per checkpoint barrier (align + capture + resume gate)",
308 )
309 .buckets(prometheus::exponential_buckets(0.001, 2.0, 16).unwrap()),
310 )
311 .unwrap()),
312 checkpoint_barrier_local_duration: reg!(Histogram::with_opts(
313 HistogramOpts::new(
314 "checkpoint_barrier_local_duration_seconds",
315 "Local paused barrier work (sink fence + shuffle align + state capture + tail handoff)",
316 )
317 .buckets(prometheus::exponential_buckets(0.001, 2.0, 16).unwrap()),
318 )
319 .unwrap()),
320 checkpoint_aligned_resume_wait: reg!(Histogram::with_opts(
321 HistogramOpts::new(
322 "checkpoint_aligned_resume_wait_seconds",
323 "Cluster-shuffle pause waiting for the global Aligned release",
324 )
325 .buckets(prometheus::exponential_buckets(0.001, 2.0, 16).unwrap()),
326 )
327 .unwrap()),
328 checkpoint_restorable_gate_wait: reg!(Histogram::with_opts(
330 HistogramOpts::new(
331 "checkpoint_restorable_gate_wait_seconds",
332 "Restorable-gate poll wait per epoch (vnode-partial presence)",
333 )
334 .buckets(prometheus::exponential_buckets(0.001, 2.0, 15).unwrap()),
335 )
336 .unwrap()),
337 coordinated_committer_lag_epochs: reg!(IntGauge::new(
338 "coordinated_committer_lag_epochs",
339 "Sealed epochs not yet externally committed by the designated committer",
340 )
341 .unwrap()),
342 checkpoint_unchanged_vnodes: reg!(IntCounter::new(
343 "checkpoint_unchanged_vnodes_total",
344 "Vnode partials written as unchanged-base references"
345 )
346 .unwrap()),
347 sink_precommit_duration: reg!(Histogram::with_opts(
349 HistogramOpts::new("sink_precommit_duration_seconds", "Sink pre-commit latency")
350 .buckets(prometheus::exponential_buckets(0.005, 2.0, 16).unwrap()),
351 )
352 .unwrap()),
353 lookup_cache_hits: reg!(IntCounterVec::new(
355 Opts::new("lookup_cache_hits_total", "On-demand lookup cache hits"),
356 &["table"],
357 )
358 .unwrap()),
359 lookup_cache_misses: reg!(IntCounterVec::new(
360 Opts::new("lookup_cache_misses_total", "On-demand lookup cache misses"),
361 &["table"],
362 )
363 .unwrap()),
364 lookup_source_errors: reg!(IntCounterVec::new(
365 Opts::new(
366 "lookup_source_errors_total",
367 "On-demand lookup source fetch errors"
368 ),
369 &["table"],
370 )
371 .unwrap()),
372 lookup_in_flight_rows: reg!(IntGaugeVec::new(
373 Opts::new(
374 "lookup_in_flight_rows",
375 "On-demand lookup rows awaiting a source fetch"
376 ),
377 &["table"],
378 )
379 .unwrap()),
380 placement_vnodes_per_domain: reg!(IntGaugeVec::new(
381 Opts::new(
382 "placement_vnodes_per_domain",
383 "Vnodes owned per failure domain (cluster mode)"
384 ),
385 &["domain"],
386 )
387 .unwrap()),
388 placement_blast_radius_ratio: reg!(Gauge::new(
389 "placement_blast_radius_ratio",
390 "Largest single domain's share of all vnodes (0-1); state that goes Restoring if it fails"
391 )
392 .unwrap()),
393 pipeline_faults_total: reg!(IntCounter::new(
394 "pipeline_faults_total",
395 "Compute-thread crashes that faulted the pipeline"
396 )
397 .unwrap()),
398 pipeline_cycle_errors_total: reg!(IntCounter::new(
399 "pipeline_cycle_errors_total",
400 "Fatal cycle errors dropped-and-continued under at-least-once delivery"
401 )
402 .unwrap()),
403 pipeline_restarts_total: reg!(IntCounter::new(
404 "pipeline_restarts_total",
405 "Automatic recover-from-checkpoint restarts after a pipeline fault"
406 )
407 .unwrap()),
408 coordinated_recoveries_total: reg!(IntCounter::new(
409 "coordinated_recoveries_total",
410 "Leader-coordinated global restart-to-epoch rounds applied by this node"
411 )
412 .unwrap()),
413 coordinated_recovery_failures_total: reg!(IntCounter::new(
414 "coordinated_recovery_failures_total",
415 "Leader-coordinated recovery rounds abandoned (self-restore failed or quorum timed out)"
416 )
417 .unwrap()),
418 shuffle_delivery_loss_incidents_total: reg!(IntCounter::new(
419 "shuffle_delivery_loss_incidents_total",
420 "Cross-node shuffle delivery-loss incidents (fences the epoch, forces replay)"
421 )
422 .unwrap()),
423 }
424 }
425}