Skip to main content

nodedb_types/timeseries/
config.rs

1//! Tiered partition configuration.
2
3use serde::{Deserialize, Serialize};
4
5use super::partition::PartitionInterval;
6
7/// Error validating a `TieredPartitionConfig`.
8#[derive(Debug, thiserror::Error)]
9#[error("config validation: {field} — {reason}")]
10pub struct ConfigValidationError {
11    pub field: String,
12    pub reason: String,
13}
14
15/// Compression codec for archived (cold) partitions.
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
17pub enum ArchiveCompression {
18    #[default]
19    Zstd,
20    Lz4,
21    Snappy,
22}
23
24/// Full lifecycle configuration for a timeseries collection.
25///
26/// All fields have sensible defaults. Platform-aware: Origin and Lite
27/// use different defaults for memory budgets and retention.
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct TieredPartitionConfig {
30    // -- Tier 0: Hot (RAM memtable) --
31    pub memtable_flush_interval_ms: u64,
32    pub memtable_max_memory_bytes: u64,
33
34    // -- Tier 1: Warm (NVMe, columnar partitions) --
35    pub partition_by: PartitionInterval,
36    pub merge_after_ms: u64,
37    pub merge_count: u32,
38
39    // -- Tier 2: Cold (S3 Parquet) --
40    pub archive_after_ms: u64,
41    pub archive_compression: ArchiveCompression,
42
43    // -- Retention --
44    pub retention_period_ms: u64,
45
46    // -- Timestamp --
47    pub timestamp_column: String,
48
49    // -- Tags --
50    pub max_tag_cardinality: u32,
51
52    // -- WAL --
53    pub wal_enabled: bool,
54
55    // -- CDC --
56    pub cdc_enabled: bool,
57
58    // -- Sync (Lite only) --
59    pub sync_resolution_ms: u64,
60    pub sync_interval_ms: u64,
61    pub retain_until_synced: bool,
62
63    // -- Battery (Lite-B mobile only) --
64    #[serde(default)]
65    pub battery_aware: bool,
66
67    // -- Burst ingestion (Lite-C) --
68    #[serde(default)]
69    pub bulk_import_threshold_rows: u64,
70
71    // -- Partition size targets (Lite-C) --
72    #[serde(default)]
73    pub partition_size_target_bytes: u64,
74
75    // -- Compaction (Lite-C) --
76    #[serde(default)]
77    pub compaction_partition_threshold: u32,
78}
79
80impl TieredPartitionConfig {
81    /// Default configuration for Origin (cloud server).
82    pub fn origin_defaults() -> Self {
83        Self {
84            memtable_flush_interval_ms: 10_000,
85            memtable_max_memory_bytes: 64 * 1024 * 1024,
86            partition_by: PartitionInterval::Auto,
87            merge_after_ms: 30 * 86_400_000,
88            merge_count: 10,
89            archive_after_ms: 0,
90            retention_period_ms: 0,
91            archive_compression: ArchiveCompression::Zstd,
92            timestamp_column: String::new(),
93            max_tag_cardinality: 100_000,
94            wal_enabled: true,
95            cdc_enabled: false,
96            sync_resolution_ms: 0,
97            sync_interval_ms: 0,
98            retain_until_synced: false,
99            battery_aware: false,
100            bulk_import_threshold_rows: 0,
101            partition_size_target_bytes: 0,
102            compaction_partition_threshold: 0,
103        }
104    }
105
106    /// Default configuration for Lite (edge device).
107    pub fn lite_defaults() -> Self {
108        Self {
109            memtable_flush_interval_ms: 30_000,
110            memtable_max_memory_bytes: 4 * 1024 * 1024,
111            partition_by: PartitionInterval::Auto,
112            merge_after_ms: 7 * 86_400_000,
113            merge_count: 4,
114            archive_after_ms: 0,
115            retention_period_ms: 7 * 86_400_000,
116            archive_compression: ArchiveCompression::Zstd,
117            timestamp_column: String::new(),
118            max_tag_cardinality: 10_000,
119            wal_enabled: true,
120            cdc_enabled: false,
121            sync_resolution_ms: 0,
122            sync_interval_ms: 30_000,
123            retain_until_synced: false,
124            battery_aware: false,
125            bulk_import_threshold_rows: 1_000_000,
126            partition_size_target_bytes: 1_024 * 1_024,
127            compaction_partition_threshold: 20,
128        }
129    }
130
131    /// Validate configuration consistency.
132    pub fn validate(&self) -> Result<(), ConfigValidationError> {
133        if self.merge_count < 2 {
134            return Err(ConfigValidationError {
135                field: "merge_count".into(),
136                reason: "must be >= 2".into(),
137            });
138        }
139        if self.retention_period_ms > 0
140            && self.archive_after_ms > 0
141            && self.retention_period_ms < self.archive_after_ms
142        {
143            return Err(ConfigValidationError {
144                field: "retention_period".into(),
145                reason: "must be >= archive_after".into(),
146            });
147        }
148        Ok(())
149    }
150}