1use std::collections::HashMap;
4use std::path::Path;
5
6use crate::common::fs::{atomic_save_json, read_json};
7use crate::segment::common::operation_error::{OperationError, OperationResult};
8use crate::segment::types::{
9 HnswConfig, PayloadStorageType, QuantizationConfig, SegmentConfig, VectorNameBuf,
10};
11use serde::{Deserialize, Serialize};
12use crate::shard::operations::optimization::OptimizerThresholds;
13use crate::wal::WalOptions;
14
15use super::optimizers::EdgeOptimizersConfig;
16use super::vectors::{EdgeSparseVectorParams, EdgeVectorParams};
17
18pub(crate) const EDGE_CONFIG_FILE: &str = "edge_config.json";
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub struct EdgeConfig {
25 #[serde(default = "default_on_disk_payload")]
27 pub on_disk_payload: bool,
28 #[serde(default)]
30 pub vectors: HashMap<VectorNameBuf, EdgeVectorParams>,
31 #[serde(default)]
33 pub sparse_vectors: HashMap<VectorNameBuf, EdgeSparseVectorParams>,
34 #[serde(default)]
36 pub hnsw_config: HnswConfig,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub quantization_config: Option<QuantizationConfig>,
41 #[serde(default)]
42 pub optimizers: EdgeOptimizersConfig,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub wal_options: Option<WalOptions>,
48}
49
50fn default_on_disk_payload() -> bool {
51 true
52}
53
54impl Default for EdgeConfig {
55 fn default() -> Self {
56 Self {
57 on_disk_payload: default_on_disk_payload(),
58 vectors: HashMap::new(),
59 sparse_vectors: HashMap::new(),
60 hnsw_config: HnswConfig::default(),
61 quantization_config: None,
62 optimizers: EdgeOptimizersConfig::default(),
63 wal_options: None,
64 }
65 }
66}
67
68impl EdgeConfig {
69 pub fn builder() -> crate::edge::builders::EdgeConfigBuilder {
71 crate::edge::builders::EdgeConfigBuilder::new()
72 }
73
74 pub fn from_segment_config(segment: &SegmentConfig) -> Self {
76 let SegmentConfig {
77 vector_data,
78 sparse_vector_data,
79 payload_storage_type,
80 } = segment;
81
82 let vectors = vector_data
83 .iter()
84 .map(|(name, v)| (name.clone(), EdgeVectorParams::from_vector_data_config(v)))
85 .collect();
86
87 let sparse_vectors = sparse_vector_data
88 .iter()
89 .map(|(name, s)| {
90 (
91 name.clone(),
92 EdgeSparseVectorParams::from_sparse_vector_data_config(s),
93 )
94 })
95 .collect();
96
97 let on_disk_payload = payload_storage_type.is_on_disk();
98
99 let hnsw_configs: Vec<HnswConfig> = vector_data
101 .values()
102 .filter_map(|v| match &v.index {
103 crate::segment::types::Indexes::Plain {} => None,
104 crate::segment::types::Indexes::Hnsw(h) => Some(*h),
105 })
106 .collect();
107 let hnsw_config = hnsw_configs
108 .first()
109 .and_then(|first| {
110 if hnsw_configs.iter().all(|h| h == first) {
111 Some(*first)
112 } else {
113 None
114 }
115 })
116 .unwrap_or_default();
117
118 Self {
119 on_disk_payload,
120 vectors,
121 sparse_vectors,
122 hnsw_config,
123 quantization_config: None,
124 optimizers: EdgeOptimizersConfig::default(),
125 wal_options: None,
126 }
127 }
128
129 pub fn check_compatible_with_segment_config(
131 &self,
132 other: &SegmentConfig,
133 ) -> Result<(), String> {
134 self.plain_segment_config().check_compatible(other)
135 }
136
137 pub fn plain_segment_config(&self) -> SegmentConfig {
140 let payload_storage_type = PayloadStorageType::from_on_disk_payload(self.on_disk_payload);
141 let vector_data = self
142 .vectors
143 .iter()
144 .map(|(name, p)| {
145 (
146 name.clone(),
147 p.to_plain_vector_data_config(self.quantization_config.as_ref()),
148 )
149 })
150 .collect();
151
152 let sparse_vector_data = self
153 .sparse_vectors
154 .iter()
155 .map(|(name, p)| (name.clone(), p.to_plain_sparse_vector_data_config()))
156 .collect();
157
158 SegmentConfig {
159 vector_data,
160 sparse_vector_data,
161 payload_storage_type,
162 }
163 }
164
165 pub fn segment_optimizer_config(&self) -> crate::shard::optimizers::config::SegmentOptimizerConfig {
168 use crate::shard::optimizers::config::SegmentOptimizerConfig;
169
170 let SegmentConfig {
171 vector_data: plain_dense_vector_config,
172 sparse_vector_data: plain_sparse_vector_config,
173 payload_storage_type,
174 } = self.plain_segment_config();
175
176 let dense_vector = self
177 .vectors
178 .iter()
179 .map(|(name, p)| {
180 (
181 name.clone(),
182 p.to_dense_vector_optimizer_config(
183 &self.hnsw_config,
184 self.quantization_config.as_ref(),
185 ),
186 )
187 })
188 .collect();
189
190 let sparse_vector = self
191 .sparse_vectors
192 .iter()
193 .map(|(name, p)| (name.clone(), p.to_sparse_vector_optimizer_config()))
194 .collect();
195
196 SegmentOptimizerConfig {
197 payload_storage_type,
198 plain_dense_vector_config,
199 plain_sparse_vector_config,
200 dense_vector,
201 sparse_vector,
202 }
203 }
204
205 pub fn vector_data_config(
208 &self,
209 name: &VectorNameBuf,
210 ) -> Option<crate::segment::types::VectorDataConfig> {
211 self.vectors
212 .get(name)
213 .map(|p| p.to_plain_vector_data_config(self.quantization_config.as_ref()))
214 }
215
216 pub fn optimizer_thresholds(&self, num_indexing_threads: usize) -> OptimizerThresholds {
217 let indexing_threshold_kb = self.optimizers.get_indexing_threshold_kb();
218 OptimizerThresholds {
219 memmap_threshold_kb: usize::MAX,
220 indexing_threshold_kb,
221 max_segment_size_kb: self
222 .optimizers
223 .get_max_segment_size_kb(num_indexing_threads),
224 deferred_internal_id: None,
225 }
226 }
227
228 pub fn save(&self, path: &Path) -> OperationResult<()> {
229 let config_path = path.join(EDGE_CONFIG_FILE);
230 atomic_save_json(&config_path, self).map_err(|e| {
231 OperationError::service_error(format!(
232 "failed to write {}: {}",
233 config_path.display(),
234 e
235 ))
236 })
237 }
238
239 pub fn load(path: &Path) -> Option<OperationResult<Self>> {
240 let config_path = path.join(EDGE_CONFIG_FILE);
241 match fs_err::exists(&config_path) {
242 Ok(false) => return None,
243 Err(e) => return Some(Err(OperationError::from(e))),
244 Ok(true) => {}
245 }
246 Some(read_json(&config_path).map_err(OperationError::from))
247 }
248
249 pub fn set_hnsw_config(&mut self, hnsw_config: HnswConfig) {
250 self.hnsw_config = hnsw_config;
251 }
252
253 pub fn set_vector_hnsw_config(
254 &mut self,
255 vector_name: &str,
256 hnsw_config: HnswConfig,
257 ) -> OperationResult<()> {
258 let name = VectorNameBuf::from(vector_name);
259 let params = self
260 .vectors
261 .get_mut(&name)
262 .ok_or_else(|| OperationError::vector_name_not_exists(vector_name))?;
263 params.hnsw_config = Some(hnsw_config);
264 Ok(())
265 }
266
267 pub fn set_optimizers_config(&mut self, optimizers: EdgeOptimizersConfig) {
268 self.optimizers = optimizers;
269 }
270}