1use super::*;
2
3mod table_db;
4mod tasks;
5
6pub use table_db::*;
7
8#[cfg(any(test, feature = "test-util"))]
9#[doc(hidden)]
10pub mod tests_table_store;
11
12#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
13mod wasm;
14#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
15use wasm::*;
16#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
17mod native;
18#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
19use native::*;
20
21use keyvaluedb::*;
22use weak_table::WeakValueHashMap;
23
24impl_veilid_log_facility!("tstore");
25
26const ALL_TABLE_NAMES: &[u8] = b"all_table_names";
27const FLUSH_TABLES_INTERVAL_SECS: u32 = 60;
28const CLEANUP_TABLES_INTERVAL_SECS: u32 = 600;
29
30#[apply(api_data_struct!)]
32#[api(eq, default, ts)]
33pub struct ColumnInfo {
34 pub key_count: AlignedU64,
35}
36
37#[apply(api_data_struct!)]
39#[api(eq, default, ts)]
40pub struct IOStatsInfo {
41 pub transactions: AlignedU64,
43 pub reads: AlignedU64,
45 pub cache_reads: AlignedU64,
47 pub writes: AlignedU64,
49 pub bytes_read: ByteCount,
51 pub cache_read_bytes: ByteCount,
53 pub bytes_written: ByteCount,
55 pub deletes: AlignedU64,
57 pub prefix_deletes: AlignedU64,
59 pub write_size_buckets: BTreeMap<usize, u64>,
62 pub tx_write_size_buckets: BTreeMap<usize, (u64, TimestampDuration)>,
66 pub started: Timestamp,
68 pub span: TimestampDuration,
70}
71
72#[apply(api_data_struct!)]
74#[api(eq, default, ts)]
75pub struct TableInfo {
76 pub table_name: String,
78 pub io_stats_since_previous: IOStatsInfo,
80 pub io_stats_overall: IOStatsInfo,
82 pub column_count: u32,
84 pub columns: Vec<ColumnInfo>,
86}
87
88#[must_use]
89struct TableStoreInner {
90 opened: WeakValueHashMap<String, Weak<TableDBUnlockedInner>>,
91 encryption_key: Option<SharedSecret>,
92 all_table_names: HashMap<String, String>,
93 all_tables_db: Option<Database>,
94 tick_subscription: Option<EventBusSubscription>,
96}
97
98impl fmt::Debug for TableStoreInner {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("TableStoreInner")
101 .field("opened", &self.opened)
102 .field("encryption_key", &self.encryption_key)
103 .field("all_table_names", &self.all_table_names)
104 .finish()
105 }
106}
107
108#[must_use]
111pub struct TableStore {
112 registry: VeilidComponentRegistry,
113 startup_lock: StartupLock,
114 inner: Mutex<TableStoreInner>, table_store_driver: TableStoreDriver,
116 async_lock: Arc<AsyncMutex<()>>,
117 flush_tables_task: TickTask<EyreReport>,
118 cleanup_tables_task: TickTask<EyreReport>,
119}
120
121impl fmt::Debug for TableStore {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 f.debug_struct("TableStore")
124 .field("registry", &self.registry)
125 .field("startup_lock", &self.startup_lock)
126 .field("inner", &self.inner)
127 .field("async_lock", &self.async_lock)
128 .finish()
129 }
130}
131
132impl_veilid_component!(TableStore);
133
134impl TableStore {
135 fn new_inner() -> TableStoreInner {
136 TableStoreInner {
137 opened: WeakValueHashMap::new(),
138 encryption_key: None,
139 all_table_names: HashMap::new(),
140 all_tables_db: None,
141 tick_subscription: None,
142 }
143 }
144 pub(crate) fn new(registry: VeilidComponentRegistry) -> Self {
145 let inner = Self::new_inner();
146 let table_store_driver = TableStoreDriver::new(registry.clone());
147
148 let this = Self {
149 registry,
150 startup_lock: StartupLock::new(),
151 inner: Mutex::new(inner),
152 table_store_driver,
153 async_lock: Arc::new(AsyncMutex::new(())),
154 flush_tables_task: TickTask::new("flush_tables_task", FLUSH_TABLES_INTERVAL_SECS),
155 cleanup_tables_task: TickTask::new("cleanup_tables_task", CLEANUP_TABLES_INTERVAL_SECS),
156 };
157
158 this.setup_tasks();
159
160 this
161 }
162
163 async fn flush(&self) {
165 let (all_table_names_value, all_tables_db) = {
166 let inner = self.inner.lock();
167 let all_table_names_value = serialize_json_bytes(&inner.all_table_names);
168 (
169 all_table_names_value,
170 inner.all_tables_db.clone().unwrap_or_log(),
171 )
172 };
173 let mut dbt = DBTransaction::new();
174 dbt.put(0, ALL_TABLE_NAMES, &all_table_names_value);
175 if let Err(e) = all_tables_db.write(dbt).await {
176 veilid_log!(self error "failed to write all tables db: {}", e);
177 }
178 }
179
180 async fn cleanup(&self) {
182 let all_open_db: Vec<_> = {
184 let inner = self.inner.lock();
185 inner.opened.values().collect()
186 };
187 for db in all_open_db {
188 let tdb = TableDB::new_from_unlocked_inner(db, 0);
189 if let Err(e) = tdb.cleanup().await {
190 veilid_log!(self error "Error cleaning up database '{}': {}", tdb.table_name(), e);
191 }
192 }
193 }
194
195 fn namespaced_name(&self, table: &str) -> VeilidAPIResult<String> {
199 if !table
200 .chars()
201 .all(|c| char::is_alphanumeric(c) || c == '_' || c == '-')
202 {
203 apibail_invalid_argument!("table name is invalid", "table", table);
204 }
205 let namespace = self.config().namespace.clone();
206 Ok(if namespace.is_empty() {
207 table.to_string()
208 } else {
209 format!("_ns_{}_{}", namespace, table)
210 })
211 }
212
213 fn name_get_or_create(&self, table: &str) -> VeilidAPIResult<String> {
214 let name = self.namespaced_name(table)?;
215
216 let mut inner = self.inner.lock();
217 if let Some(real_name) = inner.all_table_names.get(&name) {
219 return Ok(real_name.clone());
220 }
221
222 let mut real_name_bytes = [0u8; 32];
224 random_bytes(&mut real_name_bytes);
225 let real_name = data_encoding::BASE64URL_NOPAD.encode(&real_name_bytes);
226
227 if inner
228 .all_table_names
229 .insert(name.to_owned(), real_name.clone())
230 .is_some()
231 {
232 veilid_log!(self error "should not have already had this table name: {}", name);
233 };
234
235 Ok(real_name)
236 }
237
238 #[cfg_attr(
239 feature = "instrument",
240 instrument(level = "trace", target = "tstore", skip_all)
241 )]
242 fn name_delete(&self, table: &str) -> VeilidAPIResult<Option<String>> {
243 let name = self.namespaced_name(table)?;
244 let mut inner = self.inner.lock();
245 let real_name = inner.all_table_names.remove(&name);
246 Ok(real_name)
247 }
248
249 #[cfg_attr(
250 feature = "instrument",
251 instrument(level = "trace", target = "tstore", skip_all)
252 )]
253 fn name_get(&self, table: &str) -> VeilidAPIResult<Option<String>> {
254 let name = self.namespaced_name(table)?;
255 let inner = self.inner.lock();
256 let real_name = inner.all_table_names.get(&name).cloned();
257 Ok(real_name)
258 }
259
260 #[cfg_attr(
261 feature = "instrument",
262 instrument(level = "trace", target = "tstore", skip_all)
263 )]
264 fn name_rename(&self, old_table: &str, new_table: &str) -> VeilidAPIResult<()> {
265 let old_name = self.namespaced_name(old_table)?;
266 let new_name = self.namespaced_name(new_table)?;
267
268 let mut inner = self.inner.lock();
269 if inner.all_table_names.contains_key(&new_name) {
271 return Err(VeilidAPIError::generic("new table already exists"));
272 }
273 let Some(real_name) = inner.all_table_names.remove(&old_name) else {
275 return Err(VeilidAPIError::generic("table does not exist"));
276 };
277 inner.all_table_names.insert(new_name.to_owned(), real_name);
279
280 Ok(())
281 }
282
283 #[cfg_attr(
287 feature = "instrument",
288 instrument(level = "trace", target = "tstore", skip_all)
289 )]
290 pub fn list_all(&self) -> Vec<(String, String)> {
291 let Ok(_startup_guard) = self.startup_lock.enter() else {
292 return vec![];
293 };
294
295 let inner = self.inner.lock();
296 inner
297 .all_table_names
298 .iter()
299 .map(|(k, v)| (k.clone(), v.clone()))
300 .collect::<Vec<(String, String)>>()
301 }
302
303 #[cfg_attr(
307 feature = "instrument",
308 instrument(level = "trace", target = "tstore", skip_all)
309 )]
310 pub async fn delete_all(&self) {
311 let Ok(_startup_guard) = self.startup_lock.enter() else {
312 return;
313 };
314
315 let table_names = {
317 let mut inner = self.inner.lock();
318 let real_names = inner
319 .all_table_names
320 .iter()
321 .map(|(k, v)| (k.clone(), v.clone()))
322 .collect::<Vec<(String, String)>>();
323 inner.all_table_names.clear();
324 real_names
325 };
326
327 for (table_name, table_real_name) in table_names {
329 veilid_log!(self debug "deleting table: {} ({})", table_real_name, table_name);
330 if let Err(e) = self.table_store_driver.delete(&table_real_name).await {
331 veilid_log!(self error "error deleting table: {}", e);
332 }
333 }
334 self.flush().await;
335 }
336
337 #[cfg_attr(
338 feature = "instrument",
339 instrument(level = "trace", target = "tstore", skip_all)
340 )]
341 pub(crate) async fn maybe_unprotect_device_encryption_key(
342 &self,
343 dek_bytes: &[u8],
344 device_encryption_key_password: &str,
345 ) -> EyreResult<SharedSecret> {
346 if dek_bytes.len() < 4 {
349 bail!("device encryption key is not valid");
350 }
351
352 let kind = CryptoKind::try_from(&dek_bytes[0..4]).unwrap_or_log();
354 let crypto = self.crypto();
355 let Some(vcrypto) = crypto.get_async(kind) else {
356 bail!("unsupported cryptosystem '{kind}'");
357 };
358
359 if !device_encryption_key_password.is_empty() {
360 if dek_bytes.len()
361 != (4
362 + vcrypto.shared_secret_length()
363 + vcrypto.aead_overhead()
364 + vcrypto.nonce_length())
365 {
366 bail!("password protected device encryption key is not valid");
367 }
368 let protected_key =
369 &dek_bytes[4..(4 + vcrypto.shared_secret_length() + vcrypto.aead_overhead())];
370 let nonce = Nonce::new(
371 &dek_bytes[(4 + vcrypto.shared_secret_length() + vcrypto.aead_overhead())..],
372 );
373 let shared_secret = vcrypto
374 .derive_shared_secret(
375 Bytes::copy_from_slice(device_encryption_key_password.as_bytes()),
376 nonce.bytes(),
377 )
378 .await
379 .wrap_err("failed to derive shared secret")?;
380
381 let unprotected_key = vcrypto
382 .decrypt_aead(
383 Bytes::copy_from_slice(protected_key),
384 &nonce,
385 &shared_secret,
386 None,
387 )
388 .await
389 .wrap_err("failed to decrypt device encryption key")?;
390
391 return Ok(SharedSecret::new(
392 kind,
393 BareSharedSecret::new(unprotected_key.as_ref()),
394 ));
395 }
396
397 if dek_bytes.len() != (4 + vcrypto.shared_secret_length()) {
398 bail!("unprotected device encryption key is not valid");
399 }
400
401 Ok(SharedSecret::new(
402 kind,
403 BareSharedSecret::new(&dek_bytes[4..]),
404 ))
405 }
406
407 #[cfg_attr(
408 feature = "instrument",
409 instrument(level = "trace", target = "tstore", skip_all)
410 )]
411 pub(crate) async fn maybe_protect_device_encryption_key(
412 &self,
413 dek: SharedSecret,
414 device_encryption_key_password: &str,
415 ) -> EyreResult<Vec<u8>> {
416 if device_encryption_key_password.is_empty() {
418 veilid_log!(self debug "no dek password");
419 return Ok(Vec::from(dek));
421 }
422
423 let crypto = self.crypto();
425 let Some(vcrypto) = crypto.get_async(dek.kind()) else {
426 bail!("unsupported cryptosystem '{}'", dek.kind());
427 };
428
429 let nonce = vcrypto.random_nonce().await;
430 let shared_secret = vcrypto
431 .derive_shared_secret(
432 Bytes::copy_from_slice(device_encryption_key_password.as_bytes()),
433 Bytes::copy_from_slice(&nonce),
434 )
435 .await
436 .wrap_err("failed to derive shared secret")?;
437 let protected_key = vcrypto
438 .encrypt_aead(
439 Bytes::copy_from_slice(dek.ref_value()),
440 &nonce,
441 &shared_secret,
442 None,
443 )
444 .await
445 .wrap_err("failed to decrypt device encryption key")?;
446 let mut out = Vec::with_capacity(
447 4 + vcrypto.shared_secret_length() + vcrypto.aead_overhead() + vcrypto.nonce_length(),
448 );
449 out.extend_from_slice(dek.kind().bytes());
450 out.extend_from_slice(&protected_key);
451 out.extend_from_slice(&nonce);
452 debug_assert_eq!(
453 out.len(),
454 4 + vcrypto.shared_secret_length() + vcrypto.aead_overhead() + vcrypto.nonce_length()
455 );
456 Ok(out)
457 }
458
459 #[cfg_attr(
460 feature = "instrument",
461 instrument(level = "trace", target = "tstore", skip_all)
462 )]
463 async fn load_device_encryption_key(&self) -> EyreResult<Option<SharedSecret>> {
464 let dek_bytes: Option<Vec<u8>> = self
465 .protected_store()
466 .load_user_secret("device_encryption_key")?;
467 let Some(dek_bytes) = dek_bytes else {
468 veilid_log!(self debug "no device encryption key");
469 return Ok(None);
470 };
471
472 let device_encryption_key_password = self
474 .config()
475 .protected_store
476 .device_encryption_key_password
477 .clone();
478
479 Ok(Some(
480 self.maybe_unprotect_device_encryption_key(&dek_bytes, &device_encryption_key_password)
481 .await?,
482 ))
483 }
484
485 #[cfg_attr(
486 feature = "instrument",
487 instrument(level = "trace", target = "tstore", skip_all)
488 )]
489 async fn save_device_encryption_key(
490 &self,
491 device_encryption_key: Option<SharedSecret>,
492 ) -> EyreResult<()> {
493 let Some(device_encryption_key) = device_encryption_key else {
494 let existed = self
496 .protected_store()
497 .remove_user_secret("device_encryption_key")?;
498 veilid_log!(self debug "removed device encryption key. existed: {}", existed);
499 return Ok(());
500 };
501
502 let new_device_encryption_key_password = self
504 .config()
505 .protected_store
506 .new_device_encryption_key_password
507 .clone();
508 let device_encryption_key_password =
509 if let Some(new_device_encryption_key_password) = new_device_encryption_key_password {
510 veilid_log!(self debug "changing dek password");
512 new_device_encryption_key_password
513 } else {
514 veilid_log!(self debug "saving with existing dek password");
516 self.config()
517 .protected_store
518 .device_encryption_key_password
519 .clone()
520 };
521
522 let dek_bytes = self
523 .maybe_protect_device_encryption_key(
524 device_encryption_key,
525 &device_encryption_key_password,
526 )
527 .await?;
528
529 let existed = self
531 .protected_store()
532 .save_user_secret("device_encryption_key", &dek_bytes)?;
533 veilid_log!(self debug "saving device encryption key. existed: {}", existed);
534 Ok(())
535 }
536
537 fn log_facilities_impl(&self) -> VeilidComponentLogFacilities {
538 VeilidComponentLogFacilities::new().with_facility(
539 VeilidComponentLogFacility::try_new_with_tags("tstore", ["#common"]).unwrap(),
540 )
541 }
542
543 #[cfg_attr(
544 feature = "instrument",
545 instrument(level = "trace", target = "tstore", skip_all)
546 )]
547 async fn init_async(&self) -> EyreResult<()> {
548 let startup_guard = self.startup_lock.startup()?;
549 {
550 let _async_guard = self.async_lock.lock().await;
551
552 let mut device_encryption_key = self.load_device_encryption_key().await?;
554 let mut device_encryption_key_changed = false;
555 if let Some(device_encryption_key) = &device_encryption_key {
556 let best_kind = best_crypto_kind();
558 if device_encryption_key.kind() != best_kind {
559 veilid_log!(self error "Need to write migration support");
561 }
562 } else {
563 let crypto = self.crypto();
565 let vcrypto = crypto.best_async();
566 let shared_secret = vcrypto.random_shared_secret().await;
567
568 device_encryption_key = Some(shared_secret);
569 device_encryption_key_changed = true;
570 }
571
572 let changing_password = self
574 .config()
575 .protected_store
576 .new_device_encryption_key_password
577 .is_some();
578
579 if device_encryption_key_changed || changing_password {
581 self.save_device_encryption_key(device_encryption_key.clone())
582 .await?;
583 }
584
585 let all_tables_db = match self
587 .table_store_driver
588 .open("__veilid_all_tables", 1, 1)
589 .await
590 {
591 Ok(db) => db,
592 Err(e) => {
593 veilid_log!(self error "failed to create all tables table: {}", e);
594 return Err(e.into());
595 }
596 };
597 match all_tables_db.get(0, ALL_TABLE_NAMES).await {
598 Ok(Some(v)) => match deserialize_json_bytes::<HashMap<String, String>>(&v) {
599 Ok(all_table_names) => {
600 let mut inner = self.inner.lock();
601 inner.all_table_names = all_table_names;
602 }
603 Err(e) => {
604 veilid_log!(self error "could not deserialize __veilid_all_tables: {}", e);
605 }
606 },
607 Ok(None) => {
608 veilid_log!(self trace "__veilid_all_tables is empty");
610 }
611 Err(e) => {
612 veilid_log!(self error "could not get __veilid_all_tables: {}", e);
613 }
614 };
615
616 {
617 let mut inner = self.inner.lock();
618 inner.encryption_key = device_encryption_key;
619 inner.all_tables_db = Some(all_tables_db);
620 }
621 }
622 startup_guard.success();
623
624 let do_delete = self.config().table_store.delete;
627 if do_delete {
628 veilid_log!(self debug "TableStore config 'delete' enabled: deleting all tables");
629 self.delete_all().await;
630 }
631
632 Ok(())
633 }
634
635 #[cfg_attr(
636 feature = "instrument",
637 instrument(level = "trace", target = "tstore", skip_all)
638 )]
639 #[allow(clippy::unused_async)]
640 async fn post_init_async(&self) -> EyreResult<()> {
641 let tick_subscription = impl_subscribe_event_bus_async!(self, Self, tick_event_handler);
643
644 let mut inner = self.inner.lock();
645
646 inner.tick_subscription = Some(tick_subscription);
648
649 Ok(())
650 }
651
652 #[cfg_attr(
653 feature = "instrument",
654 instrument(level = "trace", target = "tstore", skip_all)
655 )]
656 #[allow(clippy::unused_async)]
657 async fn pre_terminate_async(&self) {
658 let mut inner = self.inner.lock();
660 if let Some(sub) = inner.tick_subscription.take() {
661 self.event_bus().unsubscribe(sub);
662 }
663 }
664
665 #[cfg_attr(
666 feature = "instrument",
667 instrument(level = "trace", target = "tstore", skip_all)
668 )]
669 async fn terminate_async(&self) {
670 let Ok(_startup_guard) = self.startup_lock.shutdown().await else {
671 veilid_log!(self error "table store is already shut down");
672 return;
673 };
674
675 self.cancel_tasks().await;
677
678 self.flush().await;
679
680 let mut inner = self.inner.lock();
681 inner.opened.shrink_to_fit();
682 if !inner.opened.is_empty() {
683 veilid_log!(self warn
684 "all open databases should have been closed: {:?}",
685 inner.opened
686 );
687 inner.opened.clear();
688 }
689 inner.all_tables_db = None;
690 inner.all_table_names.clear();
691 inner.encryption_key = None;
692 }
693
694 #[cfg_attr(
701 feature = "instrument",
702 instrument(level = "trace", target = "tstore", skip_all)
703 )]
704 pub async fn open(&self, name: &str, column_count: u32) -> VeilidAPIResult<TableDB> {
705 self.open_pooled(name, column_count, 1).await
706 }
707
708 #[cfg_attr(
709 feature = "instrument",
710 instrument(level = "trace", target = "tstore", skip_all)
711 )]
712 pub async fn open_pooled(
722 &self,
723 name: &str,
724 column_count: u32,
725 concurrency: usize,
726 ) -> VeilidAPIResult<TableDB> {
727 if column_count == 0 {
728 apibail_invalid_argument!(
729 "column count must be greater than zero",
730 "column_count",
731 column_count
732 );
733 }
734
735 let Ok(_startup_guard) = self.startup_lock.enter() else {
736 apibail_not_initialized!();
737 };
738
739 let _async_guard = self.async_lock.lock().await;
740
741 {
743 let inner = self.inner.lock();
744 if inner.all_tables_db.is_none() {
745 apibail_not_initialized!();
746 }
747 }
748
749 let mut table_name = self.name_get_or_create(name)?;
750
751 {
753 let inner = self.inner.lock();
754 if let Some(table_db_unlocked_inner) = inner.opened.get(&table_name) {
755 let tdb = TableDB::new_from_unlocked_inner(table_db_unlocked_inner, column_count);
756
757 let existing_col_count = tdb.get_column_count()?;
759 if column_count > existing_col_count {
760 return Err(VeilidAPIError::generic(format!(
761 "database must be closed before increasing column count {} -> {}",
762 existing_col_count, column_count,
763 )));
764 }
765
766 return Ok(tdb);
767 }
768 }
769
770 let mut db = match self
772 .table_store_driver
773 .open(&table_name, column_count, concurrency)
774 .await
775 {
776 Ok(db) => db,
777 Err(e) => {
778 self.name_delete(name).expect_or_log("removing name failed");
779 self.flush().await;
780 return Err(e);
781 }
782 };
783
784 self.flush().await;
786
787 let existing_col_count = db.num_columns().map_err(VeilidAPIError::from)?;
789 if existing_col_count > column_count {
790 drop(db);
791 db = match self
792 .table_store_driver
793 .open(&table_name, existing_col_count, concurrency)
794 .await
795 {
796 Ok(db) => db,
797 Err(e) => {
798 self.name_delete(name).expect_or_log("removing name failed");
799 self.flush().await;
800 return Err(e);
801 }
802 };
803 }
804
805 let encryption_key = self.inner.lock().encryption_key.clone();
807 let table_db = TableDB::new(
808 table_name.clone(),
809 self.registry(),
810 db,
811 encryption_key.clone(),
812 encryption_key.clone(),
813 column_count,
814 );
815
816 let mut first_failure: Option<(u32, String)> = None;
819 for col in 0..existing_col_count {
820 match table_db.get_keys(col).await {
821 Ok(_keys) => {
822 #[cfg(feature = "verbose-tracing")]
823 veilid_log!(self debug "table {}({}) col {}: {} keys read ok", name, table_name, col, _keys.len());
824 }
825 Err(e) => {
826 veilid_log!(self warn "table {}({}) col {}: get_keys failed: {}", name, table_name, col, e);
827 if first_failure.is_none() {
828 first_failure = Some((col, e.to_string()));
829 }
830 }
831 }
832 }
833
834 let table_db = if let Some((failed_col, failure_msg)) = first_failure {
835 let namespace = self.config().namespace.clone();
836
837 if !self
840 .config()
841 .table_store
842 .wipe_on_invalid_device_encryption_key
843 {
844 veilid_log!(self error
845 "table {}({}) has invalid encryption key (col {} failed: {}); wipe_on_invalid_device_encryption_key=false, refusing to wipe namespace '{}'",
846 name, table_name, failed_col, failure_msg, namespace
847 );
848 return Err(VeilidAPIError::not_initialized());
849 }
850
851 veilid_log!(self warn
852 "table {}({}) has invalid encryption key (col {} failed: {}); wiping all tables in namespace '{}' and starting fresh",
853 name, table_name, failed_col, failure_msg, namespace
854 );
855
856 drop(table_db);
858
859 {
862 let mut inner = self.inner.lock();
863 inner.all_tables_db = None;
864 inner.all_table_names.clear();
865 inner.opened.clear();
866 }
867
868 let deleted = self.table_store_driver.delete_all_in_namespace().await?;
869 veilid_log!(self warn "wiped {} table file(s) from namespace '{}'", deleted, namespace);
870
871 let all_tables_db = self
873 .table_store_driver
874 .open("__veilid_all_tables", 1, 1)
875 .await?;
876 {
877 let mut inner = self.inner.lock();
878 inner.all_tables_db = Some(all_tables_db);
879 }
880
881 table_name = self.name_get_or_create(name)?;
884 self.flush().await;
885
886 let db = self
887 .table_store_driver
888 .open(&table_name, column_count, concurrency)
889 .await?;
890
891 TableDB::new(
892 table_name.clone(),
893 self.registry(),
894 db,
895 encryption_key.clone(),
896 encryption_key.clone(),
897 column_count,
898 )
899 } else {
900 table_db
901 };
902
903 let mut inner = self.inner.lock();
905 inner
906 .opened
907 .insert(table_name.clone(), table_db.unlocked_inner());
908
909 Ok(table_db)
910 }
911
912 #[cfg_attr(
921 feature = "instrument",
922 instrument(level = "trace", target = "tstore", skip_all)
923 )]
924 pub async fn delete(&self, name: &str) -> VeilidAPIResult<bool> {
925 let Ok(_startup_guard) = self.startup_lock.enter() else {
926 apibail_not_initialized!();
927 };
928
929 let _async_guard = self.async_lock.lock().await;
930 {
932 let inner = self.inner.lock();
933 if inner.all_tables_db.is_none() {
934 apibail_not_initialized!();
935 }
936 }
937
938 let Some(table_name) = self.name_get(name)? else {
939 return Ok(false);
941 };
942
943 {
945 let inner = self.inner.lock();
946 if inner.opened.contains_key(&table_name) {
947 apibail_generic!("Not deleting table that is still opened");
948 }
949 }
950
951 let deleted = self.table_store_driver.delete(&table_name).await?;
953 if !deleted {
954 veilid_log!(self warn
956 "table existed in name table but not in storage: {} : {}",
957 name, table_name
958 );
959 }
960 if let Err(e) = self.name_delete(name) {
961 veilid_log!(self error "failed to delete name: {}", e);
962 return Err(e);
963 }
964 self.flush().await;
965 Ok(true)
966 }
967 pub async fn info(&self, name: &str) -> VeilidAPIResult<Option<TableInfo>> {
973 let Ok(_startup_guard) = self.startup_lock.enter() else {
974 apibail_not_initialized!();
975 };
976
977 let mut tdb = self.open(name, 1).await?;
979 let column_count = tdb.get_column_count()?;
980 if column_count > 1 {
981 tdb = self.open(name, column_count).await?;
982 }
983
984 let internal_name = tdb.table_name();
985 let io_stats_since_previous = tdb.io_stats(IoStatsKind::SincePrevious);
986 let io_stats_overall = tdb.io_stats(IoStatsKind::Overall);
987 let mut columns = Vec::<ColumnInfo>::with_capacity(column_count as usize);
988 for col in 0..column_count {
989 let key_count = tdb.get_key_count(col).await?;
990 columns.push(ColumnInfo {
991 key_count: AlignedU64::new(key_count),
992 })
993 }
994 Ok(Some(TableInfo {
995 table_name: internal_name,
996 io_stats_since_previous: IOStatsInfo {
997 transactions: AlignedU64::new(io_stats_since_previous.transactions),
998 reads: AlignedU64::new(io_stats_since_previous.reads),
999 cache_reads: AlignedU64::new(io_stats_since_previous.cache_reads),
1000 writes: AlignedU64::new(io_stats_since_previous.writes),
1001 bytes_read: ByteCount::new(io_stats_since_previous.bytes_read),
1002 cache_read_bytes: ByteCount::new(io_stats_since_previous.cache_read_bytes),
1003 bytes_written: ByteCount::new(io_stats_since_previous.bytes_written),
1004 deletes: AlignedU64::new(io_stats_since_previous.deletes),
1005 prefix_deletes: AlignedU64::new(io_stats_since_previous.prefix_deletes),
1006 write_size_buckets: io_stats_since_previous
1007 .write_size_buckets
1008 .into_iter()
1009 .collect(),
1010 tx_write_size_buckets: io_stats_since_previous
1011 .tx_write_size_buckets
1012 .into_iter()
1013 .map(|(k, (count, avg_duration))| {
1014 (k, (count, TimestampDuration::new(avg_duration as u64)))
1015 })
1016 .collect(),
1017 started: Timestamp::new(io_stats_since_previous.started),
1018 span: TimestampDuration::new(io_stats_since_previous.span.as_micros() as u64),
1019 },
1020 io_stats_overall: IOStatsInfo {
1021 transactions: AlignedU64::new(io_stats_overall.transactions),
1022 reads: AlignedU64::new(io_stats_overall.reads),
1023 cache_reads: AlignedU64::new(io_stats_overall.cache_reads),
1024 writes: AlignedU64::new(io_stats_overall.writes),
1025 bytes_read: ByteCount::new(io_stats_overall.bytes_read),
1026 cache_read_bytes: ByteCount::new(io_stats_overall.cache_read_bytes),
1027 bytes_written: ByteCount::new(io_stats_overall.bytes_written),
1028 deletes: AlignedU64::new(io_stats_overall.deletes),
1029 prefix_deletes: AlignedU64::new(io_stats_overall.prefix_deletes),
1030 write_size_buckets: io_stats_overall.write_size_buckets.into_iter().collect(),
1031 tx_write_size_buckets: io_stats_overall
1032 .tx_write_size_buckets
1033 .into_iter()
1034 .map(|(k, (count, avg_duration))| {
1035 (k, (count, TimestampDuration::new(avg_duration as u64)))
1036 })
1037 .collect(),
1038 started: Timestamp::new(io_stats_overall.started),
1039 span: TimestampDuration::new(io_stats_overall.span.as_micros() as u64),
1040 },
1041 column_count,
1042 columns,
1043 }))
1044 }
1045
1046 #[cfg_attr(
1055 feature = "instrument",
1056 instrument(level = "trace", target = "tstore", skip_all)
1057 )]
1058 pub async fn rename(&self, old_name: &str, new_name: &str) -> VeilidAPIResult<()> {
1059 let Ok(_startup_guard) = self.startup_lock.enter() else {
1060 apibail_not_initialized!();
1061 };
1062
1063 let _async_guard = self.async_lock.lock().await;
1064 {
1066 let inner = self.inner.lock();
1067 if inner.all_tables_db.is_none() {
1068 apibail_not_initialized!();
1069 }
1070 }
1071 veilid_log!(self debug "TableStore::rename {} -> {}", old_name, new_name);
1072 self.name_rename(old_name, new_name)?;
1073 self.flush().await;
1074 Ok(())
1075 }
1076
1077 async fn tick_event_handler(&self, evt: Arc<TickEvent>) {
1078 let lag = evt.last_tick_ts.map(|x| evt.cur_tick_ts.duration_since(x));
1079 if let Err(e) = self.tick(lag).await {
1080 error!("Error in table store tick: {}", e);
1081 }
1082 }
1083}