use rudb_common::{Result, Value};
use rudb_functions::write_metric_fields;
use rudb_metrics::{LoadProfile, Stage, StageTotals, recent_loads};
use rudb_plan::{Plan, Slice};
use crate::metadata::{Metadata, text};
pub(crate) fn write_metrics(plan: &Plan, index: u32, columns: Slice) -> Result<Metadata> {
let mut rows = Vec::new();
for load in recent_loads() {
let mut total = StageTotals::default();
for stage in Stage::ALL {
let spent = load.stage(stage);
if spent.charged == 0 && spent.waits == 0 {
continue;
}
total.cpu_ns = total.cpu_ns.saturating_add(spent.cpu_ns);
total.waits = total.waits.saturating_add(spent.waits);
total.wait_ns = total.wait_ns.saturating_add(spent.wait_ns);
rows.push(row(&load, stage.name(), &spent, false));
}
let convert = load.stage(Stage::Convert);
let pages = load.stage(Stage::Pages);
total.wall_ns = load.elapsed_ns();
total.rows = convert.rows.max(pages.rows);
total.bytes_in = pages.bytes_in;
total.bytes_out = [Stage::Dictionary, Stage::Write, Stage::Publish]
.into_iter()
.map(|stage| load.stage(stage).bytes_out)
.fold(0_u64, u64::saturating_add);
rows.push(row(&load, "total", &total, true));
}
Metadata::new("rudb_write_metrics", &write_metric_fields(), &rows, plan, index, columns)
}
fn row(load: &LoadProfile, stage: &str, spent: &StageTotals, total: bool) -> Vec<Value> {
let (accounted, resident) = if total {
(
Value::BigInt(signed(load.accounted_peak())),
load.peak_rss().map_or(Value::Null, |peak| Value::BigInt(signed(peak))),
)
} else {
(Value::Null, Value::Null)
};
vec![
Value::BigInt(signed(load.id())),
text(load.target()),
text(stage),
Value::Double(millis(spent.wall_ns)),
Value::Double(millis(spent.cpu_ns)),
Value::BigInt(signed(spent.bytes_in)),
Value::BigInt(signed(spent.bytes_out)),
Value::BigInt(signed(spent.rows)),
Value::BigInt(signed(spent.waits)),
Value::Double(millis(spent.wait_ns)),
Value::Boolean(load.finished()),
accounted,
resident,
]
}
#[expect(clippy::cast_precision_loss, reason = "a millisecond reading does not need 53 bits")]
fn millis(nanos: u64) -> f64 {
nanos as f64 / 1e6
}
fn signed(value: u64) -> i64 {
i64::try_from(value).unwrap_or(i64::MAX)
}