frankensearch-storage 0.1.0

FrankenSQLite-backed metadata and embedding job storage for frankensearch
Documentation
use std::sync::atomic::{AtomicU64, Ordering};

use serde::{Deserialize, Serialize};

#[derive(Debug, Default)]
pub struct StorageMetrics {
    opens: AtomicU64,
    schema_bootstraps: AtomicU64,
    tx_commits: AtomicU64,
    tx_rollbacks: AtomicU64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct StorageMetricsSnapshot {
    pub opens: u64,
    pub schema_bootstraps: u64,
    pub tx_commits: u64,
    pub tx_rollbacks: u64,
}

impl StorageMetrics {
    pub fn record_open(&self) {
        self.opens.fetch_add(1, Ordering::Relaxed);
    }

    pub fn record_schema_bootstrap(&self) {
        self.schema_bootstraps.fetch_add(1, Ordering::Relaxed);
    }

    pub fn record_commit(&self) {
        self.tx_commits.fetch_add(1, Ordering::Relaxed);
    }

    pub fn record_rollback(&self) {
        self.tx_rollbacks.fetch_add(1, Ordering::Relaxed);
    }

    #[must_use]
    pub fn snapshot(&self) -> StorageMetricsSnapshot {
        StorageMetricsSnapshot {
            opens: self.opens.load(Ordering::Relaxed),
            schema_bootstraps: self.schema_bootstraps.load(Ordering::Relaxed),
            tx_commits: self.tx_commits.load(Ordering::Relaxed),
            tx_rollbacks: self.tx_rollbacks.load(Ordering::Relaxed),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::StorageMetrics;

    #[test]
    fn default_metrics_are_zeroed() {
        let metrics = StorageMetrics::default();
        let snapshot = metrics.snapshot();
        assert_eq!(snapshot.opens, 0);
        assert_eq!(snapshot.schema_bootstraps, 0);
        assert_eq!(snapshot.tx_commits, 0);
        assert_eq!(snapshot.tx_rollbacks, 0);
    }

    #[test]
    fn counters_accumulate_independently() {
        let metrics = StorageMetrics::default();
        metrics.record_open();
        metrics.record_open();
        metrics.record_schema_bootstrap();
        metrics.record_commit();
        metrics.record_commit();
        metrics.record_commit();
        metrics.record_rollback();

        let snapshot = metrics.snapshot();
        assert_eq!(snapshot.opens, 2);
        assert_eq!(snapshot.schema_bootstraps, 1);
        assert_eq!(snapshot.tx_commits, 3);
        assert_eq!(snapshot.tx_rollbacks, 1);
    }

    #[test]
    fn snapshot_is_point_in_time() {
        let metrics = StorageMetrics::default();
        metrics.record_open();
        let snap1 = metrics.snapshot();

        metrics.record_open();
        let snap2 = metrics.snapshot();

        assert_eq!(snap1.opens, 1);
        assert_eq!(snap2.opens, 2);
    }

    #[test]
    fn snapshot_serde_roundtrip() {
        let metrics = StorageMetrics::default();
        metrics.record_open();
        metrics.record_commit();
        let snapshot = metrics.snapshot();

        let json = serde_json::to_string(&snapshot).expect("serialize");
        let deserialized: super::StorageMetricsSnapshot =
            serde_json::from_str(&json).expect("deserialize");
        assert_eq!(snapshot, deserialized);
    }
}