nodedb_lite/nodedb/core/open/
constructors.rs1use std::num::NonZeroUsize;
20use std::sync::{Arc, Mutex};
21
22use nodedb_types::error::{NodeDbError, NodeDbResult};
23
24use crate::config::LiteConfig;
25use crate::engine::columnar::ColumnarEngine;
26use crate::engine::fts::FtsState;
27use crate::engine::htap::HtapBridge;
28use crate::engine::sparse_vector::SparseVectorState;
29use crate::engine::strict::StrictEngine;
30use crate::engine::vector::{RestoredVectorState, VectorState};
31use crate::nodedb::lock_ext::LockExt;
32use crate::storage::engine::StorageEngine;
33
34use crate::nodedb::core::kv_local::KvLocalState;
35use crate::nodedb::core::types::NodeDbLite;
36
37impl<S: StorageEngine> NodeDbLite<S> {
38 pub async fn open(storage: S) -> NodeDbResult<Arc<Self>> {
52 Self::open_with_config(storage, LiteConfig::from_env()).await
53 }
54
55 pub async fn open_with_config(storage: S, config: LiteConfig) -> NodeDbResult<Arc<Self>> {
62 Self::open_inner(storage, config).await
63 }
64
65 pub async fn open_with_budget(storage: S, memory_budget: usize) -> NodeDbResult<Arc<Self>> {
70 let config = LiteConfig {
71 memory_budget,
72 ..LiteConfig::default()
73 };
74 Self::open_with_config(storage, config).await
75 }
76
77 async fn open_inner(storage: S, config: LiteConfig) -> NodeDbResult<Arc<Self>> {
78 config.validate()?;
79
80 let governor = Arc::new(
81 nodedb_mem::MemoryGovernor::new(config.to_governor_config())
82 .map_err(NodeDbError::config)?,
83 );
84 #[cfg(not(target_arch = "wasm32"))]
85 let sync_enabled = config.sync_enabled;
86 let kv_cache_capacity = NonZeroUsize::new(config.kv_cache_capacity)
87 .ok_or_else(|| NodeDbError::config("kv_cache_capacity must be greater than 0"))?;
88 let kv_local = Arc::new(KvLocalState::new(kv_cache_capacity));
89
90 #[cfg(not(target_arch = "wasm32"))]
92 let outbound_queue_cap = config.outbound_queue_cap;
93
94 let storage = Arc::new(storage);
95
96 let (crdt, lite_identity, replayed_rows) =
99 Self::restore_identity_and_crdt(&storage, config.corruption_policy).await?;
100
101 let (fts_manager, fts_checkpoint_complete) =
103 Self::restore_fts_indices(&storage, &governor).await?;
104
105 let (sparse_manager, sparse_checkpoint_present) =
107 Self::restore_sparse_indices(&storage).await;
108
109 let graph_memory = nodedb_mem::ScopedMemory::new(
111 Arc::clone(&governor),
112 nodedb_types::DatabaseId::DEFAULT,
113 nodedb_types::TenantId::new(0),
114 nodedb_mem::EngineId::Graph,
115 );
116 let csr = Self::restore_csr_indices(&storage, &graph_memory).await?;
117
118 let (hnsw_map, hnsw_id_map) = Self::restore_hnsw_indices(&storage).await?;
120
121 let spatial_memory = nodedb_mem::ScopedMemory::new(
123 Arc::clone(&governor),
124 nodedb_types::DatabaseId::DEFAULT,
125 nodedb_types::TenantId::new(0),
126 nodedb_mem::EngineId::Spatial,
127 );
128 let spatial = Arc::new(Mutex::new(
129 Self::restore_spatial_indices(&storage, &spatial_memory).await,
130 ));
131
132 let strict = StrictEngine::restore(Arc::clone(&storage))
134 .await
135 .map_err(NodeDbError::storage)?;
136
137 let columnar_memory = nodedb_mem::ScopedMemory::new(
139 Arc::clone(&governor),
140 nodedb_types::DatabaseId::DEFAULT,
141 nodedb_types::TenantId::new(0),
142 nodedb_mem::EngineId::Columnar,
143 );
144 #[cfg(not(target_arch = "wasm32"))]
145 let mut columnar = ColumnarEngine::restore(Arc::clone(&storage), columnar_memory.clone())
146 .await
147 .map_err(NodeDbError::storage)?;
148 #[cfg(target_arch = "wasm32")]
149 let columnar = ColumnarEngine::restore(Arc::clone(&storage), columnar_memory.clone())
150 .await
151 .map_err(NodeDbError::storage)?;
152
153 #[cfg(not(target_arch = "wasm32"))]
155 let outbound_queues =
156 Self::build_outbound_queues(&storage, sync_enabled, outbound_queue_cap, &mut columnar)
157 .await?;
158 #[cfg(not(target_arch = "wasm32"))]
159 let columnar_outbound = outbound_queues.columnar_outbound;
160 #[cfg(not(target_arch = "wasm32"))]
161 let vector_outbound = outbound_queues.vector_outbound;
162 #[cfg(not(target_arch = "wasm32"))]
163 let fts_outbound_init = outbound_queues.fts_outbound;
164 #[cfg(not(target_arch = "wasm32"))]
165 let spatial_outbound_init = outbound_queues.spatial_outbound;
166 #[cfg(not(target_arch = "wasm32"))]
167 let timeseries_outbound_init = outbound_queues.timeseries_outbound;
168 #[cfg(not(target_arch = "wasm32"))]
169 let kv_outbound = outbound_queues.kv_outbound;
170
171 let crdt = Arc::new(Mutex::new(crdt));
172 let strict = Arc::new(strict);
173 let columnar = Arc::new(columnar);
174 let htap = Arc::new(HtapBridge::new());
175 let timeseries = Arc::new(Mutex::new(
176 crate::engine::timeseries::engine::TimeseriesEngine::new(),
177 ));
178 let vector_memory = nodedb_mem::ScopedMemory::new(
179 Arc::clone(&governor),
180 nodedb_types::DatabaseId::DEFAULT,
181 nodedb_types::TenantId::new(0),
182 nodedb_mem::EngineId::Vector,
183 );
184 let vector_state = Arc::new(VectorState::from_restored(RestoredVectorState {
185 storage: Arc::clone(&storage),
186 search_ef: 128,
187 indices: hnsw_map,
188 id_map: hnsw_id_map,
189 memory: vector_memory,
190 }));
191 let fts_state = Arc::new(FtsState::from_restored(fts_manager));
192 let sparse_state = Arc::new(SparseVectorState::from_restored(sparse_manager));
193 let array_engine = crate::engine::array::ArrayEngineState::open(&storage)
194 .await
195 .map_err(NodeDbError::storage)?;
196 let array_state = Arc::new(tokio::sync::Mutex::new(array_engine));
197
198 let csr_arc = Arc::new(Mutex::new(csr));
199 let query_engine =
200 crate::query::LiteQueryEngine::new(crate::query::LiteQueryEngineParams {
201 crdt: Arc::clone(&crdt),
202 strict: Arc::clone(&strict),
203 columnar: Arc::clone(&columnar),
204 htap: Arc::clone(&htap),
205 storage: Arc::clone(&storage),
206 timeseries: Arc::clone(×eries),
207 vector_state: Arc::clone(&vector_state),
208 array_state: Arc::clone(&array_state),
209 fts_state: Arc::clone(&fts_state),
210 sparse_state: Arc::clone(&sparse_state),
211 spatial: Arc::clone(&spatial),
212 csr: Arc::clone(&csr_arc),
213 governor: Arc::clone(&governor),
214 kv_local: Arc::clone(&kv_local),
215 });
216
217 #[cfg(not(target_arch = "wasm32"))]
222 let query_engine = {
223 let mut query_engine = query_engine;
224 if let Some(ref q) = fts_outbound_init {
225 query_engine.set_fts_outbound(Arc::clone(q));
226 }
227 if let Some(ref q) = spatial_outbound_init {
228 query_engine.set_spatial_outbound(Arc::clone(q));
229 }
230 if let Some(ref q) = kv_outbound {
231 query_engine.set_kv_outbound(Arc::clone(q));
232 }
233 query_engine
234 };
235
236 #[cfg(not(target_arch = "wasm32"))]
239 let array_sync = Self::build_array_sync_state(&storage, &array_state).await?;
240 #[cfg(not(target_arch = "wasm32"))]
241 let array_schemas = array_sync.array_schemas;
242 #[cfg(not(target_arch = "wasm32"))]
243 let array_outbound = array_sync.array_outbound;
244 #[cfg(not(target_arch = "wasm32"))]
245 let array_inbound = array_sync.array_inbound;
246 #[cfg(not(target_arch = "wasm32"))]
247 let stream_seq = array_sync.stream_seq;
248
249 let db = Self {
250 storage,
251 vector_state,
252 csr: csr_arc,
253 crdt,
254 governor,
255 vector_mem_token: Mutex::new(None),
256 graph_mem_token: Mutex::new(None),
257 crdt_mem_token: Mutex::new(None),
258 query_engine,
259 fts_state,
260 sparse_state,
261 spatial,
262 strict,
263 columnar,
264 htap,
265 timeseries,
266 array_state,
267 #[cfg(not(target_arch = "wasm32"))]
268 array_schemas,
269 #[cfg(not(target_arch = "wasm32"))]
270 array_outbound,
271 #[cfg(not(target_arch = "wasm32"))]
272 array_inbound,
273 #[cfg(not(target_arch = "wasm32"))]
274 stream_seq,
275 #[cfg(not(target_arch = "wasm32"))]
276 columnar_outbound,
277 #[cfg(not(target_arch = "wasm32"))]
278 vector_outbound,
279 #[cfg(not(target_arch = "wasm32"))]
280 fts_outbound: fts_outbound_init,
281 #[cfg(not(target_arch = "wasm32"))]
282 spatial_outbound: spatial_outbound_init,
283 #[cfg(not(target_arch = "wasm32"))]
284 timeseries_outbound: timeseries_outbound_init,
285 #[cfg(not(target_arch = "wasm32"))]
286 kv_outbound,
287 identity: Mutex::new(lite_identity),
288 identity_change: tokio::sync::Mutex::new(()),
289 flush_lock: tokio::sync::Mutex::new(()),
290 kv_local,
291 sync_gate: std::sync::RwLock::new(None),
292 tasks: crate::tasks::TaskRegistry::default(),
293 };
294
295 {
299 let mut known: std::collections::BTreeSet<String> = db
300 .crdt
301 .lock_or_recover()
302 .collection_names()
303 .into_iter()
304 .collect();
305 known.extend(db.strict.collection_names());
306 known.extend(db.columnar.collection_names());
307 known.extend(
308 crate::nodedb::collection::ddl::load_persisted_collection_metas(&*db.storage)
309 .await?
310 .into_keys(),
311 );
312 crate::index::legacy::clear_legacy_entries(&*db.storage, &known)
313 .await
314 .map_err(NodeDbError::storage)?;
315 let indexes = &db.query_engine.indexes;
316 let loaded = indexes
317 .load(&*db.storage)
318 .await
319 .map_err(NodeDbError::storage)?;
320 crate::index::rebuild::rebuild_at_open(
321 indexes,
322 &*db.storage,
323 &db.crdt,
324 &db.strict,
325 loaded.rebuild_all,
326 )
327 .await
328 .map_err(NodeDbError::storage)?;
329 }
330
331 {
334 let fts_empty = db.fts_state.manager.lock_or_recover().is_empty();
335 if fts_empty || !fts_checkpoint_complete || !sparse_checkpoint_present {
336 db.rebuild_text_indices().await?;
337 }
338 db.rebuild_authoritative_text_indices().await?;
339 for (collection, rows) in &replayed_rows {
340 if db.strict.schema(collection).is_some()
341 || crate::engine::document::history::ops::is_bitemporal(
342 db.storage.as_ref(),
343 collection,
344 )
345 .await?
346 {
347 continue;
348 }
349 db.reindex_documents_local(collection, rows.iter().map(String::as_str))?;
350 }
351 db.fts_state.mark_checkpoint_trusted();
352 }
353
354 let spatial_empty = db.spatial.lock_or_recover().is_empty();
358 if spatial_empty {
359 db.rebuild_spatial_indices();
360 }
361
362 db.migrate_legacy_edge_keys()
367 .await
368 .map_err(NodeDbError::storage)?;
369
370 let csr_empty = db.csr.lock_or_recover().is_empty();
378 if csr_empty {
379 db.rebuild_graph_indices().await;
380 }
381
382 let db = Arc::new(db);
386 db.start_auto_flush(config.auto_flush_ms);
387 db.start_auto_compact(config.auto_compact_ms);
388
389 Ok(db)
390 }
391}