use anyhow::Result;
use metrics::{counter, describe_counter, describe_histogram, histogram};
use std::{sync::Once, time::Instant};
use tracing::field;
pub(super) const MATERIALIZATIONS_TOTAL: &str = "netsuke_runner_dyndep_materializations_total";
pub(super) const MATERIALIZATION_DURATION: &str =
"netsuke_runner_dyndep_materialization_duration_seconds";
pub(super) const RETENTIONS_TOTAL: &str = "netsuke_runner_dyndep_retentions_total";
pub(super) const RETENTION_DURATION: &str = "netsuke_runner_dyndep_retention_duration_seconds";
pub(super) const RETAINED_FILES_RECLAIMED: &str =
"netsuke_runner_dyndep_retained_files_reclaimed_total";
pub(super) const RETAINED_BYTES_RECLAIMED: &str =
"netsuke_runner_dyndep_retained_bytes_reclaimed_total";
pub(super) const TEMP_FILE_RETRIES_TOTAL: &str = "netsuke_runner_dyndep_temp_file_retries_total";
pub(super) fn instrument_materialization<T>(
sidecar_count: usize,
materialize: impl FnOnce() -> Result<T>,
) -> Result<T> {
describe_metrics();
let span = tracing::trace_span!(
"runner.dyndep.materialize",
sidecar_count,
outcome = field::Empty,
error_category = field::Empty,
);
let _guard = span.enter();
let started = Instant::now();
let result = materialize();
let outcome = record_outcome(&span, &result, "dyndep materialization failed");
counter!(MATERIALIZATIONS_TOTAL, "outcome" => outcome).increment(1);
histogram!(MATERIALIZATION_DURATION).record(started.elapsed());
result
}
pub(super) fn instrument_sidecar_materialization<T>(
materialize: impl FnOnce() -> Result<T>,
) -> Result<T> {
let span = tracing::trace_span!(
"runner.dyndep.materialize_sidecar",
outcome = field::Empty,
error_category = field::Empty,
);
let _guard = span.enter();
let result = materialize();
record_outcome(&span, &result, "dyndep sidecar materialization failed");
result
}
pub(super) fn instrument_retention<T>(
retain: impl FnOnce() -> Result<T>,
reclaimed: impl FnOnce(&T) -> (u64, u64),
) -> Result<T> {
describe_metrics();
let span = tracing::trace_span!(
"runner.dyndep.retain",
outcome = field::Empty,
error_category = field::Empty,
);
let _guard = span.enter();
let started = Instant::now();
let result = retain();
let outcome = record_outcome(&span, &result, "dyndep retention failed");
counter!(RETENTIONS_TOTAL, "outcome" => outcome).increment(1);
histogram!(RETENTION_DURATION).record(started.elapsed());
if let Ok(summary) = &result {
let (files, bytes) = reclaimed(summary);
counter!(RETAINED_FILES_RECLAIMED).increment(files);
counter!(RETAINED_BYTES_RECLAIMED).increment(bytes);
}
result
}
pub(super) fn record_temp_file_retry(outcome: &'static str) {
describe_metrics();
counter!(TEMP_FILE_RETRIES_TOTAL, "outcome" => outcome).increment(1);
}
fn record_outcome<T>(
span: &tracing::Span,
result: &Result<T>,
failure_message: &'static str,
) -> &'static str {
if result.is_err() {
span.record("outcome", "error");
span.record("error_category", "dyndep_io");
tracing::debug!(error_category = "dyndep_io", "{failure_message}");
"error"
} else {
span.record("outcome", "success");
"success"
}
}
fn describe_metrics() {
static DESCRIBE: Once = Once::new();
DESCRIBE.call_once(|| {
describe_counter!(
MATERIALIZATIONS_TOTAL,
"Counts dyndep materialization outcomes by bounded outcome."
);
describe_histogram!(
MATERIALIZATION_DURATION,
"Measures dyndep materialization duration in seconds."
);
describe_counter!(
RETENTIONS_TOTAL,
"Counts bounded dyndep retention outcomes by fixed outcome."
);
describe_histogram!(
RETENTION_DURATION,
"Measures bounded dyndep retention duration in seconds."
);
describe_counter!(
RETAINED_FILES_RECLAIMED,
"Counts dyndep sidecar files reclaimed by retention."
);
describe_counter!(
RETAINED_BYTES_RECLAIMED,
"Counts dyndep sidecar bytes reclaimed by retention."
);
describe_counter!(
TEMP_FILE_RETRIES_TOTAL,
"Counts dyndep temporary-file collisions by fixed retry outcome."
);
});
}