nodedb_lite/engine/columnar/store/state.rs
1// SPDX-License-Identifier: Apache-2.0
2
3//! Columnar engine state and collection metadata.
4
5use crate::storage::engine::StorageEngine;
6#[cfg(not(target_arch = "wasm32"))]
7use crate::sync::outbound::columnar::ColumnarOutbound;
8#[cfg(not(target_arch = "wasm32"))]
9use crate::sync::outbound::timeseries::TimeseriesOutbound;
10use nodedb_columnar::mutation::MutationEngine;
11use nodedb_mem::ScopedMemory;
12use nodedb_types::columnar::ColumnarProfile;
13use std::collections::HashMap;
14use std::sync::{Arc, Mutex, RwLock};
15
16/// Per-collection segment metadata.
17#[derive(
18 Debug,
19 Clone,
20 serde::Serialize,
21 serde::Deserialize,
22 zerompk::ToMessagePack,
23 zerompk::FromMessagePack,
24)]
25pub(in crate::engine::columnar) struct SegmentMeta {
26 pub(in crate::engine::columnar) segment_id: u32,
27 pub(in crate::engine::columnar) row_count: u64,
28 /// Milliseconds since Unix epoch when this segment was first written.
29 /// Used by bitemporal purge to determine which superseded segments are
30 /// eligible for deletion.
31 #[serde(default)]
32 pub(super) system_time_from_ms: i64,
33 /// For bitemporal collections: the millisecond timestamp when the last
34 /// live row in this segment was deleted (compacted away). `None` means
35 /// the segment still has live rows. Segments with `Some(t)` where
36 /// `t < cutoff_ms` are eligible for physical deletion by `purge_bitemporal_before`.
37 #[serde(default)]
38 pub(in crate::engine::columnar) fully_deleted_at_ms: Option<i64>,
39}
40
41/// Per-collection state. Wrapped in `Mutex` inside `ColumnarEngine`.
42pub(in crate::engine::columnar) struct CollectionState {
43 pub(in crate::engine::columnar) mutation: MutationEngine,
44 pub(super) profile: ColumnarProfile,
45 /// Whether this collection has bitemporal system-time tracking.
46 pub(super) bitemporal: bool,
47 /// Ordered list of flushed segments (including fully-deleted tombstones for
48 /// bitemporal collections — they persist until `purge_bitemporal_before` clears them).
49 pub(in crate::engine::columnar) segments: Vec<SegmentMeta>,
50 /// Next segment ID to assign.
51 pub(super) next_segment_id: u32,
52}
53
54pub(super) type CollectionMap = HashMap<String, Arc<Mutex<CollectionState>>>;
55
56/// Manages all columnar collections for a NodeDbLite instance.
57pub struct ColumnarEngine<S: StorageEngine> {
58 pub(in crate::engine::columnar) storage: Arc<S>,
59 pub(super) collections: RwLock<CollectionMap>,
60 /// Optional outbound queue for plain columnar insert sync.
61 /// `None` when sync is disabled or not yet configured.
62 #[cfg(not(target_arch = "wasm32"))]
63 pub(super) outbound: Option<Arc<ColumnarOutbound<S>>>,
64 /// Optional outbound queue for timeseries-profile insert sync.
65 ///
66 /// Timeseries collections must use `TimeseriesPush` frames on Origin
67 /// (the columnar `MutationEngine` and the timeseries engine are separate
68 /// storage paths on Origin). When this queue is present, inserts into
69 /// collections with `ColumnarProfile::Timeseries` are enqueued here
70 /// instead of `outbound`.
71 #[cfg(not(target_arch = "wasm32"))]
72 pub(super) timeseries_outbound: Option<Arc<TimeseriesOutbound<S>>>,
73 /// Governor handle bound to the columnar engine budget. Cloned into every
74 /// `SegmentWriter` this engine creates.
75 pub(super) memory: ScopedMemory,
76}