icydb-cli 0.296.4

Developer CLI tools for IcyDB
//! Module: metrics report rendering.
//! Responsibility: render canonical journal debt and one execution-cost window.
//! Does not own: endpoint calls, Wire decoding, or endpoint publication.

use icydb::metrics::{EntityMetrics, InstructionMetrics, MetricsReport};

use crate::table::{ColumnAlign, append_indented_table};

type MetricsEntityRow = [String; 5];

const HEADERS: [&str; 5] = ["entity", "hits", "instructions", "average", "max"];
const ALIGNMENTS: [ColumnAlign; 5] = [
    ColumnAlign::Left,
    ColumnAlign::Right,
    ColumnAlign::Right,
    ColumnAlign::Right,
    ColumnAlign::Right,
];

pub(super) fn render_metrics_report(report: &MetricsReport) -> String {
    let debt = report.journal_debt();
    let convergence = report.convergence();
    let appended = convergence.appended();
    let retired = convergence.retired();
    let mut output = format!(
        "IcyDB metrics\n  window: {}..{} ({} ms)\n  heap-local window ID: {}\n  entities: {} of {} (bounded path prefix, sorted by cost)\n\njournal\n  canonical debt: {} batches, {} records, {} encoded batch bytes\n  window appended: {} batches, {} records, {} encoded batch bytes\n  window retired: {} batches, {} records, {} encoded batch bytes\n  movement overflowed: {}\n  fold: {} samples, {} total instructions, {} max instructions (sample overflow={}, total overflow={})\n  startup recovery: {} samples, {} total instructions, {} max instructions (sample overflow={}, total overflow={})\n  instruction spans may nest; totals are not additive\n\nentities\n",
        report.window_start_ms(),
        report.window_end_ms(),
        report
            .window_end_ms()
            .saturating_sub(report.window_start_ms()),
        report
            .window_id()
            .map_or_else(|| "unavailable".to_string(), |id| id.to_string()),
        report.entities().len(),
        report.total_entities(),
        debt.batch_count(),
        debt.record_count(),
        debt.encoded_batch_bytes(),
        appended.batch_count(),
        appended.record_count(),
        appended.encoded_batch_bytes(),
        retired.batch_count(),
        retired.record_count(),
        retired.encoded_batch_bytes(),
        convergence.overflowed(),
        convergence.journal_fold().samples(),
        convergence.journal_fold().total(),
        convergence.journal_fold().maximum().unwrap_or(0),
        convergence.journal_fold().samples_overflowed(),
        convergence.journal_fold().total_overflowed(),
        report.schema_lifecycle().startup_recovery().samples(),
        report.schema_lifecycle().startup_recovery().total(),
        report
            .schema_lifecycle()
            .startup_recovery()
            .maximum()
            .unwrap_or(0),
        report
            .schema_lifecycle()
            .startup_recovery()
            .samples_overflowed(),
        report
            .schema_lifecycle()
            .startup_recovery()
            .total_overflowed(),
    );
    if report.entities().is_empty() {
        output.push_str("  None\n");
        return output;
    }

    if report.entities().iter().any(|entity| {
        entity.instructions().samples_overflowed() || entity.instructions().total_overflowed()
    }) {
        output.push_str("  + marks a clipped count or instruction total\n");
    }
    let rows = report.entities().iter().map(entity_row).collect::<Vec<_>>();
    append_indented_table(&mut output, "  ", &HEADERS, &rows, &ALIGNMENTS);
    output
}

fn entity_row(entity: &EntityMetrics) -> MetricsEntityRow {
    // The producer preserves actual overflow quality; raw cap values cannot
    // distinguish clipped observations from exact caps and cannot rebuild it.
    let average = average(entity.instructions())
        .map_or_else(|| "unavailable".to_string(), |value| value.to_string());
    [
        entity.path().to_string(),
        if entity.instructions().samples_overflowed() {
            format!("{}+", entity.instructions().samples())
        } else {
            entity.instructions().samples().to_string()
        },
        if entity.instructions().total_overflowed() {
            format!("{}+", entity.instructions().total())
        } else {
            entity.instructions().total().to_string()
        },
        average,
        entity.instructions().maximum().unwrap_or(0).to_string(),
    ]
}

// Rendering owns floor arithmetic. Clipped source quality is not an exact mean.
const fn average(instructions: &InstructionMetrics) -> Option<u128> {
    if instructions.samples_overflowed() || instructions.total_overflowed() {
        return None;
    }
    instructions
        .total()
        .checked_div(instructions.samples() as u128)
}

#[cfg(test)]
mod tests;