1use std::{
5 borrow::Borrow,
6 marker::PhantomData,
7 ops::{Bound, Deref, RangeBounds},
8 path::{Path, PathBuf},
9 sync::{Arc, Weak},
10 time::Duration,
11};
12
13use fastcrypto::hash::{Digest, HashFunction};
14use iota_common::debug_fatal;
15use iota_macros::{fail_point, nondeterministic};
16use prometheus_filtered::{Histogram, HistogramTimer};
17use rocksdb::{
18 DBPinnableSlice, DBWithThreadMode, Error, LiveFile, MultiThreaded, ReadOptions,
19 SnapshotWithThreadMode, WriteBatch,
20};
21use serde::{Serialize, de::DeserializeOwned};
22use tokio::{sync::oneshot, time::Instant};
23use tracing::{debug, error, instrument, warn};
24use typed_store_error::TypedStoreError;
25
26use crate::{
27 DbIterator,
28 memstore::{InMemoryBatch, InMemoryDB},
29 metrics::{DBMetrics, RocksDBPerfContext, SamplingInterval},
30 rocks::{
31 RocksDB,
32 errors::{typed_store_err_from_bcs_err, typed_store_err_from_rocks_err},
33 options::ReadWriteOptions,
34 rocks_cf, rocks_util,
35 safe_iter::{SafeIter, SafeRevIter},
36 },
37 traits::{Map, TableSummary},
38 util::{
39 be_fix_int_ser, iterator_bounds_with_range, prefix_iterator_bounds,
40 prefix_iterator_bounds_with_range,
41 },
42};
43
44#[derive(Clone)]
45pub(crate) enum ColumnFamily {
46 Rocks(String),
47 #[allow(dead_code)]
48 InMemory(String),
49}
50
51impl std::fmt::Debug for ColumnFamily {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 match self {
54 ColumnFamily::Rocks(name) => write!(f, "RocksDB cf: {name}"),
55 ColumnFamily::InMemory(name) => write!(f, "InMemory cf: {name}"),
56 }
57 }
58}
59
60impl ColumnFamily {
61 pub(crate) fn name(&self) -> &str {
62 match self {
63 ColumnFamily::Rocks(name) => name,
64 ColumnFamily::InMemory(name) => name,
65 }
66 }
67}
68
69pub(crate) enum Storage {
70 Rocks(RocksDB),
71 #[allow(dead_code)]
72 InMemory(InMemoryDB),
73}
74
75pub struct DbSnapshot<'db> {
87 snapshot: SnapshotWithThreadMode<'db, DBWithThreadMode<MultiThreaded>>,
88}
89
90impl std::fmt::Debug for Storage {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 match self {
93 Storage::Rocks(db) => write!(f, "RocksDB Storage {db:?}"),
94 Storage::InMemory(db) => write!(f, "InMemoryDB Storage {db:?}"),
95 }
96 }
97}
98
99pub(crate) enum GetResult<'a> {
100 Rocks(DBPinnableSlice<'a>),
101 InMemory(Vec<u8>),
102}
103
104impl Deref for GetResult<'_> {
105 type Target = [u8];
106 fn deref(&self) -> &[u8] {
107 match self {
108 GetResult::Rocks(d) => d.deref(),
109 GetResult::InMemory(d) => d.deref(),
110 }
111 }
112}
113
114pub enum StorageWriteBatch {
115 Rocks(rocksdb::WriteBatch),
116 InMemory(InMemoryBatch),
117}
118
119#[derive(Debug, Default)]
120pub struct MetricConf {
121 pub db_name: String,
122 pub read_sample_interval: SamplingInterval,
123 pub write_sample_interval: SamplingInterval,
124 pub iter_sample_interval: SamplingInterval,
125}
126
127impl MetricConf {
128 pub fn new(db_name: &str) -> Self {
129 if db_name.is_empty() {
130 error!("A meaningful db name should be used for metrics reporting.")
131 }
132 Self {
133 db_name: db_name.to_string(),
134 read_sample_interval: SamplingInterval::default(),
135 write_sample_interval: SamplingInterval::default(),
136 iter_sample_interval: SamplingInterval::default(),
137 }
138 }
139
140 pub fn with_sampling(self, read_interval: SamplingInterval) -> Self {
141 Self {
142 db_name: self.db_name,
143 read_sample_interval: read_interval,
144 write_sample_interval: SamplingInterval::default(),
145 iter_sample_interval: SamplingInterval::default(),
146 }
147 }
148}
149
150const CF_METRICS_REPORT_PERIOD_SECS: u64 = 30;
151
152#[derive(Debug)]
153pub struct Database {
154 pub(crate) storage: Storage,
155 pub(crate) metric_conf: MetricConf,
156 closed: Arc<()>,
159}
160
161pub struct DatabaseClose(Weak<()>);
163
164impl Drop for Database {
165 fn drop(&mut self) {
166 DBMetrics::get().decrement_num_active_dbs(&self.metric_conf.db_name);
167 }
168}
169
170impl Database {
171 pub fn close_handle(&self) -> DatabaseClose {
174 DatabaseClose(Arc::downgrade(&self.closed))
175 }
176
177 pub(crate) fn new(storage: Storage, metric_conf: MetricConf) -> Self {
178 DBMetrics::get().increment_num_active_dbs(&metric_conf.db_name);
179 Self {
180 storage,
181 metric_conf,
182 closed: Arc::new(()),
183 }
184 }
185
186 pub(crate) fn get<K: AsRef<[u8]>>(
187 &self,
188 cf: &ColumnFamily,
189 key: K,
190 readopts: &ReadOptions,
191 ) -> Result<Option<GetResult<'_>>, TypedStoreError> {
192 match (&self.storage, cf) {
193 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => Ok(db
194 .underlying
195 .get_pinned_cf_opt(&rocks_cf(db, cf.name())?, key, readopts)
196 .map_err(typed_store_err_from_rocks_err)?
197 .map(GetResult::Rocks)),
198 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => {
199 Ok(db.get(cf_name, key)?.map(GetResult::InMemory))
200 }
201
202 _ => Err(TypedStoreError::RocksDB(
203 "typed store invariant violation".to_string(),
204 )),
205 }
206 }
207
208 pub(crate) fn multi_get<I, K>(
209 &self,
210 cf: &ColumnFamily,
211 keys: I,
212 readopts: &ReadOptions,
213 ) -> Vec<Result<Option<GetResult<'_>>, TypedStoreError>>
214 where
215 I: IntoIterator<Item = K>,
216 K: AsRef<[u8]>,
217 {
218 match (&self.storage, cf) {
219 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => {
220 let keys_vec: Vec<K> = keys.into_iter().collect();
221 let handle = match rocks_cf(db, cf.name()) {
222 Ok(handle) => handle,
223 Err(e) => return keys_vec.iter().map(|_| Err(e.clone())).collect(),
225 };
226 let res = db.underlying.batched_multi_get_cf_opt(
227 &handle,
228 keys_vec.iter(),
229 false,
231 readopts,
232 );
233 res.into_iter()
234 .map(|r| {
235 r.map_err(typed_store_err_from_rocks_err)
236 .map(|item| item.map(GetResult::Rocks))
237 })
238 .collect()
239 }
240 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => db
241 .multi_get(cf_name, keys)
242 .into_iter()
243 .map(|r| r.map(|value| value.map(GetResult::InMemory)))
244 .collect(),
245 _ => unreachable!("typed store invariant violation"),
246 }
247 }
248
249 pub fn cf_handle(&self, name: &str) -> Option<()> {
250 match &self.storage {
251 Storage::Rocks(db) => db.underlying.cf_handle(name).map(|_| ()),
252 Storage::InMemory(db) => db.has_cf(name).then_some(()),
253 }
254 }
255
256 #[instrument(level = "debug", skip(self, options), err)]
263 pub fn create_cf(&self, name: &str, options: &rocksdb::Options) -> Result<(), TypedStoreError> {
264 match &self.storage {
265 Storage::Rocks(db) => nondeterministic!(db.underlying.create_cf(name, options))
266 .map_err(typed_store_err_from_rocks_err),
267 Storage::InMemory(db) => db.create_cf(name),
268 }
269 }
270
271 #[instrument(level = "debug", skip(self), err)]
274 pub fn drop_cf(&self, name: &str) -> Result<(), TypedStoreError> {
275 if name == rocksdb::DEFAULT_COLUMN_FAMILY_NAME {
276 return Err(TypedStoreError::RocksDB(format!(
279 "the {name} column family cannot be dropped"
280 )));
281 }
282 match &self.storage {
283 Storage::Rocks(db) => {
284 let handle = db.underlying.cf_handle(name);
289 let dropped = nondeterministic!(db.underlying.drop_cf(name))
290 .map_err(typed_store_err_from_rocks_err);
291 drop(handle);
292 if dropped.is_ok() {
293 DBMetrics::get().remove_column_family(name);
294 }
295 dropped
296 }
297 Storage::InMemory(db) => {
298 db.drop_cf(name);
299 Ok(())
300 }
301 }
302 }
303
304 pub(crate) fn delete_cf<K: AsRef<[u8]>>(
305 &self,
306 cf: &ColumnFamily,
307 key: K,
308 ) -> Result<(), TypedStoreError> {
309 fail_point!("delete-cf-before");
310 let ret = match (&self.storage, cf) {
311 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => {
312 rocks_cf(db, cf.name()).and_then(|handle| {
313 db.underlying
314 .delete_cf(&handle, key)
315 .map_err(typed_store_err_from_rocks_err)
316 })
317 }
318 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => {
319 db.delete(cf_name, key.as_ref())
320 }
321 _ => Err(TypedStoreError::RocksDB(
322 "typed store invariant violation".to_string(),
323 )),
324 };
325 fail_point!("delete-cf-after");
326 #[allow(clippy::let_and_return)]
327 ret
328 }
329
330 pub fn path_for_pruning(&self) -> &Path {
331 match &self.storage {
332 Storage::Rocks(rocks) => rocks.underlying.path(),
333 _ => unimplemented!("method is only supported for rocksdb backend"),
334 }
335 }
336
337 pub(crate) fn put_cf(
338 &self,
339 cf: &ColumnFamily,
340 key: Vec<u8>,
341 value: Vec<u8>,
342 ) -> Result<(), TypedStoreError> {
343 fail_point!("put-cf-before");
344 let ret = match (&self.storage, cf) {
345 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => {
346 rocks_cf(db, cf.name()).and_then(|handle| {
347 db.underlying
348 .put_cf(&handle, key, value)
349 .map_err(typed_store_err_from_rocks_err)
350 })
351 }
352 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => db.put(cf_name, key, value),
353 _ => Err(TypedStoreError::RocksDB(
354 "typed store invariant violation".to_string(),
355 )),
356 };
357 fail_point!("put-cf-after");
358 #[allow(clippy::let_and_return)]
359 ret
360 }
361
362 pub(crate) fn key_may_exist_cf<K: AsRef<[u8]>>(
363 &self,
364 cf: &ColumnFamily,
365 key: K,
366 readopts: &ReadOptions,
367 ) -> bool {
368 match &self.storage {
369 Storage::Rocks(rocks) => match rocks_cf(rocks, cf.name()) {
372 Ok(handle) => rocks
373 .underlying
374 .key_may_exist_cf_opt(&handle, key, readopts),
375 Err(_) => true,
377 },
378 _ => true,
379 }
380 }
381
382 pub(crate) fn flush_cf(&self, cf: &ColumnFamily) -> Result<(), TypedStoreError> {
384 match &self.storage {
385 Storage::Rocks(rocks) => nondeterministic!({
390 let cf_name = cf.name();
391 if let Some(handle) = rocks.underlying.cf_handle(cf_name) {
394 rocks.underlying.flush_cf(&handle).map_err(|e| {
395 TypedStoreError::RocksDB(format!(
396 "Failed to flush column family {cf_name}: {e}"
397 ))
398 })?;
399 }
400 Ok(())
401 }),
402 Storage::InMemory(_) => Ok(()),
404 }
405 }
406
407 pub fn flush_all(&self) -> Result<(), TypedStoreError> {
410 match &self.storage {
411 Storage::Rocks(rocks) => nondeterministic!({
414 for cf_name in rocks.cf_names().map_err(|e| {
415 TypedStoreError::RocksDB(format!(
416 "Failed to list column families of {}: {e}",
417 rocks.underlying.path().display()
418 ))
419 })? {
420 if let Some(cf) = rocks.underlying.cf_handle(&cf_name) {
421 rocks.underlying.flush_cf(&cf).map_err(|e| {
422 TypedStoreError::RocksDB(format!(
423 "Failed to flush column family {cf_name}: {e}"
424 ))
425 })?;
426 }
427 }
428 Ok(())
429 }),
430 Storage::InMemory(_) => Ok(()),
432 }
433 }
434
435 pub fn write_opt(
436 &self,
437 batch: StorageWriteBatch,
438 write_options: &rocksdb::WriteOptions,
439 ) -> Result<(), TypedStoreError> {
440 fail_point!("batch-write-before");
441 let ret = match (&self.storage, batch) {
442 (Storage::Rocks(rocks), StorageWriteBatch::Rocks(batch)) => rocks
443 .underlying
444 .write_opt(batch, write_options)
445 .map_err(typed_store_err_from_rocks_err),
446 (Storage::InMemory(db), StorageWriteBatch::InMemory(batch)) => {
447 db.write(batch)
449 }
450 _ => Err(TypedStoreError::RocksDB(
451 "using invalid batch type for the database".to_string(),
452 )),
453 };
454 fail_point!("batch-write-after");
455
456 #[allow(clippy::let_and_return)]
457 ret
458 }
459
460 pub(crate) fn compact_range_cf<K: AsRef<[u8]>>(
461 &self,
462 cf: &ColumnFamily,
463 start: Option<K>,
464 end: Option<K>,
465 ) -> Result<(), TypedStoreError> {
466 if let Storage::Rocks(rocksdb) = &self.storage {
467 let handle = rocks_cf(rocksdb, cf.name())?;
468 rocksdb.underlying.compact_range_cf(&handle, start, end);
469 }
470 Ok(())
471 }
472
473 pub fn sorted_runs(&self, cf_name: &str) -> Result<u64, TypedStoreError> {
477 Ok(self
478 .files_per_level(cf_name)?
479 .iter()
480 .enumerate()
481 .map(|(level, &files)| {
482 if level == 0 {
483 files
484 } else {
485 u64::from(files > 0)
486 }
487 })
488 .sum())
489 }
490
491 pub fn sst_file_count(&self, cf_name: &str) -> Result<u64, TypedStoreError> {
494 Ok(self.files_per_level(cf_name)?.iter().sum())
495 }
496
497 fn files_per_level(&self, cf_name: &str) -> Result<Vec<u64>, TypedStoreError> {
499 let Storage::Rocks(rocks) = &self.storage else {
500 return Ok(Vec::new());
501 };
502 let Some(handle) = rocks.underlying.cf_handle(cf_name) else {
503 return Ok(Vec::new());
504 };
505 let mut levels = Vec::new();
506 for level in 0.. {
509 let Some(files) = rocks
510 .underlying
511 .property_value_cf(&handle, &format!("rocksdb.num-files-at-level{level}"))
512 .map_err(typed_store_err_from_rocks_err)?
513 else {
514 break;
515 };
516 levels.push(files.trim().parse().map_err(|e| {
517 TypedStoreError::RocksDB(format!(
518 "the file count of level {level} of {cf_name} is not a number: {e}"
519 ))
520 })?);
521 }
522 Ok(levels)
523 }
524
525 pub fn sst_files_size(&self, cf_name: &str) -> Result<u64, TypedStoreError> {
530 let Storage::Rocks(rocks) = &self.storage else {
531 return Ok(0);
532 };
533 let Some(handle) = rocks.underlying.cf_handle(cf_name) else {
534 return Ok(0);
535 };
536 Ok(rocks
537 .underlying
538 .property_int_value_cf(&handle, rocksdb::properties::TOTAL_SST_FILES_SIZE)
539 .map_err(typed_store_err_from_rocks_err)?
540 .unwrap_or(0))
541 }
542
543 pub fn compact_cf(&self, cf_name: &str) -> Result<(), TypedStoreError> {
547 if let Storage::Rocks(rocks) = &self.storage {
548 let handle = rocks_cf(rocks, cf_name)?;
549 let mut options = rocksdb::CompactOptions::default();
550 options.set_exclusive_manual_compaction(false);
551 options.set_bottommost_level_compaction(rocksdb::BottommostLevelCompaction::Force);
552 nondeterministic!(rocks.underlying.compact_range_cf_opt(
553 &handle,
554 None::<&[u8]>,
555 None::<&[u8]>,
556 &options
557 ));
558 }
559 Ok(())
560 }
561
562 pub fn snapshot(&self) -> DbSnapshot<'_> {
569 match &self.storage {
570 Storage::Rocks(rocks) => DbSnapshot {
571 snapshot: rocks.underlying.snapshot(),
572 },
573 Storage::InMemory(_) => unimplemented!("method is only supported for rocksdb backend"),
574 }
575 }
576
577 pub fn get_sampling_interval(&self) -> SamplingInterval {
578 self.metric_conf.read_sample_interval.new_from_self()
579 }
580
581 pub fn multiget_sampling_interval(&self) -> SamplingInterval {
582 self.metric_conf.read_sample_interval.new_from_self()
583 }
584
585 pub fn write_sampling_interval(&self) -> SamplingInterval {
586 self.metric_conf.write_sample_interval.new_from_self()
587 }
588
589 pub fn iter_sampling_interval(&self) -> SamplingInterval {
590 self.metric_conf.iter_sample_interval.new_from_self()
591 }
592
593 pub(crate) fn db_name(&self) -> String {
594 let name = &self.metric_conf.db_name;
595 if name.is_empty() {
596 "default".to_string()
597 } else {
598 name.clone()
599 }
600 }
601
602 pub fn live_files(&self) -> Result<Vec<LiveFile>, Error> {
603 match &self.storage {
604 Storage::Rocks(rocks) => rocks.underlying.live_files(),
605 _ => Ok(vec![]),
606 }
607 }
608
609 pub(crate) fn try_catch_up_with_primary(&self) -> Result<(), TypedStoreError> {
610 if let Storage::Rocks(rocks) = &self.storage {
611 rocks
612 .underlying
613 .try_catch_up_with_primary()
614 .map_err(typed_store_err_from_rocks_err)?;
615 }
616 Ok(())
617 }
618}
619
620fn rocks_cf_from_db<'a>(
621 db: &'a Database,
622 cf_name: &str,
623) -> Result<Arc<rocksdb::BoundColumnFamily<'a>>, TypedStoreError> {
624 match &db.storage {
625 Storage::Rocks(rocksdb) => rocks_cf(rocksdb, cf_name),
626 _ => Err(TypedStoreError::RocksDB(
627 "using invalid batch type for the database".to_string(),
628 )),
629 }
630}
631
632#[derive(Clone, Debug)]
634pub struct DBMap<K, V> {
635 pub db: Arc<Database>,
636 _phantom: PhantomData<fn(K) -> V>,
637 column_family: ColumnFamily,
638 pub opts: ReadWriteOptions,
639 db_metrics: Arc<DBMetrics>,
640 get_sample_interval: SamplingInterval,
641 multiget_sample_interval: SamplingInterval,
642 write_sample_interval: SamplingInterval,
643 iter_sample_interval: SamplingInterval,
644 _metrics_task_cancel_handle: Arc<oneshot::Sender<()>>,
645 metrics_label: Arc<str>,
648}
649
650impl<K, V> DBMap<K, V> {
651 pub(crate) fn new(
652 db: Arc<Database>,
653 opts: &ReadWriteOptions,
654 column_family: ColumnFamily,
655 skip_metrics_reporting: bool,
656 ) -> Self {
657 let db_cloned = Arc::downgrade(&db);
658 let db_metrics = DBMetrics::get();
659 let db_metrics_cloned = db_metrics.clone();
660 let cf = column_family.name().to_string();
661
662 let (sender, mut recv) = tokio::sync::oneshot::channel();
663 if !skip_metrics_reporting && matches!(db.storage, Storage::Rocks(_)) {
664 tokio::task::spawn(async move {
665 let mut interval =
666 tokio::time::interval(Duration::from_secs(CF_METRICS_REPORT_PERIOD_SECS));
667 loop {
668 tokio::select! {
669 _ = interval.tick() => {
670 if let Some(db) = db_cloned.upgrade() {
671 let cf = cf.clone();
672 let db_metrics = db_metrics.clone();
673 if let Err(e) = tokio::task::spawn_blocking(move || {
674 Self::report_rocksdb_metrics(&db, &cf, &db_metrics);
675 }).await {
676 error!("Failed to log metrics with error: {}", e);
677 }
678 } else {
679 break;
680 }
681 }
682 _ = &mut recv => break,
683 }
684 }
685 debug!("Returning the cf metric logging task for DBMap: {}", &cf);
686 });
687 }
688 let metrics_label = Arc::from(column_family.name());
689 DBMap {
690 db: db.clone(),
691 opts: opts.clone(),
692 _phantom: PhantomData,
693 column_family,
694 metrics_label,
695 db_metrics: db_metrics_cloned,
696 _metrics_task_cancel_handle: Arc::new(sender),
697 get_sample_interval: db.get_sampling_interval(),
698 multiget_sample_interval: db.multiget_sampling_interval(),
699 write_sample_interval: db.write_sampling_interval(),
700 iter_sample_interval: db.iter_sampling_interval(),
701 }
702 }
703
704 #[instrument(level = "debug", skip(db), err)]
712 pub fn reopen(
713 db: &Arc<Database>,
714 opt_cf: Option<&str>,
715 rw_options: &ReadWriteOptions,
716 skip_metrics_reporting: bool,
717 ) -> Result<Self, TypedStoreError> {
718 let cf_key = opt_cf
719 .unwrap_or(rocksdb::DEFAULT_COLUMN_FAMILY_NAME)
720 .to_owned();
721 if db.cf_handle(&cf_key).is_none() {
722 return Err(TypedStoreError::UnregisteredColumn(cf_key));
723 }
724
725 let column_family = match &db.storage {
726 Storage::Rocks(_) => ColumnFamily::Rocks(cf_key),
727 Storage::InMemory(_) => ColumnFamily::InMemory(cf_key),
728 };
729 Ok(DBMap::new(
730 db.clone(),
731 rw_options,
732 column_family,
733 skip_metrics_reporting,
734 ))
735 }
736
737 pub fn with_metrics_label(mut self, label: &str) -> Self {
741 self.metrics_label = Arc::from(label);
742 self
743 }
744
745 pub fn metrics_label(&self) -> &str {
747 &self.metrics_label
748 }
749
750 pub fn cf_name(&self) -> &str {
751 self.column_family.name()
752 }
753
754 pub fn batch(&self) -> DBBatch {
755 let batch = match &self.db.storage {
756 Storage::Rocks(_) => StorageWriteBatch::Rocks(WriteBatch::default()),
757 Storage::InMemory(_) => StorageWriteBatch::InMemory(InMemoryBatch::default()),
758 };
759 DBBatch::new(
760 &self.db,
761 batch,
762 &self.db_metrics,
763 &self.write_sample_interval,
764 )
765 }
766
767 pub fn ingest_sst_files(&self, paths: &[PathBuf]) -> Result<(), TypedStoreError> {
777 if paths.is_empty() {
778 return Ok(());
779 }
780 match &self.db.storage {
781 Storage::Rocks(rocks) => nondeterministic!({
784 let handle = rocks_cf(rocks, self.cf_name())?;
785 let mut options = rocksdb::IngestExternalFileOptions::default();
786 options.set_move_files(true);
787 rocks
788 .underlying
789 .ingest_external_file_cf_opts(&handle, &options, paths.to_vec())
790 .map_err(typed_store_err_from_rocks_err)
791 }),
792 Storage::InMemory(_) => unimplemented!("method is only supported for rocksdb backend"),
793 }
794 }
795
796 pub fn flush(&self) -> Result<(), TypedStoreError> {
798 self.db.flush_cf(&self.column_family)
799 }
800
801 pub fn flush_all(&self) -> Result<(), TypedStoreError> {
804 self.db.flush_all()
805 }
806
807 pub fn compact_range<J: Serialize>(&self, start: &J, end: &J) -> Result<(), TypedStoreError> {
808 let from_buf = be_fix_int_ser(start);
809 let to_buf = be_fix_int_ser(end);
810 self.db
811 .compact_range_cf(&self.column_family, Some(from_buf), Some(to_buf))
812 }
813
814 pub fn compact_range_raw(
815 &self,
816 cf_name: &str,
817 start: Vec<u8>,
818 end: Vec<u8>,
819 ) -> Result<(), TypedStoreError> {
820 let cf = match &self.db.storage {
821 Storage::Rocks(_) => ColumnFamily::Rocks(cf_name.to_string()),
822 Storage::InMemory(_) => ColumnFamily::InMemory(cf_name.to_string()),
823 };
824 self.db.compact_range_cf(&cf, Some(start), Some(end))
825 }
826
827 fn multi_get_pinned(
829 &self,
830 keys_bytes: impl IntoIterator<Item = Vec<u8>>,
831 ) -> Result<Vec<Option<GetResult<'_>>>, TypedStoreError> {
832 let _timer = self
833 .db_metrics
834 .op_metrics
835 .rocksdb_multiget_latency_seconds
836 .with_label_values(&[self.metrics_label()])
837 .start_timer();
838 let perf_ctx = if self.multiget_sample_interval.sample() {
839 Some(RocksDBPerfContext)
840 } else {
841 None
842 };
843 let results: Result<Vec<_>, TypedStoreError> = self
844 .db
845 .multi_get(&self.column_family, keys_bytes, &self.opts.readopts())
846 .into_iter()
847 .collect();
848 let entries = results?;
849 let entry_size = entries
850 .iter()
851 .flatten()
852 .map(|entry| entry.len())
853 .sum::<usize>();
854 self.db_metrics
855 .op_metrics
856 .rocksdb_multiget_bytes
857 .with_label_values(&[self.metrics_label()])
858 .observe(entry_size as f64);
859 if perf_ctx.is_some() {
860 self.db_metrics
861 .read_perf_ctx_metrics
862 .report_metrics(self.metrics_label());
863 }
864 Ok(entries)
865 }
866
867 pub fn table_summary(&self) -> eyre::Result<TableSummary>
868 where
869 K: Serialize + DeserializeOwned,
870 V: Serialize + DeserializeOwned,
871 {
872 let mut num_keys = 0;
873 let mut key_bytes_total = 0;
874 let mut value_bytes_total = 0;
875 let mut key_hist = hdrhistogram::Histogram::<u64>::new_with_max(100000, 2).unwrap();
876 let mut value_hist = hdrhistogram::Histogram::<u64>::new_with_max(100000, 2).unwrap();
877 for item in self.safe_iter() {
878 let (key, value) = item?;
879 num_keys += 1;
880 let key_len = be_fix_int_ser(key.borrow()).len();
881 let value_len = bcs::to_bytes(value.borrow())?.len();
882 key_bytes_total += key_len;
883 value_bytes_total += value_len;
884 key_hist.record(key_len as u64)?;
885 value_hist.record(value_len as u64)?;
886 }
887 Ok(TableSummary {
888 num_keys,
889 key_bytes_total,
890 value_bytes_total,
891 key_hist,
892 value_hist,
893 })
894 }
895
896 fn create_iter_context(
898 &self,
899 ) -> (
900 Option<HistogramTimer>,
901 Option<Histogram>,
902 Option<Histogram>,
903 Option<RocksDBPerfContext>,
904 ) {
905 let timer = self
906 .db_metrics
907 .op_metrics
908 .rocksdb_iter_latency_seconds
909 .with_label_values(&[self.metrics_label()])
910 .start_timer();
911 let bytes_scanned = self
912 .db_metrics
913 .op_metrics
914 .rocksdb_iter_bytes
915 .with_label_values(&[self.metrics_label()]);
916 let keys_scanned = self
917 .db_metrics
918 .op_metrics
919 .rocksdb_iter_keys
920 .with_label_values(&[self.metrics_label()]);
921 let perf_ctx = if self.iter_sample_interval.sample() {
922 Some(RocksDBPerfContext)
923 } else {
924 None
925 };
926 (
927 Some(timer),
928 Some(bytes_scanned),
929 Some(keys_scanned),
930 perf_ctx,
931 )
932 }
933
934 fn rocks_safe_iter<'a>(
937 &self,
938 db: &'a RocksDB,
939 readopts: ReadOptions,
940 ) -> Result<SafeIter<'a, K, V>, TypedStoreError>
941 where
942 K: DeserializeOwned,
943 V: DeserializeOwned,
944 {
945 let db_iter = db
946 .underlying
947 .raw_iterator_cf_opt(&rocks_cf(db, self.column_family.name())?, readopts);
948 let (_timer, bytes_scanned, keys_scanned, _perf_ctx) = self.create_iter_context();
949 Ok(SafeIter::new(
950 self.metrics_label().to_string(),
951 db_iter,
952 _timer,
953 _perf_ctx,
954 bytes_scanned,
955 keys_scanned,
956 Some(self.db_metrics.clone()),
957 ))
958 }
959
960 pub fn safe_iter_at_snapshot<'a>(
965 &'a self,
966 db_snapshot: &'a DbSnapshot<'a>,
967 ) -> DbIterator<'a, (K, V)>
968 where
969 K: DeserializeOwned,
970 V: DeserializeOwned,
971 {
972 match &self.db.storage {
973 Storage::Rocks(db) => {
974 let mut readopts = self.opts.readopts();
975 readopts.fill_cache(false);
976 readopts.set_snapshot(&db_snapshot.snapshot);
977 match self.rocks_safe_iter(db, readopts) {
978 Ok(iter) => Box::new(iter),
979 Err(e) => error_iterator(e),
980 }
981 }
982 Storage::InMemory(_) => {
983 unreachable!("a DbSnapshot is only created over the rocksdb backend")
984 }
985 }
986 }
987
988 fn iter_forward_raw(
992 &self,
993 lower_bound: Option<Vec<u8>>,
994 upper_bound: Option<Vec<u8>>,
995 ) -> DbIterator<'_, (K, V)>
996 where
997 K: DeserializeOwned,
998 V: DeserializeOwned,
999 {
1000 match &self.db.storage {
1001 Storage::Rocks(db) => {
1002 let readopts =
1003 rocks_util::apply_range_bounds(self.opts.readopts(), lower_bound, upper_bound);
1004 match self.rocks_safe_iter(db, readopts) {
1005 Ok(iter) => Box::new(iter),
1006 Err(e) => error_iterator(e),
1007 }
1008 }
1009 Storage::InMemory(db) => {
1010 db.iterator(self.column_family.name(), lower_bound, upper_bound, false)
1011 }
1012 }
1013 }
1014
1015 fn iter_reversed_raw(
1022 &self,
1023 lower_bound: Option<Vec<u8>>,
1024 upper_bound: Option<Vec<u8>>,
1025 ) -> DbIterator<'_, (K, V)>
1026 where
1027 K: DeserializeOwned,
1028 V: DeserializeOwned,
1029 {
1030 match &self.db.storage {
1031 Storage::Rocks(db) => {
1032 let readopts =
1033 rocks_util::apply_range_bounds(self.opts.readopts(), lower_bound, None);
1034 match self.rocks_safe_iter(db, readopts) {
1035 Ok(iter) => Box::new(SafeRevIter::new(iter, upper_bound)),
1036 Err(e) => error_iterator(e),
1037 }
1038 }
1039 Storage::InMemory(db) => {
1040 db.iterator(self.column_family.name(), lower_bound, upper_bound, true)
1041 }
1042 }
1043 }
1044
1045 pub fn safe_iter_with_prefix<P>(&self, prefix: &P) -> DbIterator<'_, (K, V)>
1052 where
1053 P: ?Sized + Serialize,
1054 K: DeserializeOwned,
1055 V: DeserializeOwned,
1056 {
1057 let (lower_bound, upper_bound) = prefix_iterator_bounds(prefix);
1058 self.iter_forward_raw(lower_bound, upper_bound)
1059 }
1060
1061 pub fn safe_iter_with_prefix_from<P, C>(
1077 &self,
1078 prefix: &P,
1079 lower: Bound<&C>,
1080 ) -> DbIterator<'_, (K, V)>
1081 where
1082 P: ?Sized + Serialize,
1083 C: ?Sized + Serialize,
1084 K: DeserializeOwned,
1085 V: DeserializeOwned,
1086 {
1087 let (lower_bound, upper_bound) =
1088 prefix_iterator_bounds_with_range::<P, C>(prefix, (lower, Bound::Unbounded));
1089 self.iter_forward_raw(lower_bound, upper_bound)
1090 }
1091
1092 pub fn safe_iter_with_prefix_after<P, C>(
1097 &self,
1098 prefix: &P,
1099 cursor: Option<&C>,
1100 ) -> DbIterator<'_, (K, V)>
1101 where
1102 P: ?Sized + Serialize,
1103 C: ?Sized + Serialize,
1104 K: DeserializeOwned,
1105 V: DeserializeOwned,
1106 {
1107 self.safe_iter_with_prefix_from(prefix, cursor.map_or(Bound::Unbounded, Bound::Excluded))
1108 }
1109
1110 pub fn safe_iter_with_prefix_reversed<P>(&self, prefix: &P) -> DbIterator<'_, (K, V)>
1114 where
1115 P: ?Sized + Serialize,
1116 K: DeserializeOwned,
1117 V: DeserializeOwned,
1118 {
1119 let (lower_bound, upper_bound) = prefix_iterator_bounds(prefix);
1120 self.iter_reversed_raw(lower_bound, upper_bound)
1121 }
1122}
1123
1124pub struct DBBatch {
1194 database: Arc<Database>,
1195 batch: StorageWriteBatch,
1196 db_metrics: Arc<DBMetrics>,
1197 write_sample_interval: SamplingInterval,
1198}
1199
1200impl DBBatch {
1201 pub fn new(
1205 dbref: &Arc<Database>,
1206 batch: StorageWriteBatch,
1207 db_metrics: &Arc<DBMetrics>,
1208 write_sample_interval: &SamplingInterval,
1209 ) -> Self {
1210 DBBatch {
1211 database: dbref.clone(),
1212 batch,
1213 db_metrics: db_metrics.clone(),
1214 write_sample_interval: write_sample_interval.clone(),
1215 }
1216 }
1217
1218 #[instrument(level = "trace", skip_all, err)]
1220 pub fn write(self) -> Result<(), TypedStoreError> {
1221 self.write_opt(&rocksdb::WriteOptions::default())
1222 }
1223
1224 #[instrument(level = "trace", skip_all, err)]
1232 pub fn write_opt(self, write_options: &rocksdb::WriteOptions) -> Result<(), TypedStoreError> {
1233 let db_name = self.database.db_name();
1234 let timer = self
1235 .db_metrics
1236 .op_metrics
1237 .rocksdb_batch_commit_latency_seconds
1238 .with_label_values(&[&db_name])
1239 .start_timer();
1240 let batch_size_bytes = self.size_in_bytes();
1241
1242 let perf_ctx = if self.write_sample_interval.sample() {
1243 Some(RocksDBPerfContext)
1244 } else {
1245 None
1246 };
1247 self.database.write_opt(self.batch, write_options)?;
1248 self.db_metrics
1249 .op_metrics
1250 .rocksdb_batch_commit_bytes
1251 .with_label_values(&[&db_name])
1252 .observe(batch_size_bytes as f64);
1253
1254 if perf_ctx.is_some() {
1255 self.db_metrics
1256 .write_perf_ctx_metrics
1257 .report_metrics(&db_name);
1258 }
1259 let elapsed_secs = timer.stop_and_record();
1260 let threshold_secs = very_slow_batch_write_threshold_secs(batch_size_bytes);
1261 if elapsed_secs > threshold_secs {
1262 warn!(
1263 elapsed_secs,
1264 threshold_secs,
1265 batch_size_bytes,
1266 ?db_name,
1267 "very slow batch write"
1268 );
1269 self.db_metrics
1270 .op_metrics
1271 .rocksdb_very_slow_batch_writes_count
1272 .with_label_values(&[&db_name])
1273 .inc();
1274 self.db_metrics
1275 .op_metrics
1276 .rocksdb_very_slow_batch_writes_duration_ms
1277 .with_label_values(&[&db_name])
1278 .inc_by((elapsed_secs * 1000.0) as u64);
1279 }
1280 Ok(())
1281 }
1282
1283 pub fn size_in_bytes(&self) -> usize {
1284 match self.batch {
1285 StorageWriteBatch::Rocks(ref b) => b.size_in_bytes(),
1286 StorageWriteBatch::InMemory(_) => 0,
1287 }
1288 }
1289
1290 pub fn delete_batch<J: Borrow<K>, K: Serialize, V>(
1291 &mut self,
1292 db: &DBMap<K, V>,
1293 purged_vals: impl IntoIterator<Item = J>,
1294 ) -> Result<(), TypedStoreError> {
1295 self.delete_batch_raw_keys(
1296 db,
1297 purged_vals
1298 .into_iter()
1299 .map(|key| be_fix_int_ser(key.borrow())),
1300 )
1301 }
1302
1303 fn delete_batch_raw_keys<K, V>(
1306 &mut self,
1307 db: &DBMap<K, V>,
1308 purged_vals: impl IntoIterator<Item = Vec<u8>>,
1309 ) -> Result<(), TypedStoreError> {
1310 if !Arc::ptr_eq(&db.db, &self.database) {
1311 return Err(TypedStoreError::CrossDBBatch);
1312 }
1313
1314 purged_vals
1315 .into_iter()
1316 .try_for_each::<_, Result<_, TypedStoreError>>(|k_buf| {
1317 match (&mut self.batch, &db.column_family) {
1318 (StorageWriteBatch::Rocks(b), ColumnFamily::Rocks(name)) => {
1319 b.delete_cf(&rocks_cf_from_db(&self.database, name)?, k_buf)
1320 }
1321 (StorageWriteBatch::InMemory(b), ColumnFamily::InMemory(name)) => {
1322 b.delete_cf(name, k_buf)
1323 }
1324 _ => Err(TypedStoreError::RocksDB(
1325 "typed store invariant violation".to_string(),
1326 ))?,
1327 }
1328 Ok(())
1329 })?;
1330 Ok(())
1331 }
1332
1333 pub fn schedule_delete_range<K: Serialize, V>(
1338 &mut self,
1339 db: &DBMap<K, V>,
1340 from: &K,
1341 to: &K,
1342 ) -> Result<(), TypedStoreError> {
1343 self.schedule_delete_range_raw(db, be_fix_int_ser(from), be_fix_int_ser(to))
1344 }
1345
1346 fn schedule_delete_range_raw<K, V>(
1351 &mut self,
1352 db: &DBMap<K, V>,
1353 from: Vec<u8>,
1354 to: Vec<u8>,
1355 ) -> Result<(), TypedStoreError> {
1356 if !Arc::ptr_eq(&db.db, &self.database) {
1357 return Err(TypedStoreError::CrossDBBatch);
1358 }
1359
1360 if let StorageWriteBatch::Rocks(b) = &mut self.batch {
1361 b.delete_range_cf(&rocks_cf_from_db(&self.database, db.cf_name())?, from, to);
1362 }
1363 Ok(())
1364 }
1365
1366 pub fn insert_batch<J: Borrow<K>, K: Serialize, U: Borrow<V>, V: Serialize>(
1368 &mut self,
1369 db: &DBMap<K, V>,
1370 new_vals: impl IntoIterator<Item = (J, U)>,
1371 ) -> Result<&mut Self, TypedStoreError> {
1372 self.insert_batch_raw_keys(
1373 db,
1374 new_vals
1375 .into_iter()
1376 .map(|(key, value)| (be_fix_int_ser(key.borrow()), value)),
1377 )
1378 }
1379
1380 fn insert_batch_raw_keys<K, U: Borrow<V>, V: Serialize>(
1383 &mut self,
1384 db: &DBMap<K, V>,
1385 new_vals: impl IntoIterator<Item = (Vec<u8>, U)>,
1386 ) -> Result<&mut Self, TypedStoreError> {
1387 if !Arc::ptr_eq(&db.db, &self.database) {
1388 return Err(TypedStoreError::CrossDBBatch);
1389 }
1390 let mut total = 0usize;
1391 new_vals
1392 .into_iter()
1393 .try_for_each::<_, Result<_, TypedStoreError>>(|(k_buf, v)| {
1394 let v_buf = bcs::to_bytes(v.borrow()).map_err(typed_store_err_from_bcs_err)?;
1395 total += k_buf.len() + v_buf.len();
1396 if db.opts.log_value_hash {
1397 let key_hash = default_hash(&k_buf);
1398 let value_hash = default_hash(&v_buf);
1399 debug!(
1400 "Insert to DB table: {:?}, key_hash: {:?}, value_hash: {:?}",
1401 db.cf_name(),
1402 key_hash,
1403 value_hash
1404 );
1405 }
1406 match (&mut self.batch, &db.column_family) {
1407 (StorageWriteBatch::Rocks(b), ColumnFamily::Rocks(name)) => {
1408 b.put_cf(&rocks_cf_from_db(&self.database, name)?, k_buf, v_buf)
1409 }
1410 (StorageWriteBatch::InMemory(b), ColumnFamily::InMemory(name)) => {
1411 b.put_cf(name, k_buf, v_buf)
1412 }
1413 _ => Err(TypedStoreError::RocksDB(
1414 "typed store invariant violation".to_string(),
1415 ))?,
1416 }
1417 Ok(())
1418 })?;
1419 self.db_metrics
1420 .op_metrics
1421 .rocksdb_batch_put_bytes
1422 .with_label_values(&[db.metrics_label()])
1423 .observe(total as f64);
1424 Ok(self)
1425 }
1426
1427 pub fn insert_batch_tagged<J: Borrow<K>, K: Serialize, U: Borrow<V>, V: Serialize>(
1429 &mut self,
1430 map: &TaggedDBMap<K, V>,
1431 new_vals: impl IntoIterator<Item = (J, U)>,
1432 ) -> Result<&mut Self, TypedStoreError> {
1433 self.insert_batch_raw_keys(
1434 &map.map,
1435 new_vals
1436 .into_iter()
1437 .map(|(key, value)| (be_fix_int_ser(&(map.tag, key.borrow())), value)),
1438 )
1439 }
1440
1441 pub fn delete_batch_tagged<J: Borrow<K>, K: Serialize, V>(
1443 &mut self,
1444 map: &TaggedDBMap<K, V>,
1445 purged_vals: impl IntoIterator<Item = J>,
1446 ) -> Result<(), TypedStoreError> {
1447 self.delete_batch_raw_keys(
1448 &map.map,
1449 purged_vals
1450 .into_iter()
1451 .map(|key| be_fix_int_ser(&(map.tag, key.borrow()))),
1452 )
1453 }
1454}
1455
1456impl<K, V> DBMap<K, V> {
1457 #[instrument(level = "trace", skip_all, err)]
1458 pub(crate) fn contains_raw_key(&self, key_buf: Vec<u8>) -> Result<bool, TypedStoreError> {
1459 let readopts = self.opts.readopts();
1460 Ok(self
1461 .db
1462 .key_may_exist_cf(&self.column_family, &key_buf, &readopts)
1463 && self
1464 .db
1465 .get(&self.column_family, &key_buf, &readopts)?
1466 .is_some())
1467 }
1468
1469 #[instrument(level = "trace", skip_all, err)]
1470 pub(crate) fn multi_contains_raw_keys(
1471 &self,
1472 keys: impl IntoIterator<Item = Vec<u8>>,
1473 ) -> Result<Vec<bool>, TypedStoreError> {
1474 let values = self.multi_get_pinned(keys)?;
1475 Ok(values.into_iter().map(|v| v.is_some()).collect())
1476 }
1477
1478 #[instrument(level = "trace", skip_all, err)]
1479 pub(crate) fn get_raw_key(&self, key_buf: Vec<u8>) -> Result<Option<V>, TypedStoreError>
1480 where
1481 V: DeserializeOwned,
1482 {
1483 let _timer = self
1484 .db_metrics
1485 .op_metrics
1486 .rocksdb_get_latency_seconds
1487 .with_label_values(&[self.metrics_label()])
1488 .start_timer();
1489 let perf_ctx = if self.get_sample_interval.sample() {
1490 Some(RocksDBPerfContext)
1491 } else {
1492 None
1493 };
1494 let res = self
1495 .db
1496 .get(&self.column_family, &key_buf, &self.opts.readopts())?;
1497 self.db_metrics
1498 .op_metrics
1499 .rocksdb_get_bytes
1500 .with_label_values(&[self.metrics_label()])
1501 .observe(res.as_ref().map_or(0.0, |v| v.len() as f64));
1502 if perf_ctx.is_some() {
1503 self.db_metrics
1504 .read_perf_ctx_metrics
1505 .report_metrics(self.metrics_label());
1506 }
1507 match res {
1508 Some(data) => {
1509 let value = bcs::from_bytes(&data).map_err(typed_store_err_from_bcs_err);
1510 if value.is_err() {
1511 let key_hash = default_hash(&key_buf);
1512 let value_hash = default_hash(&data);
1513 debug_fatal!(
1514 "Failed to deserialize value from DB table {:?}, key_hash: {:?}, value_hash: {:?}, error: {:?}",
1515 self.cf_name(),
1516 key_hash,
1517 value_hash,
1518 value.as_ref().err().unwrap()
1519 );
1520 }
1521 Ok(Some(value?))
1522 }
1523 None => Ok(None),
1524 }
1525 }
1526
1527 #[instrument(level = "trace", skip_all, err)]
1528 pub(crate) fn insert_raw_key(&self, key_buf: Vec<u8>, value: &V) -> Result<(), TypedStoreError>
1529 where
1530 V: Serialize,
1531 {
1532 let timer = self
1533 .db_metrics
1534 .op_metrics
1535 .rocksdb_put_latency_seconds
1536 .with_label_values(&[self.metrics_label()])
1537 .start_timer();
1538 let perf_ctx = if self.write_sample_interval.sample() {
1539 Some(RocksDBPerfContext)
1540 } else {
1541 None
1542 };
1543 let value_buf = bcs::to_bytes(value).map_err(typed_store_err_from_bcs_err)?;
1544 self.db_metrics
1545 .op_metrics
1546 .rocksdb_put_bytes
1547 .with_label_values(&[self.metrics_label()])
1548 .observe((key_buf.len() + value_buf.len()) as f64);
1549 if perf_ctx.is_some() {
1550 self.db_metrics
1551 .write_perf_ctx_metrics
1552 .report_metrics(self.metrics_label());
1553 }
1554 self.db.put_cf(&self.column_family, key_buf, value_buf)?;
1555
1556 let elapsed_secs = timer.stop_and_record();
1557 if elapsed_secs > 1.0 {
1558 warn!(elapsed_secs, cf = ?self.cf_name(), "very slow insert");
1559 self.db_metrics
1560 .op_metrics
1561 .rocksdb_very_slow_puts_count
1562 .with_label_values(&[self.metrics_label()])
1563 .inc();
1564 self.db_metrics
1565 .op_metrics
1566 .rocksdb_very_slow_puts_duration_ms
1567 .with_label_values(&[self.metrics_label()])
1568 .inc_by((elapsed_secs * 1000.0) as u64);
1569 }
1570
1571 Ok(())
1572 }
1573
1574 #[instrument(level = "trace", skip_all, err)]
1575 pub(crate) fn remove_raw_key(&self, key_buf: Vec<u8>) -> Result<(), TypedStoreError> {
1576 let _timer = self
1577 .db_metrics
1578 .op_metrics
1579 .rocksdb_delete_latency_seconds
1580 .with_label_values(&[self.metrics_label()])
1581 .start_timer();
1582 let perf_ctx = if self.write_sample_interval.sample() {
1583 Some(RocksDBPerfContext)
1584 } else {
1585 None
1586 };
1587 self.db.delete_cf(&self.column_family, key_buf)?;
1588 self.db_metrics
1589 .op_metrics
1590 .rocksdb_deletes
1591 .with_label_values(&[self.metrics_label()])
1592 .inc();
1593 if perf_ctx.is_some() {
1594 self.db_metrics
1595 .write_perf_ctx_metrics
1596 .report_metrics(self.metrics_label());
1597 }
1598 Ok(())
1599 }
1600
1601 #[instrument(level = "trace", skip_all, err)]
1602 pub(crate) fn multi_get_raw_keys(
1603 &self,
1604 keys: impl IntoIterator<Item = Vec<u8>>,
1605 ) -> Result<Vec<Option<V>>, TypedStoreError>
1606 where
1607 V: DeserializeOwned,
1608 {
1609 let results = self.multi_get_pinned(keys)?;
1610 let values_parsed: Result<Vec<_>, TypedStoreError> = results
1611 .into_iter()
1612 .map(|value_byte| match value_byte {
1613 Some(data) => Ok(Some(
1614 bcs::from_bytes(&data).map_err(typed_store_err_from_bcs_err)?,
1615 )),
1616 None => Ok(None),
1617 })
1618 .collect();
1619
1620 values_parsed
1621 }
1622}
1623
1624impl<'a, K, V> Map<'a, K, V> for DBMap<K, V>
1625where
1626 K: Serialize + DeserializeOwned,
1627 V: Serialize + DeserializeOwned,
1628{
1629 type Error = TypedStoreError;
1630
1631 fn contains_key(&self, key: &K) -> Result<bool, TypedStoreError> {
1632 self.contains_raw_key(be_fix_int_ser(key))
1633 }
1634
1635 fn multi_contains_keys<J>(
1636 &self,
1637 keys: impl IntoIterator<Item = J>,
1638 ) -> Result<Vec<bool>, Self::Error>
1639 where
1640 J: Borrow<K>,
1641 {
1642 self.multi_contains_raw_keys(keys.into_iter().map(|k| be_fix_int_ser(k.borrow())))
1643 }
1644
1645 fn get(&self, key: &K) -> Result<Option<V>, TypedStoreError> {
1646 self.get_raw_key(be_fix_int_ser(key))
1647 }
1648
1649 fn insert(&self, key: &K, value: &V) -> Result<(), TypedStoreError> {
1650 self.insert_raw_key(be_fix_int_ser(key), value)
1651 }
1652
1653 fn remove(&self, key: &K) -> Result<(), TypedStoreError> {
1654 self.remove_raw_key(be_fix_int_ser(key))
1655 }
1656
1657 #[instrument(level = "trace", skip_all, err)]
1661 fn schedule_delete_all(&self) -> Result<(), TypedStoreError> {
1662 let Some(last_key) = self
1663 .safe_range_iter_reversed(..)
1664 .next()
1665 .transpose()?
1666 .map(|(k, _v)| k)
1667 else {
1668 return Ok(());
1669 };
1670 let mut to = be_fix_int_ser(&last_key);
1673 to.push(0);
1674 let mut batch = self.batch();
1677 batch.schedule_delete_range_raw(self, Vec::new(), to)?;
1678 batch.write()
1679 }
1680
1681 fn is_empty(&self) -> bool {
1682 self.safe_iter().next().is_none()
1683 }
1684
1685 fn safe_iter(&'a self) -> DbIterator<'a, (K, V)> {
1686 match &self.db.storage {
1687 Storage::Rocks(db) => match self.rocks_safe_iter(db, self.opts.readopts()) {
1688 Ok(iter) => Box::new(iter),
1689 Err(e) => error_iterator(e),
1690 },
1691 Storage::InMemory(db) => db.iterator(self.column_family.name(), None, None, false),
1692 }
1693 }
1694
1695 fn safe_iter_with_bounds(
1696 &'a self,
1697 lower_bound: Option<K>,
1698 upper_bound: Option<K>,
1699 ) -> DbIterator<'a, (K, V)> {
1700 let range = (
1701 lower_bound.map(Bound::Included).unwrap_or(Bound::Unbounded),
1702 upper_bound.map(Bound::Excluded).unwrap_or(Bound::Unbounded),
1703 );
1704 self.safe_range_iter(range)
1705 }
1706
1707 fn safe_range_iter(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
1708 let (lower_bound, upper_bound) = iterator_bounds_with_range(range);
1709 self.iter_forward_raw(lower_bound, upper_bound)
1710 }
1711
1712 fn safe_range_iter_reversed(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
1716 let (lower_bound, upper_bound) = iterator_bounds_with_range(range);
1717 self.iter_reversed_raw(lower_bound, upper_bound)
1718 }
1719
1720 fn multi_get<J>(
1722 &self,
1723 keys: impl IntoIterator<Item = J>,
1724 ) -> Result<Vec<Option<V>>, TypedStoreError>
1725 where
1726 J: Borrow<K>,
1727 {
1728 self.multi_get_raw_keys(keys.into_iter().map(|k| be_fix_int_ser(k.borrow())))
1729 }
1730
1731 #[instrument(level = "trace", skip_all, err)]
1733 fn multi_insert<J, U>(
1734 &self,
1735 key_val_pairs: impl IntoIterator<Item = (J, U)>,
1736 ) -> Result<(), Self::Error>
1737 where
1738 J: Borrow<K>,
1739 U: Borrow<V>,
1740 {
1741 let mut batch = self.batch();
1742 batch.insert_batch(self, key_val_pairs)?;
1743 batch.write()
1744 }
1745
1746 #[instrument(level = "trace", skip_all, err)]
1748 fn multi_remove<J>(&self, keys: impl IntoIterator<Item = J>) -> Result<(), Self::Error>
1749 where
1750 J: Borrow<K>,
1751 {
1752 let mut batch = self.batch();
1753 batch.delete_batch(self, keys)?;
1754 batch.write()
1755 }
1756
1757 #[instrument(level = "trace", skip_all, err)]
1759 fn try_catch_up_with_primary(&self) -> Result<(), Self::Error> {
1760 self.db.try_catch_up_with_primary()
1761 }
1762}
1763
1764pub struct TaggedDBMap<K, V> {
1817 tag: u8,
1818 map: DBMap<(u8, K), V>,
1819}
1820
1821impl<K, V> TaggedDBMap<K, V> {
1822 fn strip_tag(row: Result<((u8, K), V), TypedStoreError>) -> Result<(K, V), TypedStoreError> {
1825 row.map(|((_, key), value)| (key, value))
1826 }
1827
1828 pub(crate) fn tag(&self) -> u8 {
1829 self.tag
1830 }
1831
1832 pub fn ingest_sst_files(&self, paths: &[PathBuf]) -> Result<(), TypedStoreError> {
1835 self.map.ingest_sst_files(paths)
1836 }
1837
1838 pub fn reopen(
1841 db: &Arc<Database>,
1842 cf_name: &str,
1843 tag: u8,
1844 rw_options: &ReadWriteOptions,
1845 skip_metrics_reporting: bool,
1846 ) -> Result<Self, TypedStoreError> {
1847 Ok(Self {
1848 tag,
1849 map: DBMap::reopen(db, Some(cf_name), rw_options, skip_metrics_reporting)?,
1850 })
1851 }
1852
1853 pub fn batch(&self) -> DBBatch {
1855 self.map.batch()
1856 }
1857
1858 pub fn with_metrics_label(mut self, label: &str) -> Self {
1860 self.map = self.map.with_metrics_label(label);
1861 self
1862 }
1863}
1864
1865impl<'a, K, V> Map<'a, K, V> for TaggedDBMap<K, V>
1866where
1867 K: Serialize + DeserializeOwned,
1868 V: Serialize + DeserializeOwned,
1869{
1870 type Error = TypedStoreError;
1871
1872 fn contains_key(&self, key: &K) -> Result<bool, TypedStoreError> {
1873 self.map.contains_raw_key(be_fix_int_ser(&(self.tag, key)))
1874 }
1875
1876 fn multi_contains_keys<J>(
1877 &self,
1878 keys: impl IntoIterator<Item = J>,
1879 ) -> Result<Vec<bool>, TypedStoreError>
1880 where
1881 J: Borrow<K>,
1882 {
1883 self.map.multi_contains_raw_keys(
1884 keys.into_iter()
1885 .map(|key| be_fix_int_ser(&(self.tag, key.borrow()))),
1886 )
1887 }
1888
1889 fn get(&self, key: &K) -> Result<Option<V>, TypedStoreError> {
1890 self.map.get_raw_key(be_fix_int_ser(&(self.tag, key)))
1891 }
1892
1893 fn multi_get<J>(
1894 &self,
1895 keys: impl IntoIterator<Item = J>,
1896 ) -> Result<Vec<Option<V>>, TypedStoreError>
1897 where
1898 J: Borrow<K>,
1899 {
1900 self.map.multi_get_raw_keys(
1901 keys.into_iter()
1902 .map(|key| be_fix_int_ser(&(self.tag, key.borrow()))),
1903 )
1904 }
1905
1906 fn insert(&self, key: &K, value: &V) -> Result<(), TypedStoreError> {
1907 self.map
1908 .insert_raw_key(be_fix_int_ser(&(self.tag, key)), value)
1909 }
1910
1911 fn remove(&self, key: &K) -> Result<(), TypedStoreError> {
1912 self.map.remove_raw_key(be_fix_int_ser(&(self.tag, key)))
1913 }
1914
1915 #[instrument(level = "trace", skip_all, err)]
1916 fn multi_insert<J, U>(
1917 &self,
1918 key_val_pairs: impl IntoIterator<Item = (J, U)>,
1919 ) -> Result<(), TypedStoreError>
1920 where
1921 J: Borrow<K>,
1922 U: Borrow<V>,
1923 {
1924 let mut batch = self.batch();
1925 batch.insert_batch_tagged(self, key_val_pairs)?;
1926 batch.write()
1927 }
1928
1929 #[instrument(level = "trace", skip_all, err)]
1930 fn multi_remove<J>(&self, keys: impl IntoIterator<Item = J>) -> Result<(), TypedStoreError>
1931 where
1932 J: Borrow<K>,
1933 {
1934 let mut batch = self.batch();
1935 batch.delete_batch_tagged(self, keys)?;
1936 batch.write()
1937 }
1938
1939 #[instrument(level = "trace", skip_all, err)]
1945 fn schedule_delete_all(&self) -> Result<(), TypedStoreError> {
1946 let from = be_fix_int_ser(&self.tag);
1947 let to = match prefix_iterator_bounds(&self.tag).1 {
1948 Some(to) => {
1949 if self.is_empty() {
1950 return Ok(());
1951 }
1952 to
1953 }
1954 None => match self.safe_range_iter_reversed(..).next().transpose()? {
1959 Some((last_key, _)) => {
1960 let mut to = be_fix_int_ser(&(self.tag, last_key));
1961 to.push(0);
1962 to
1963 }
1964 None => return Ok(()),
1965 },
1966 };
1967
1968 let mut batch = self.batch();
1969 batch.schedule_delete_range_raw(&self.map, from, to)?;
1970 batch.write()
1971 }
1972
1973 fn is_empty(&self) -> bool {
1974 self.safe_iter().next().is_none()
1975 }
1976
1977 fn safe_iter(&'a self) -> DbIterator<'a, (K, V)> {
1978 Box::new(
1979 self.map
1980 .safe_iter_with_prefix(&self.tag)
1981 .map(Self::strip_tag),
1982 )
1983 }
1984
1985 fn safe_iter_with_bounds(
1986 &'a self,
1987 lower_bound: Option<K>,
1988 upper_bound: Option<K>,
1989 ) -> DbIterator<'a, (K, V)> {
1990 let range = (
1991 lower_bound.map(Bound::Included).unwrap_or(Bound::Unbounded),
1992 upper_bound.map(Bound::Excluded).unwrap_or(Bound::Unbounded),
1993 );
1994 self.safe_range_iter(range)
1995 }
1996
1997 fn safe_range_iter(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
1998 let (lower_bound, upper_bound) = prefix_iterator_bounds_with_range(&self.tag, range);
1999 Box::new(
2000 self.map
2001 .iter_forward_raw(lower_bound, upper_bound)
2002 .map(Self::strip_tag),
2003 )
2004 }
2005
2006 fn safe_range_iter_reversed(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
2007 let (lower_bound, upper_bound) = prefix_iterator_bounds_with_range(&self.tag, range);
2008 Box::new(
2009 self.map
2010 .iter_reversed_raw(lower_bound, upper_bound)
2011 .map(Self::strip_tag),
2012 )
2013 }
2014
2015 fn try_catch_up_with_primary(&self) -> Result<(), TypedStoreError> {
2016 self.map.try_catch_up_with_primary()
2017 }
2018}
2019
2020pub async fn wait_for_database_close(db: DatabaseClose) -> bool {
2024 let deadline = Instant::now() + Duration::from_secs(30);
2027 while db.0.strong_count() != 0 {
2028 if Instant::now() > deadline {
2029 return false;
2030 }
2031 tokio::time::sleep(Duration::from_millis(100)).await;
2032 }
2033 true
2034}
2035
2036pub(crate) fn error_iterator<'a, T: 'a>(error: TypedStoreError) -> DbIterator<'a, T> {
2038 Box::new(std::iter::once(Err(error)))
2039}
2040
2041pub fn drop_tolerant_write_options() -> rocksdb::WriteOptions {
2044 let mut options = rocksdb::WriteOptions::default();
2045 options.set_ignore_missing_column_families(true);
2046 options
2047}
2048
2049fn default_hash(value: &[u8]) -> Digest<32> {
2050 let mut hasher = fastcrypto::hash::Blake2b256::default();
2051 hasher.update(value);
2052 hasher.finalize()
2053}
2054
2055const THROUGHPUT_FLOOR_BYTES_PER_SEC: f64 = 32.0 * 1024.0 * 1024.0; const MIN_VERY_SLOW_BATCH_WRITE_SECS: f64 = 1.0;
2061
2062fn very_slow_batch_write_threshold_secs(batch_size_bytes: usize) -> f64 {
2067 (batch_size_bytes as f64 / THROUGHPUT_FLOOR_BYTES_PER_SEC).max(MIN_VERY_SLOW_BATCH_WRITE_SECS)
2068}
2069
2070#[cfg(test)]
2071mod tests {
2072 use super::very_slow_batch_write_threshold_secs;
2073
2074 const MIB: usize = 1024 * 1024;
2075
2076 #[test]
2077 fn very_slow_batch_write_threshold_never_below_one_second() {
2078 assert_eq!(very_slow_batch_write_threshold_secs(0), 1.0);
2079 assert_eq!(very_slow_batch_write_threshold_secs(1), 1.0);
2080 assert_eq!(very_slow_batch_write_threshold_secs(MIB), 1.0);
2081 assert_eq!(very_slow_batch_write_threshold_secs(32 * MIB), 1.0);
2082 }
2083
2084 #[test]
2085 fn very_slow_batch_write_threshold_grows_with_batch_size() {
2086 assert_eq!(very_slow_batch_write_threshold_secs(48 * MIB), 1.5);
2087 assert_eq!(very_slow_batch_write_threshold_secs(64 * MIB), 2.0);
2088 assert_eq!(very_slow_batch_write_threshold_secs(320 * MIB), 10.0);
2089 }
2090}