nodedb_types/timeseries/
config.rs1use serde::{Deserialize, Serialize};
6
7use super::partition::PartitionInterval;
8
9#[derive(Debug, thiserror::Error)]
11#[error("config validation: {field} — {reason}")]
12pub struct ConfigValidationError {
13 pub field: String,
14 pub reason: String,
15}
16
17#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "lowercase")]
20#[non_exhaustive]
21pub enum ArchiveCompression {
22 #[default]
23 #[serde(rename = "zstd")]
24 Zstd,
25 #[serde(rename = "lz4")]
26 Lz4,
27 #[serde(rename = "snappy")]
28 Snappy,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct TieredPartitionConfig {
37 pub memtable_flush_interval_ms: u64,
39 pub memtable_max_memory_bytes: u64,
40
41 pub partition_by: PartitionInterval,
43 pub merge_after_ms: u64,
44 pub merge_count: u32,
45
46 pub archive_after_ms: u64,
48 pub archive_compression: ArchiveCompression,
49
50 pub retention_period_ms: u64,
52
53 pub timestamp_column: String,
55
56 pub max_tag_cardinality: u32,
58
59 pub wal_enabled: bool,
61
62 pub cdc_enabled: bool,
64
65 pub sync_resolution_ms: u64,
67 pub sync_interval_ms: u64,
68 pub retain_until_synced: bool,
69
70 #[serde(default)]
72 pub battery_aware: bool,
73
74 #[serde(default)]
76 pub bulk_import_threshold_rows: u64,
77
78 #[serde(default)]
80 pub partition_size_target_bytes: u64,
81
82 #[serde(default)]
84 pub compaction_partition_threshold: u32,
85}
86
87impl TieredPartitionConfig {
88 pub fn origin_defaults() -> Self {
90 Self {
91 memtable_flush_interval_ms: 10_000,
92 memtable_max_memory_bytes: 64 * 1024 * 1024,
93 partition_by: PartitionInterval::Auto,
94 merge_after_ms: 30 * 86_400_000,
95 merge_count: 10,
96 archive_after_ms: 0,
97 retention_period_ms: 0,
98 archive_compression: ArchiveCompression::Zstd,
99 timestamp_column: String::new(),
100 max_tag_cardinality: 100_000,
101 wal_enabled: true,
102 cdc_enabled: false,
103 sync_resolution_ms: 0,
104 sync_interval_ms: 0,
105 retain_until_synced: false,
106 battery_aware: false,
107 bulk_import_threshold_rows: 0,
108 partition_size_target_bytes: 0,
109 compaction_partition_threshold: 0,
110 }
111 }
112
113 pub fn lite_defaults() -> Self {
115 Self {
116 memtable_flush_interval_ms: 30_000,
117 memtable_max_memory_bytes: 4 * 1024 * 1024,
118 partition_by: PartitionInterval::Auto,
119 merge_after_ms: 7 * 86_400_000,
120 merge_count: 4,
121 archive_after_ms: 0,
122 retention_period_ms: 7 * 86_400_000,
123 archive_compression: ArchiveCompression::Zstd,
124 timestamp_column: String::new(),
125 max_tag_cardinality: 10_000,
126 wal_enabled: true,
127 cdc_enabled: false,
128 sync_resolution_ms: 0,
129 sync_interval_ms: 30_000,
130 retain_until_synced: false,
131 battery_aware: false,
132 bulk_import_threshold_rows: 1_000_000,
133 partition_size_target_bytes: 1_024 * 1_024,
134 compaction_partition_threshold: 20,
135 }
136 }
137
138 pub fn validate(&self) -> Result<(), ConfigValidationError> {
140 if self.merge_count < 2 {
141 return Err(ConfigValidationError {
142 field: "merge_count".into(),
143 reason: "must be >= 2".into(),
144 });
145 }
146 if self.retention_period_ms > 0
147 && self.archive_after_ms > 0
148 && self.retention_period_ms < self.archive_after_ms
149 {
150 return Err(ConfigValidationError {
151 field: "retention_period".into(),
152 reason: "must be >= archive_after".into(),
153 });
154 }
155 Ok(())
156 }
157}