nodedb_types/timeseries/
config.rs1use serde::{Deserialize, Serialize};
4
5use super::partition::PartitionInterval;
6
7#[derive(Debug, thiserror::Error)]
9#[error("config validation: {field} — {reason}")]
10pub struct ConfigValidationError {
11 pub field: String,
12 pub reason: String,
13}
14
15#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
17pub enum ArchiveCompression {
18 #[default]
19 Zstd,
20 Lz4,
21 Snappy,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct TieredPartitionConfig {
30 pub memtable_flush_interval_ms: u64,
32 pub memtable_max_memory_bytes: u64,
33
34 pub partition_by: PartitionInterval,
36 pub merge_after_ms: u64,
37 pub merge_count: u32,
38
39 pub archive_after_ms: u64,
41 pub archive_compression: ArchiveCompression,
42
43 pub retention_period_ms: u64,
45
46 pub timestamp_column: String,
48
49 pub max_tag_cardinality: u32,
51
52 pub wal_enabled: bool,
54
55 pub cdc_enabled: bool,
57
58 pub sync_resolution_ms: u64,
60 pub sync_interval_ms: u64,
61 pub retain_until_synced: bool,
62
63 #[serde(default)]
65 pub battery_aware: bool,
66
67 #[serde(default)]
69 pub bulk_import_threshold_rows: u64,
70
71 #[serde(default)]
73 pub partition_size_target_bytes: u64,
74
75 #[serde(default)]
77 pub compaction_partition_threshold: u32,
78}
79
80impl TieredPartitionConfig {
81 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 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 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}