otlp2records 0.5.0

Transform OTLP telemetry to flattened records
Documentation
use std::fs;
use std::path::Path;
use std::time::{Duration, Instant};

use otlp2records::{transform_logs, transform_metrics, transform_traces, InputFormat};

const WARMUP_ITERS: usize = 5;
const MEASURE_ITERS: usize = 50;

#[derive(Clone, Copy)]
enum WorkloadKind {
    Logs,
    Traces,
    Metrics,
}

struct Workload {
    name: &'static str,
    fixture: &'static str,
    kind: WorkloadKind,
}

struct Measurement {
    workload: &'static str,
    table: &'static str,
    rows_per_iter: usize,
    iterations: usize,
    total: Duration,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let workloads = [
        Workload {
            name: "logs_large",
            fixture: "testdata/logs_large.pb",
            kind: WorkloadKind::Logs,
        },
        Workload {
            name: "traces_large",
            fixture: "testdata/traces_large.pb",
            kind: WorkloadKind::Traces,
        },
        Workload {
            name: "metrics_gauge",
            fixture: "testdata/metrics_gauge.pb",
            kind: WorkloadKind::Metrics,
        },
        Workload {
            name: "metrics_sum",
            fixture: "testdata/metrics_sum.pb",
            kind: WorkloadKind::Metrics,
        },
        Workload {
            name: "metrics_histogram",
            fixture: "testdata/metrics_histogram.pb",
            kind: WorkloadKind::Metrics,
        },
        Workload {
            name: "metrics_exponential_histogram",
            fixture: "testdata/metrics_exponential_histogram.pb",
            kind: WorkloadKind::Metrics,
        },
        Workload {
            name: "metrics_mixed",
            fixture: "testdata/metrics_mixed.pb",
            kind: WorkloadKind::Metrics,
        },
    ];

    println!("workload,table,rows_per_iter,iterations,total_ms,ms_per_iter,rows_per_sec");
    for workload in workloads {
        if !Path::new(workload.fixture).exists() {
            eprintln!("skipping missing fixture {}", workload.fixture);
            continue;
        }

        let bytes = fs::read(workload.fixture)?;

        for _ in 0..WARMUP_ITERS {
            run_once(workload.kind, &bytes)?;
        }

        let mut last_rows = Vec::new();
        let start = Instant::now();
        for _ in 0..MEASURE_ITERS {
            last_rows = run_once(workload.kind, &bytes)?;
        }
        let total = start.elapsed();

        for (table, rows) in last_rows {
            let measurement = Measurement {
                workload: workload.name,
                table,
                rows_per_iter: rows,
                iterations: MEASURE_ITERS,
                total,
            };
            print_measurement(measurement);
        }
    }

    Ok(())
}

fn run_once(
    kind: WorkloadKind,
    bytes: &[u8],
) -> Result<Vec<(&'static str, usize)>, Box<dyn std::error::Error>> {
    match kind {
        WorkloadKind::Logs => {
            let batch = transform_logs(bytes, InputFormat::Protobuf)?;
            Ok(vec![("logs", batch.num_rows())])
        }
        WorkloadKind::Traces => {
            let batch = transform_traces(bytes, InputFormat::Protobuf)?;
            Ok(vec![("traces", batch.num_rows())])
        }
        WorkloadKind::Metrics => {
            let batches = transform_metrics(bytes, InputFormat::Protobuf)?;
            let mut out = Vec::new();
            if let Some(batch) = batches.gauge {
                out.push(("gauge", batch.num_rows()));
            }
            if let Some(batch) = batches.sum {
                out.push(("sum", batch.num_rows()));
            }
            if let Some(batch) = batches.histogram {
                out.push(("histogram", batch.num_rows()));
            }
            if let Some(batch) = batches.exp_histogram {
                out.push(("exp_histogram", batch.num_rows()));
            }
            Ok(out)
        }
    }
}

fn print_measurement(measurement: Measurement) {
    let total_ms = measurement.total.as_secs_f64() * 1000.0;
    let ms_per_iter = total_ms / measurement.iterations as f64;
    let total_rows = measurement.rows_per_iter * measurement.iterations;
    let rows_per_sec = total_rows as f64 / measurement.total.as_secs_f64();

    println!(
        "{},{},{},{},{:.3},{:.3},{:.0}",
        measurement.workload,
        measurement.table,
        measurement.rows_per_iter,
        measurement.iterations,
        total_ms,
        ms_per_iter,
        rows_per_sec
    );
}