1use std::{
5 borrow::Borrow,
6 marker::PhantomData,
7 ops::{Bound, Deref, RangeBounds},
8 path::Path,
9 sync::Arc,
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::{DBPinnableSlice, Error, LiveFile, ReadOptions, WriteBatch, checkpoint::Checkpoint};
18use serde::{Serialize, de::DeserializeOwned};
19use tokio::sync::oneshot;
20use tracing::{debug, error, instrument, warn};
21use typed_store_error::TypedStoreError;
22
23use crate::{
24 DbIterator,
25 memstore::{InMemoryBatch, InMemoryDB},
26 metrics::{DBMetrics, RocksDBPerfContext, SamplingInterval},
27 rocks::{
28 RocksDB,
29 errors::{typed_store_err_from_bcs_err, typed_store_err_from_rocks_err},
30 options::ReadWriteOptions,
31 rocks_cf, rocks_util,
32 safe_iter::{SafeIter, SafeRevIter},
33 },
34 traits::{Map, TableSummary},
35 util::{
36 be_fix_int_ser, iterator_bounds_with_range, prefix_iterator_bounds,
37 prefix_iterator_bounds_with_range,
38 },
39};
40
41#[derive(Clone)]
42pub(crate) enum ColumnFamily {
43 Rocks(String),
44 #[allow(dead_code)]
45 InMemory(String),
46}
47
48impl std::fmt::Debug for ColumnFamily {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 ColumnFamily::Rocks(name) => write!(f, "RocksDB cf: {name}"),
52 ColumnFamily::InMemory(name) => write!(f, "InMemory cf: {name}"),
53 }
54 }
55}
56
57impl ColumnFamily {
58 pub(crate) fn name(&self) -> &str {
59 match self {
60 ColumnFamily::Rocks(name) => name,
61 ColumnFamily::InMemory(name) => name,
62 }
63 }
64}
65
66pub(crate) enum Storage {
67 Rocks(RocksDB),
68 #[allow(dead_code)]
69 InMemory(InMemoryDB),
70}
71
72impl std::fmt::Debug for Storage {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 match self {
75 Storage::Rocks(db) => write!(f, "RocksDB Storage {db:?}"),
76 Storage::InMemory(db) => write!(f, "InMemoryDB Storage {db:?}"),
77 }
78 }
79}
80
81pub(crate) enum GetResult<'a> {
82 Rocks(DBPinnableSlice<'a>),
83 InMemory(Vec<u8>),
84}
85
86impl Deref for GetResult<'_> {
87 type Target = [u8];
88 fn deref(&self) -> &[u8] {
89 match self {
90 GetResult::Rocks(d) => d.deref(),
91 GetResult::InMemory(d) => d.deref(),
92 }
93 }
94}
95
96pub enum StorageWriteBatch {
97 Rocks(rocksdb::WriteBatch),
98 InMemory(InMemoryBatch),
99}
100
101#[derive(Debug, Default)]
102pub struct MetricConf {
103 pub db_name: String,
104 pub read_sample_interval: SamplingInterval,
105 pub write_sample_interval: SamplingInterval,
106 pub iter_sample_interval: SamplingInterval,
107}
108
109impl MetricConf {
110 pub fn new(db_name: &str) -> Self {
111 if db_name.is_empty() {
112 error!("A meaningful db name should be used for metrics reporting.")
113 }
114 Self {
115 db_name: db_name.to_string(),
116 read_sample_interval: SamplingInterval::default(),
117 write_sample_interval: SamplingInterval::default(),
118 iter_sample_interval: SamplingInterval::default(),
119 }
120 }
121
122 pub fn with_sampling(self, read_interval: SamplingInterval) -> Self {
123 Self {
124 db_name: self.db_name,
125 read_sample_interval: read_interval,
126 write_sample_interval: SamplingInterval::default(),
127 iter_sample_interval: SamplingInterval::default(),
128 }
129 }
130}
131
132const CF_METRICS_REPORT_PERIOD_SECS: u64 = 30;
133
134#[derive(Debug)]
135pub struct Database {
136 pub(crate) storage: Storage,
137 pub(crate) metric_conf: MetricConf,
138}
139
140impl Drop for Database {
141 fn drop(&mut self) {
142 DBMetrics::get().decrement_num_active_dbs(&self.metric_conf.db_name);
143 }
144}
145
146impl Database {
147 pub(crate) fn new(storage: Storage, metric_conf: MetricConf) -> Self {
148 DBMetrics::get().increment_num_active_dbs(&metric_conf.db_name);
149 Self {
150 storage,
151 metric_conf,
152 }
153 }
154
155 pub(crate) fn get<K: AsRef<[u8]>>(
156 &self,
157 cf: &ColumnFamily,
158 key: K,
159 readopts: &ReadOptions,
160 ) -> Result<Option<GetResult<'_>>, TypedStoreError> {
161 match (&self.storage, cf) {
162 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => Ok(db
163 .underlying
164 .get_pinned_cf_opt(&rocks_cf(db, cf.name()), key, readopts)
165 .map_err(typed_store_err_from_rocks_err)?
166 .map(GetResult::Rocks)),
167 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => {
168 Ok(db.get(cf_name, key).map(GetResult::InMemory))
169 }
170
171 _ => Err(TypedStoreError::RocksDB(
172 "typed store invariant violation".to_string(),
173 )),
174 }
175 }
176
177 pub(crate) fn multi_get<I, K>(
178 &self,
179 cf: &ColumnFamily,
180 keys: I,
181 readopts: &ReadOptions,
182 ) -> Vec<Result<Option<GetResult<'_>>, TypedStoreError>>
183 where
184 I: IntoIterator<Item = K>,
185 K: AsRef<[u8]>,
186 {
187 match (&self.storage, cf) {
188 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => {
189 let keys_vec: Vec<K> = keys.into_iter().collect();
190 let res = db.underlying.batched_multi_get_cf_opt(
191 &rocks_cf(db, cf.name()),
192 keys_vec.iter(),
193 false,
195 readopts,
196 );
197 res.into_iter()
198 .map(|r| {
199 r.map_err(typed_store_err_from_rocks_err)
200 .map(|item| item.map(GetResult::Rocks))
201 })
202 .collect()
203 }
204 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => db
205 .multi_get(cf_name, keys)
206 .into_iter()
207 .map(|r| Ok(r.map(GetResult::InMemory)))
208 .collect(),
209 _ => unreachable!("typed store invariant violation"),
210 }
211 }
212
213 pub fn cf_handle(&self, name: &str) -> Option<()> {
214 match &self.storage {
215 Storage::Rocks(db) => db.underlying.cf_handle(name).map(|_| ()),
216 Storage::InMemory(db) => db.has_cf(name).then_some(()),
217 }
218 }
219
220 #[instrument(level = "debug", skip(self, options), err)]
227 pub fn create_cf(&self, name: &str, options: &rocksdb::Options) -> Result<(), TypedStoreError> {
228 match &self.storage {
229 Storage::Rocks(db) => nondeterministic!(db.underlying.create_cf(name, options))
230 .map_err(typed_store_err_from_rocks_err),
231 Storage::InMemory(db) => {
232 db.create_cf(name);
233 Ok(())
234 }
235 }
236 }
237
238 #[instrument(level = "debug", skip(self), err)]
241 pub fn drop_cf(&self, name: &str) -> Result<(), TypedStoreError> {
242 if name == rocksdb::DEFAULT_COLUMN_FAMILY_NAME {
243 return Err(TypedStoreError::RocksDB(format!(
246 "the {name} column family cannot be dropped"
247 )));
248 }
249 match &self.storage {
250 Storage::Rocks(db) => nondeterministic!(db.underlying.drop_cf(name))
251 .map_err(typed_store_err_from_rocks_err),
252 Storage::InMemory(db) => {
253 db.drop_cf(name);
254 Ok(())
255 }
256 }
257 }
258
259 pub(crate) fn delete_cf<K: AsRef<[u8]>>(
260 &self,
261 cf: &ColumnFamily,
262 key: K,
263 ) -> Result<(), TypedStoreError> {
264 fail_point!("delete-cf-before");
265 let ret = match (&self.storage, cf) {
266 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => db
267 .underlying
268 .delete_cf(&rocks_cf(db, cf.name()), key)
269 .map_err(typed_store_err_from_rocks_err),
270 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => {
271 db.delete(cf_name, key.as_ref());
272 Ok(())
273 }
274 _ => Err(TypedStoreError::RocksDB(
275 "typed store invariant violation".to_string(),
276 )),
277 };
278 fail_point!("delete-cf-after");
279 #[allow(clippy::let_and_return)]
280 ret
281 }
282
283 pub fn path_for_pruning(&self) -> &Path {
284 match &self.storage {
285 Storage::Rocks(rocks) => rocks.underlying.path(),
286 _ => unimplemented!("method is only supported for rocksdb backend"),
287 }
288 }
289
290 pub(crate) fn put_cf(
291 &self,
292 cf: &ColumnFamily,
293 key: Vec<u8>,
294 value: Vec<u8>,
295 ) -> Result<(), TypedStoreError> {
296 fail_point!("put-cf-before");
297 let ret = match (&self.storage, cf) {
298 (Storage::Rocks(db), ColumnFamily::Rocks(_)) => db
299 .underlying
300 .put_cf(&rocks_cf(db, cf.name()), key, value)
301 .map_err(typed_store_err_from_rocks_err),
302 (Storage::InMemory(db), ColumnFamily::InMemory(cf_name)) => {
303 db.put(cf_name, key, value);
304 Ok(())
305 }
306 _ => Err(TypedStoreError::RocksDB(
307 "typed store invariant violation".to_string(),
308 )),
309 };
310 fail_point!("put-cf-after");
311 #[allow(clippy::let_and_return)]
312 ret
313 }
314
315 pub(crate) fn key_may_exist_cf<K: AsRef<[u8]>>(
316 &self,
317 cf: &ColumnFamily,
318 key: K,
319 readopts: &ReadOptions,
320 ) -> bool {
321 match &self.storage {
322 Storage::Rocks(rocks) => {
325 rocks
326 .underlying
327 .key_may_exist_cf_opt(&rocks_cf(rocks, cf.name()), key, readopts)
328 }
329 _ => true,
330 }
331 }
332
333 pub(crate) fn flush_cf(&self, cf: &ColumnFamily) -> Result<(), TypedStoreError> {
335 match &self.storage {
336 Storage::Rocks(rocks) => nondeterministic!({
341 let cf_name = cf.name();
342 if let Some(handle) = rocks.underlying.cf_handle(cf_name) {
343 rocks.underlying.flush_cf(&handle).map_err(|e| {
344 TypedStoreError::RocksDB(format!(
345 "Failed to flush column family {cf_name}: {e}"
346 ))
347 })?;
348 }
349 Ok(())
350 }),
351 Storage::InMemory(_) => Ok(()),
353 }
354 }
355
356 pub fn flush_all(&self) -> Result<(), TypedStoreError> {
359 match &self.storage {
360 Storage::Rocks(rocks) => nondeterministic!({
363 for cf_name in rocks.cf_names().map_err(|e| {
364 TypedStoreError::RocksDB(format!(
365 "Failed to list column families of {}: {e}",
366 rocks.underlying.path().display()
367 ))
368 })? {
369 if let Some(cf) = rocks.underlying.cf_handle(&cf_name) {
370 rocks.underlying.flush_cf(&cf).map_err(|e| {
371 TypedStoreError::RocksDB(format!(
372 "Failed to flush column family {cf_name}: {e}"
373 ))
374 })?;
375 }
376 }
377 Ok(())
378 }),
379 Storage::InMemory(_) => Ok(()),
381 }
382 }
383
384 pub fn write(&self, batch: StorageWriteBatch) -> Result<(), TypedStoreError> {
385 self.write_opt(batch, &rocksdb::WriteOptions::default())
386 }
387
388 pub fn write_opt(
389 &self,
390 batch: StorageWriteBatch,
391 write_options: &rocksdb::WriteOptions,
392 ) -> Result<(), TypedStoreError> {
393 fail_point!("batch-write-before");
394 let ret = match (&self.storage, batch) {
395 (Storage::Rocks(rocks), StorageWriteBatch::Rocks(batch)) => rocks
396 .underlying
397 .write_opt(batch, write_options)
398 .map_err(typed_store_err_from_rocks_err),
399 (Storage::InMemory(db), StorageWriteBatch::InMemory(batch)) => {
400 db.write(batch);
402 Ok(())
403 }
404 _ => Err(TypedStoreError::RocksDB(
405 "using invalid batch type for the database".to_string(),
406 )),
407 };
408 fail_point!("batch-write-after");
409
410 #[allow(clippy::let_and_return)]
411 ret
412 }
413
414 pub(crate) fn compact_range_cf<K: AsRef<[u8]>>(
415 &self,
416 cf: &ColumnFamily,
417 start: Option<K>,
418 end: Option<K>,
419 ) {
420 if let Storage::Rocks(rocksdb) = &self.storage {
421 rocksdb
422 .underlying
423 .compact_range_cf(&rocks_cf(rocksdb, cf.name()), start, end);
424 }
425 }
426
427 pub fn checkpoint(&self, path: &Path) -> Result<(), TypedStoreError> {
428 if let Storage::Rocks(rocks) = &self.storage {
430 let checkpoint =
431 Checkpoint::new(&rocks.underlying).map_err(typed_store_err_from_rocks_err)?;
432 checkpoint
433 .create_checkpoint(path)
434 .map_err(|e| TypedStoreError::RocksDB(e.to_string()))?;
435 }
436 Ok(())
437 }
438
439 pub fn get_sampling_interval(&self) -> SamplingInterval {
440 self.metric_conf.read_sample_interval.new_from_self()
441 }
442
443 pub fn multiget_sampling_interval(&self) -> SamplingInterval {
444 self.metric_conf.read_sample_interval.new_from_self()
445 }
446
447 pub fn write_sampling_interval(&self) -> SamplingInterval {
448 self.metric_conf.write_sample_interval.new_from_self()
449 }
450
451 pub fn iter_sampling_interval(&self) -> SamplingInterval {
452 self.metric_conf.iter_sample_interval.new_from_self()
453 }
454
455 pub(crate) fn db_name(&self) -> String {
456 let name = &self.metric_conf.db_name;
457 if name.is_empty() {
458 "default".to_string()
459 } else {
460 name.clone()
461 }
462 }
463
464 pub fn live_files(&self) -> Result<Vec<LiveFile>, Error> {
465 match &self.storage {
466 Storage::Rocks(rocks) => rocks.underlying.live_files(),
467 _ => Ok(vec![]),
468 }
469 }
470
471 pub(crate) fn try_catch_up_with_primary(&self) -> Result<(), TypedStoreError> {
472 if let Storage::Rocks(rocks) = &self.storage {
473 rocks
474 .underlying
475 .try_catch_up_with_primary()
476 .map_err(typed_store_err_from_rocks_err)?;
477 }
478 Ok(())
479 }
480}
481
482fn rocks_cf_from_db<'a>(
483 db: &'a Database,
484 cf_name: &str,
485) -> Result<Arc<rocksdb::BoundColumnFamily<'a>>, TypedStoreError> {
486 match &db.storage {
487 Storage::Rocks(rocksdb) => Ok(rocksdb
488 .underlying
489 .cf_handle(cf_name)
490 .expect("the column family was deleted unexpectedly")),
491 _ => Err(TypedStoreError::RocksDB(
492 "using invalid batch type for the database".to_string(),
493 )),
494 }
495}
496
497#[derive(Clone, Debug)]
499pub struct DBMap<K, V> {
500 pub db: Arc<Database>,
501 _phantom: PhantomData<fn(K) -> V>,
502 column_family: ColumnFamily,
503 pub opts: ReadWriteOptions,
504 db_metrics: Arc<DBMetrics>,
505 get_sample_interval: SamplingInterval,
506 multiget_sample_interval: SamplingInterval,
507 write_sample_interval: SamplingInterval,
508 iter_sample_interval: SamplingInterval,
509 _metrics_task_cancel_handle: Arc<oneshot::Sender<()>>,
510}
511
512impl<K, V> DBMap<K, V> {
513 pub(crate) fn new(
514 db: Arc<Database>,
515 opts: &ReadWriteOptions,
516 column_family: ColumnFamily,
517 skip_metrics_reporting: bool,
518 ) -> Self {
519 let db_cloned = Arc::downgrade(&db);
520 let db_metrics = DBMetrics::get();
521 let db_metrics_cloned = db_metrics.clone();
522 let cf = column_family.name().to_string();
523
524 let (sender, mut recv) = tokio::sync::oneshot::channel();
525 if !skip_metrics_reporting && matches!(db.storage, Storage::Rocks(_)) {
526 tokio::task::spawn(async move {
527 let mut interval =
528 tokio::time::interval(Duration::from_secs(CF_METRICS_REPORT_PERIOD_SECS));
529 loop {
530 tokio::select! {
531 _ = interval.tick() => {
532 if let Some(db) = db_cloned.upgrade() {
533 let cf = cf.clone();
534 let db_metrics = db_metrics.clone();
535 if let Err(e) = tokio::task::spawn_blocking(move || {
536 Self::report_rocksdb_metrics(&db, &cf, &db_metrics);
537 }).await {
538 error!("Failed to log metrics with error: {}", e);
539 }
540 } else {
541 break;
542 }
543 }
544 _ = &mut recv => break,
545 }
546 }
547 debug!("Returning the cf metric logging task for DBMap: {}", &cf);
548 });
549 }
550 DBMap {
551 db: db.clone(),
552 opts: opts.clone(),
553 _phantom: PhantomData,
554 column_family,
555 db_metrics: db_metrics_cloned,
556 _metrics_task_cancel_handle: Arc::new(sender),
557 get_sample_interval: db.get_sampling_interval(),
558 multiget_sample_interval: db.multiget_sampling_interval(),
559 write_sample_interval: db.write_sampling_interval(),
560 iter_sample_interval: db.iter_sampling_interval(),
561 }
562 }
563
564 #[instrument(level = "debug", skip(db), err)]
572 pub fn reopen(
573 db: &Arc<Database>,
574 opt_cf: Option<&str>,
575 rw_options: &ReadWriteOptions,
576 skip_metrics_reporting: bool,
577 ) -> Result<Self, TypedStoreError> {
578 let cf_key = opt_cf
579 .unwrap_or(rocksdb::DEFAULT_COLUMN_FAMILY_NAME)
580 .to_owned();
581 if db.cf_handle(&cf_key).is_none() {
582 return Err(TypedStoreError::UnregisteredColumn(cf_key));
583 }
584
585 let column_family = match &db.storage {
586 Storage::Rocks(_) => ColumnFamily::Rocks(cf_key),
587 Storage::InMemory(_) => ColumnFamily::InMemory(cf_key),
588 };
589 Ok(DBMap::new(
590 db.clone(),
591 rw_options,
592 column_family,
593 skip_metrics_reporting,
594 ))
595 }
596
597 pub fn cf_name(&self) -> &str {
598 self.column_family.name()
599 }
600
601 pub fn batch(&self) -> DBBatch {
602 let batch = match &self.db.storage {
603 Storage::Rocks(_) => StorageWriteBatch::Rocks(WriteBatch::default()),
604 Storage::InMemory(_) => StorageWriteBatch::InMemory(InMemoryBatch::default()),
605 };
606 DBBatch::new(
607 &self.db,
608 batch,
609 &self.db_metrics,
610 &self.write_sample_interval,
611 )
612 }
613
614 pub fn flush(&self) -> Result<(), TypedStoreError> {
616 self.db.flush_cf(&self.column_family)
617 }
618
619 pub fn flush_all(&self) -> Result<(), TypedStoreError> {
622 self.db.flush_all()
623 }
624
625 pub fn compact_range<J: Serialize>(&self, start: &J, end: &J) -> Result<(), TypedStoreError> {
626 let from_buf = be_fix_int_ser(start);
627 let to_buf = be_fix_int_ser(end);
628 self.db
629 .compact_range_cf(&self.column_family, Some(from_buf), Some(to_buf));
630 Ok(())
631 }
632
633 pub fn compact_range_raw(
634 &self,
635 cf_name: &str,
636 start: Vec<u8>,
637 end: Vec<u8>,
638 ) -> Result<(), TypedStoreError> {
639 let cf = match &self.db.storage {
640 Storage::Rocks(_) => ColumnFamily::Rocks(cf_name.to_string()),
641 Storage::InMemory(_) => ColumnFamily::InMemory(cf_name.to_string()),
642 };
643 self.db.compact_range_cf(&cf, Some(start), Some(end));
644 Ok(())
645 }
646
647 fn multi_get_pinned(
649 &self,
650 keys_bytes: impl IntoIterator<Item = Vec<u8>>,
651 ) -> Result<Vec<Option<GetResult<'_>>>, TypedStoreError> {
652 let _timer = self
653 .db_metrics
654 .op_metrics
655 .rocksdb_multiget_latency_seconds
656 .with_label_values(&[self.cf_name()])
657 .start_timer();
658 let perf_ctx = if self.multiget_sample_interval.sample() {
659 Some(RocksDBPerfContext)
660 } else {
661 None
662 };
663 let results: Result<Vec<_>, TypedStoreError> = self
664 .db
665 .multi_get(&self.column_family, keys_bytes, &self.opts.readopts())
666 .into_iter()
667 .collect();
668 let entries = results?;
669 let entry_size = entries
670 .iter()
671 .flatten()
672 .map(|entry| entry.len())
673 .sum::<usize>();
674 self.db_metrics
675 .op_metrics
676 .rocksdb_multiget_bytes
677 .with_label_values(&[self.cf_name()])
678 .observe(entry_size as f64);
679 if perf_ctx.is_some() {
680 self.db_metrics
681 .read_perf_ctx_metrics
682 .report_metrics(self.cf_name());
683 }
684 Ok(entries)
685 }
686
687 pub fn checkpoint_db(&self, path: &Path) -> Result<(), TypedStoreError> {
688 self.db.checkpoint(path)
689 }
690
691 pub fn table_summary(&self) -> eyre::Result<TableSummary>
692 where
693 K: Serialize + DeserializeOwned,
694 V: Serialize + DeserializeOwned,
695 {
696 let mut num_keys = 0;
697 let mut key_bytes_total = 0;
698 let mut value_bytes_total = 0;
699 let mut key_hist = hdrhistogram::Histogram::<u64>::new_with_max(100000, 2).unwrap();
700 let mut value_hist = hdrhistogram::Histogram::<u64>::new_with_max(100000, 2).unwrap();
701 for item in self.safe_iter() {
702 let (key, value) = item?;
703 num_keys += 1;
704 let key_len = be_fix_int_ser(key.borrow()).len();
705 let value_len = bcs::to_bytes(value.borrow())?.len();
706 key_bytes_total += key_len;
707 value_bytes_total += value_len;
708 key_hist.record(key_len as u64)?;
709 value_hist.record(value_len as u64)?;
710 }
711 Ok(TableSummary {
712 num_keys,
713 key_bytes_total,
714 value_bytes_total,
715 key_hist,
716 value_hist,
717 })
718 }
719
720 fn create_iter_context(
722 &self,
723 ) -> (
724 Option<HistogramTimer>,
725 Option<Histogram>,
726 Option<Histogram>,
727 Option<RocksDBPerfContext>,
728 ) {
729 let timer = self
730 .db_metrics
731 .op_metrics
732 .rocksdb_iter_latency_seconds
733 .with_label_values(&[self.cf_name()])
734 .start_timer();
735 let bytes_scanned = self
736 .db_metrics
737 .op_metrics
738 .rocksdb_iter_bytes
739 .with_label_values(&[self.cf_name()]);
740 let keys_scanned = self
741 .db_metrics
742 .op_metrics
743 .rocksdb_iter_keys
744 .with_label_values(&[self.cf_name()]);
745 let perf_ctx = if self.iter_sample_interval.sample() {
746 Some(RocksDBPerfContext)
747 } else {
748 None
749 };
750 (
751 Some(timer),
752 Some(bytes_scanned),
753 Some(keys_scanned),
754 perf_ctx,
755 )
756 }
757
758 fn rocks_safe_iter<'a>(&self, db: &'a RocksDB, readopts: ReadOptions) -> SafeIter<'a, K, V>
761 where
762 K: DeserializeOwned,
763 V: DeserializeOwned,
764 {
765 let db_iter = db
766 .underlying
767 .raw_iterator_cf_opt(&rocks_cf(db, self.column_family.name()), readopts);
768 let (_timer, bytes_scanned, keys_scanned, _perf_ctx) = self.create_iter_context();
769 SafeIter::new(
770 self.cf_name().to_string(),
771 db_iter,
772 _timer,
773 _perf_ctx,
774 bytes_scanned,
775 keys_scanned,
776 Some(self.db_metrics.clone()),
777 )
778 }
779
780 fn iter_forward_raw(
784 &self,
785 lower_bound: Option<Vec<u8>>,
786 upper_bound: Option<Vec<u8>>,
787 ) -> DbIterator<'_, (K, V)>
788 where
789 K: DeserializeOwned,
790 V: DeserializeOwned,
791 {
792 match &self.db.storage {
793 Storage::Rocks(db) => {
794 let readopts =
795 rocks_util::apply_range_bounds(self.opts.readopts(), lower_bound, upper_bound);
796 Box::new(self.rocks_safe_iter(db, readopts))
797 }
798 Storage::InMemory(db) => {
799 db.iterator(self.column_family.name(), lower_bound, upper_bound, false)
800 }
801 }
802 }
803
804 fn iter_reversed_raw(
811 &self,
812 lower_bound: Option<Vec<u8>>,
813 upper_bound: Option<Vec<u8>>,
814 ) -> DbIterator<'_, (K, V)>
815 where
816 K: DeserializeOwned,
817 V: DeserializeOwned,
818 {
819 match &self.db.storage {
820 Storage::Rocks(db) => {
821 let readopts =
822 rocks_util::apply_range_bounds(self.opts.readopts(), lower_bound, None);
823 Box::new(SafeRevIter::new(
824 self.rocks_safe_iter(db, readopts),
825 upper_bound,
826 ))
827 }
828 Storage::InMemory(db) => {
829 db.iterator(self.column_family.name(), lower_bound, upper_bound, true)
830 }
831 }
832 }
833
834 pub fn safe_iter_with_prefix<P>(&self, prefix: &P) -> DbIterator<'_, (K, V)>
841 where
842 P: ?Sized + Serialize,
843 K: DeserializeOwned,
844 V: DeserializeOwned,
845 {
846 let (lower_bound, upper_bound) = prefix_iterator_bounds(prefix);
847 self.iter_forward_raw(lower_bound, upper_bound)
848 }
849
850 pub fn safe_iter_with_prefix_from<P, C>(&self, prefix: &P, cursor: &C) -> DbIterator<'_, (K, V)>
861 where
862 P: ?Sized + Serialize,
863 C: ?Sized + Serialize,
864 K: DeserializeOwned,
865 V: DeserializeOwned,
866 {
867 let (lower_bound, upper_bound) = prefix_iterator_bounds(prefix);
868 let lower_bound = lower_bound.map(|mut lower| {
869 lower.extend_from_slice(&be_fix_int_ser(cursor));
870 lower
871 });
872 self.iter_forward_raw(lower_bound, upper_bound)
873 }
874
875 pub fn safe_iter_with_prefix_reversed<P>(&self, prefix: &P) -> DbIterator<'_, (K, V)>
879 where
880 P: ?Sized + Serialize,
881 K: DeserializeOwned,
882 V: DeserializeOwned,
883 {
884 let (lower_bound, upper_bound) = prefix_iterator_bounds(prefix);
885 self.iter_reversed_raw(lower_bound, upper_bound)
886 }
887}
888
889pub struct DBBatch {
959 database: Arc<Database>,
960 batch: StorageWriteBatch,
961 db_metrics: Arc<DBMetrics>,
962 write_sample_interval: SamplingInterval,
963}
964
965impl DBBatch {
966 pub fn new(
970 dbref: &Arc<Database>,
971 batch: StorageWriteBatch,
972 db_metrics: &Arc<DBMetrics>,
973 write_sample_interval: &SamplingInterval,
974 ) -> Self {
975 DBBatch {
976 database: dbref.clone(),
977 batch,
978 db_metrics: db_metrics.clone(),
979 write_sample_interval: write_sample_interval.clone(),
980 }
981 }
982
983 #[instrument(level = "trace", skip_all, err)]
985 pub fn write(self) -> Result<(), TypedStoreError> {
986 self.write_opt(&rocksdb::WriteOptions::default())
987 }
988
989 #[instrument(level = "trace", skip_all, err)]
992 pub fn write_opt(self, write_options: &rocksdb::WriteOptions) -> Result<(), TypedStoreError> {
993 let db_name = self.database.db_name();
994 let timer = self
995 .db_metrics
996 .op_metrics
997 .rocksdb_batch_commit_latency_seconds
998 .with_label_values(&[&db_name])
999 .start_timer();
1000 let batch_size_bytes = self.size_in_bytes();
1001
1002 let perf_ctx = if self.write_sample_interval.sample() {
1003 Some(RocksDBPerfContext)
1004 } else {
1005 None
1006 };
1007 self.database.write_opt(self.batch, write_options)?;
1008 self.db_metrics
1009 .op_metrics
1010 .rocksdb_batch_commit_bytes
1011 .with_label_values(&[&db_name])
1012 .observe(batch_size_bytes as f64);
1013
1014 if perf_ctx.is_some() {
1015 self.db_metrics
1016 .write_perf_ctx_metrics
1017 .report_metrics(&db_name);
1018 }
1019 let elapsed_secs = timer.stop_and_record();
1020 let threshold_secs = very_slow_batch_write_threshold_secs(batch_size_bytes);
1021 if elapsed_secs > threshold_secs {
1022 warn!(
1023 elapsed_secs,
1024 threshold_secs,
1025 batch_size_bytes,
1026 ?db_name,
1027 "very slow batch write"
1028 );
1029 self.db_metrics
1030 .op_metrics
1031 .rocksdb_very_slow_batch_writes_count
1032 .with_label_values(&[&db_name])
1033 .inc();
1034 self.db_metrics
1035 .op_metrics
1036 .rocksdb_very_slow_batch_writes_duration_ms
1037 .with_label_values(&[&db_name])
1038 .inc_by((elapsed_secs * 1000.0) as u64);
1039 }
1040 Ok(())
1041 }
1042
1043 pub fn size_in_bytes(&self) -> usize {
1044 match self.batch {
1045 StorageWriteBatch::Rocks(ref b) => b.size_in_bytes(),
1046 StorageWriteBatch::InMemory(_) => 0,
1047 }
1048 }
1049
1050 pub fn delete_batch<J: Borrow<K>, K: Serialize, V>(
1051 &mut self,
1052 db: &DBMap<K, V>,
1053 purged_vals: impl IntoIterator<Item = J>,
1054 ) -> Result<(), TypedStoreError> {
1055 self.delete_batch_raw_keys(
1056 db,
1057 purged_vals
1058 .into_iter()
1059 .map(|key| be_fix_int_ser(key.borrow())),
1060 )
1061 }
1062
1063 fn delete_batch_raw_keys<K, V>(
1066 &mut self,
1067 db: &DBMap<K, V>,
1068 purged_vals: impl IntoIterator<Item = Vec<u8>>,
1069 ) -> Result<(), TypedStoreError> {
1070 if !Arc::ptr_eq(&db.db, &self.database) {
1071 return Err(TypedStoreError::CrossDBBatch);
1072 }
1073
1074 purged_vals
1075 .into_iter()
1076 .try_for_each::<_, Result<_, TypedStoreError>>(|k_buf| {
1077 match (&mut self.batch, &db.column_family) {
1078 (StorageWriteBatch::Rocks(b), ColumnFamily::Rocks(name)) => {
1079 b.delete_cf(&rocks_cf_from_db(&self.database, name)?, k_buf)
1080 }
1081 (StorageWriteBatch::InMemory(b), ColumnFamily::InMemory(name)) => {
1082 b.delete_cf(name, k_buf)
1083 }
1084 _ => Err(TypedStoreError::RocksDB(
1085 "typed store invariant violation".to_string(),
1086 ))?,
1087 }
1088 Ok(())
1089 })?;
1090 Ok(())
1091 }
1092
1093 pub fn schedule_delete_range<K: Serialize, V>(
1098 &mut self,
1099 db: &DBMap<K, V>,
1100 from: &K,
1101 to: &K,
1102 ) -> Result<(), TypedStoreError> {
1103 self.schedule_delete_range_raw(db, be_fix_int_ser(from), be_fix_int_ser(to))
1104 }
1105
1106 fn schedule_delete_range_raw<K, V>(
1111 &mut self,
1112 db: &DBMap<K, V>,
1113 from: Vec<u8>,
1114 to: Vec<u8>,
1115 ) -> Result<(), TypedStoreError> {
1116 if !Arc::ptr_eq(&db.db, &self.database) {
1117 return Err(TypedStoreError::CrossDBBatch);
1118 }
1119
1120 if let StorageWriteBatch::Rocks(b) = &mut self.batch {
1121 b.delete_range_cf(&rocks_cf_from_db(&self.database, db.cf_name())?, from, to);
1122 }
1123 Ok(())
1124 }
1125
1126 pub fn insert_batch<J: Borrow<K>, K: Serialize, U: Borrow<V>, V: Serialize>(
1128 &mut self,
1129 db: &DBMap<K, V>,
1130 new_vals: impl IntoIterator<Item = (J, U)>,
1131 ) -> Result<&mut Self, TypedStoreError> {
1132 self.insert_batch_raw_keys(
1133 db,
1134 new_vals
1135 .into_iter()
1136 .map(|(key, value)| (be_fix_int_ser(key.borrow()), value)),
1137 )
1138 }
1139
1140 fn insert_batch_raw_keys<K, U: Borrow<V>, V: Serialize>(
1143 &mut self,
1144 db: &DBMap<K, V>,
1145 new_vals: impl IntoIterator<Item = (Vec<u8>, U)>,
1146 ) -> Result<&mut Self, TypedStoreError> {
1147 if !Arc::ptr_eq(&db.db, &self.database) {
1148 return Err(TypedStoreError::CrossDBBatch);
1149 }
1150 let mut total = 0usize;
1151 new_vals
1152 .into_iter()
1153 .try_for_each::<_, Result<_, TypedStoreError>>(|(k_buf, v)| {
1154 let v_buf = bcs::to_bytes(v.borrow()).map_err(typed_store_err_from_bcs_err)?;
1155 total += k_buf.len() + v_buf.len();
1156 if db.opts.log_value_hash {
1157 let key_hash = default_hash(&k_buf);
1158 let value_hash = default_hash(&v_buf);
1159 debug!(
1160 "Insert to DB table: {:?}, key_hash: {:?}, value_hash: {:?}",
1161 db.cf_name(),
1162 key_hash,
1163 value_hash
1164 );
1165 }
1166 match (&mut self.batch, &db.column_family) {
1167 (StorageWriteBatch::Rocks(b), ColumnFamily::Rocks(name)) => {
1168 b.put_cf(&rocks_cf_from_db(&self.database, name)?, k_buf, v_buf)
1169 }
1170 (StorageWriteBatch::InMemory(b), ColumnFamily::InMemory(name)) => {
1171 b.put_cf(name, k_buf, v_buf)
1172 }
1173 _ => Err(TypedStoreError::RocksDB(
1174 "typed store invariant violation".to_string(),
1175 ))?,
1176 }
1177 Ok(())
1178 })?;
1179 self.db_metrics
1180 .op_metrics
1181 .rocksdb_batch_put_bytes
1182 .with_label_values(&[db.cf_name()])
1183 .observe(total as f64);
1184 Ok(self)
1185 }
1186
1187 pub fn insert_batch_tagged<J: Borrow<K>, K: Serialize, U: Borrow<V>, V: Serialize>(
1189 &mut self,
1190 map: &TaggedDBMap<K, V>,
1191 new_vals: impl IntoIterator<Item = (J, U)>,
1192 ) -> Result<&mut Self, TypedStoreError> {
1193 self.insert_batch_raw_keys(
1194 &map.map,
1195 new_vals
1196 .into_iter()
1197 .map(|(key, value)| (be_fix_int_ser(&(map.tag, key.borrow())), value)),
1198 )
1199 }
1200
1201 pub fn delete_batch_tagged<J: Borrow<K>, K: Serialize, V>(
1203 &mut self,
1204 map: &TaggedDBMap<K, V>,
1205 purged_vals: impl IntoIterator<Item = J>,
1206 ) -> Result<(), TypedStoreError> {
1207 self.delete_batch_raw_keys(
1208 &map.map,
1209 purged_vals
1210 .into_iter()
1211 .map(|key| be_fix_int_ser(&(map.tag, key.borrow()))),
1212 )
1213 }
1214}
1215
1216impl<K, V> DBMap<K, V> {
1217 #[instrument(level = "trace", skip_all, err)]
1218 pub(crate) fn contains_raw_key(&self, key_buf: Vec<u8>) -> Result<bool, TypedStoreError> {
1219 let readopts = self.opts.readopts();
1220 Ok(self
1221 .db
1222 .key_may_exist_cf(&self.column_family, &key_buf, &readopts)
1223 && self
1224 .db
1225 .get(&self.column_family, &key_buf, &readopts)?
1226 .is_some())
1227 }
1228
1229 #[instrument(level = "trace", skip_all, err)]
1230 pub(crate) fn multi_contains_raw_keys(
1231 &self,
1232 keys: impl IntoIterator<Item = Vec<u8>>,
1233 ) -> Result<Vec<bool>, TypedStoreError> {
1234 let values = self.multi_get_pinned(keys)?;
1235 Ok(values.into_iter().map(|v| v.is_some()).collect())
1236 }
1237
1238 #[instrument(level = "trace", skip_all, err)]
1239 pub(crate) fn get_raw_key(&self, key_buf: Vec<u8>) -> Result<Option<V>, TypedStoreError>
1240 where
1241 V: DeserializeOwned,
1242 {
1243 let _timer = self
1244 .db_metrics
1245 .op_metrics
1246 .rocksdb_get_latency_seconds
1247 .with_label_values(&[self.cf_name()])
1248 .start_timer();
1249 let perf_ctx = if self.get_sample_interval.sample() {
1250 Some(RocksDBPerfContext)
1251 } else {
1252 None
1253 };
1254 let res = self
1255 .db
1256 .get(&self.column_family, &key_buf, &self.opts.readopts())?;
1257 self.db_metrics
1258 .op_metrics
1259 .rocksdb_get_bytes
1260 .with_label_values(&[self.cf_name()])
1261 .observe(res.as_ref().map_or(0.0, |v| v.len() as f64));
1262 if perf_ctx.is_some() {
1263 self.db_metrics
1264 .read_perf_ctx_metrics
1265 .report_metrics(self.cf_name());
1266 }
1267 match res {
1268 Some(data) => {
1269 let value = bcs::from_bytes(&data).map_err(typed_store_err_from_bcs_err);
1270 if value.is_err() {
1271 let key_hash = default_hash(&key_buf);
1272 let value_hash = default_hash(&data);
1273 debug_fatal!(
1274 "Failed to deserialize value from DB table {:?}, key_hash: {:?}, value_hash: {:?}, error: {:?}",
1275 self.cf_name(),
1276 key_hash,
1277 value_hash,
1278 value.as_ref().err().unwrap()
1279 );
1280 }
1281 Ok(Some(value?))
1282 }
1283 None => Ok(None),
1284 }
1285 }
1286
1287 #[instrument(level = "trace", skip_all, err)]
1288 pub(crate) fn insert_raw_key(&self, key_buf: Vec<u8>, value: &V) -> Result<(), TypedStoreError>
1289 where
1290 V: Serialize,
1291 {
1292 let timer = self
1293 .db_metrics
1294 .op_metrics
1295 .rocksdb_put_latency_seconds
1296 .with_label_values(&[self.cf_name()])
1297 .start_timer();
1298 let perf_ctx = if self.write_sample_interval.sample() {
1299 Some(RocksDBPerfContext)
1300 } else {
1301 None
1302 };
1303 let value_buf = bcs::to_bytes(value).map_err(typed_store_err_from_bcs_err)?;
1304 self.db_metrics
1305 .op_metrics
1306 .rocksdb_put_bytes
1307 .with_label_values(&[self.cf_name()])
1308 .observe((key_buf.len() + value_buf.len()) as f64);
1309 if perf_ctx.is_some() {
1310 self.db_metrics
1311 .write_perf_ctx_metrics
1312 .report_metrics(self.cf_name());
1313 }
1314 self.db.put_cf(&self.column_family, key_buf, value_buf)?;
1315
1316 let elapsed_secs = timer.stop_and_record();
1317 if elapsed_secs > 1.0 {
1318 warn!(elapsed_secs, cf = ?self.cf_name(), "very slow insert");
1319 self.db_metrics
1320 .op_metrics
1321 .rocksdb_very_slow_puts_count
1322 .with_label_values(&[self.cf_name()])
1323 .inc();
1324 self.db_metrics
1325 .op_metrics
1326 .rocksdb_very_slow_puts_duration_ms
1327 .with_label_values(&[self.cf_name()])
1328 .inc_by((elapsed_secs * 1000.0) as u64);
1329 }
1330
1331 Ok(())
1332 }
1333
1334 #[instrument(level = "trace", skip_all, err)]
1335 pub(crate) fn remove_raw_key(&self, key_buf: Vec<u8>) -> Result<(), TypedStoreError> {
1336 let _timer = self
1337 .db_metrics
1338 .op_metrics
1339 .rocksdb_delete_latency_seconds
1340 .with_label_values(&[self.cf_name()])
1341 .start_timer();
1342 let perf_ctx = if self.write_sample_interval.sample() {
1343 Some(RocksDBPerfContext)
1344 } else {
1345 None
1346 };
1347 self.db.delete_cf(&self.column_family, key_buf)?;
1348 self.db_metrics
1349 .op_metrics
1350 .rocksdb_deletes
1351 .with_label_values(&[self.cf_name()])
1352 .inc();
1353 if perf_ctx.is_some() {
1354 self.db_metrics
1355 .write_perf_ctx_metrics
1356 .report_metrics(self.cf_name());
1357 }
1358 Ok(())
1359 }
1360
1361 #[instrument(level = "trace", skip_all, err)]
1362 pub(crate) fn multi_get_raw_keys(
1363 &self,
1364 keys: impl IntoIterator<Item = Vec<u8>>,
1365 ) -> Result<Vec<Option<V>>, TypedStoreError>
1366 where
1367 V: DeserializeOwned,
1368 {
1369 let results = self.multi_get_pinned(keys)?;
1370 let values_parsed: Result<Vec<_>, TypedStoreError> = results
1371 .into_iter()
1372 .map(|value_byte| match value_byte {
1373 Some(data) => Ok(Some(
1374 bcs::from_bytes(&data).map_err(typed_store_err_from_bcs_err)?,
1375 )),
1376 None => Ok(None),
1377 })
1378 .collect();
1379
1380 values_parsed
1381 }
1382}
1383
1384impl<'a, K, V> Map<'a, K, V> for DBMap<K, V>
1385where
1386 K: Serialize + DeserializeOwned,
1387 V: Serialize + DeserializeOwned,
1388{
1389 type Error = TypedStoreError;
1390
1391 fn contains_key(&self, key: &K) -> Result<bool, TypedStoreError> {
1392 self.contains_raw_key(be_fix_int_ser(key))
1393 }
1394
1395 fn multi_contains_keys<J>(
1396 &self,
1397 keys: impl IntoIterator<Item = J>,
1398 ) -> Result<Vec<bool>, Self::Error>
1399 where
1400 J: Borrow<K>,
1401 {
1402 self.multi_contains_raw_keys(keys.into_iter().map(|k| be_fix_int_ser(k.borrow())))
1403 }
1404
1405 fn get(&self, key: &K) -> Result<Option<V>, TypedStoreError> {
1406 self.get_raw_key(be_fix_int_ser(key))
1407 }
1408
1409 fn insert(&self, key: &K, value: &V) -> Result<(), TypedStoreError> {
1410 self.insert_raw_key(be_fix_int_ser(key), value)
1411 }
1412
1413 fn remove(&self, key: &K) -> Result<(), TypedStoreError> {
1414 self.remove_raw_key(be_fix_int_ser(key))
1415 }
1416
1417 #[instrument(level = "trace", skip_all, err)]
1421 fn schedule_delete_all(&self) -> Result<(), TypedStoreError> {
1422 let Some(last_key) = self
1423 .safe_range_iter_reversed(..)
1424 .next()
1425 .transpose()?
1426 .map(|(k, _v)| k)
1427 else {
1428 return Ok(());
1429 };
1430 let mut to = be_fix_int_ser(&last_key);
1433 to.push(0);
1434 let mut batch = self.batch();
1437 batch.schedule_delete_range_raw(self, Vec::new(), to)?;
1438 batch.write()
1439 }
1440
1441 fn is_empty(&self) -> bool {
1442 self.safe_iter().next().is_none()
1443 }
1444
1445 fn safe_iter(&'a self) -> DbIterator<'a, (K, V)> {
1446 match &self.db.storage {
1447 Storage::Rocks(db) => {
1448 let db_iter = db.underlying.raw_iterator_cf_opt(
1449 &rocks_cf(db, self.column_family.name()),
1450 self.opts.readopts(),
1451 );
1452 let (_timer, bytes_scanned, keys_scanned, _perf_ctx) = self.create_iter_context();
1453 Box::new(SafeIter::new(
1454 self.cf_name().to_string(),
1455 db_iter,
1456 _timer,
1457 _perf_ctx,
1458 bytes_scanned,
1459 keys_scanned,
1460 Some(self.db_metrics.clone()),
1461 ))
1462 }
1463 Storage::InMemory(db) => db.iterator(self.column_family.name(), None, None, false),
1464 }
1465 }
1466
1467 fn safe_iter_with_bounds(
1468 &'a self,
1469 lower_bound: Option<K>,
1470 upper_bound: Option<K>,
1471 ) -> DbIterator<'a, (K, V)> {
1472 let range = (
1473 lower_bound.map(Bound::Included).unwrap_or(Bound::Unbounded),
1474 upper_bound.map(Bound::Excluded).unwrap_or(Bound::Unbounded),
1475 );
1476 self.safe_range_iter(range)
1477 }
1478
1479 fn safe_range_iter(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
1480 let (lower_bound, upper_bound) = iterator_bounds_with_range(range);
1481 self.iter_forward_raw(lower_bound, upper_bound)
1482 }
1483
1484 fn safe_range_iter_reversed(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
1488 let (lower_bound, upper_bound) = iterator_bounds_with_range(range);
1489 self.iter_reversed_raw(lower_bound, upper_bound)
1490 }
1491
1492 fn multi_get<J>(
1494 &self,
1495 keys: impl IntoIterator<Item = J>,
1496 ) -> Result<Vec<Option<V>>, TypedStoreError>
1497 where
1498 J: Borrow<K>,
1499 {
1500 self.multi_get_raw_keys(keys.into_iter().map(|k| be_fix_int_ser(k.borrow())))
1501 }
1502
1503 #[instrument(level = "trace", skip_all, err)]
1505 fn multi_insert<J, U>(
1506 &self,
1507 key_val_pairs: impl IntoIterator<Item = (J, U)>,
1508 ) -> Result<(), Self::Error>
1509 where
1510 J: Borrow<K>,
1511 U: Borrow<V>,
1512 {
1513 let mut batch = self.batch();
1514 batch.insert_batch(self, key_val_pairs)?;
1515 batch.write()
1516 }
1517
1518 #[instrument(level = "trace", skip_all, err)]
1520 fn multi_remove<J>(&self, keys: impl IntoIterator<Item = J>) -> Result<(), Self::Error>
1521 where
1522 J: Borrow<K>,
1523 {
1524 let mut batch = self.batch();
1525 batch.delete_batch(self, keys)?;
1526 batch.write()
1527 }
1528
1529 #[instrument(level = "trace", skip_all, err)]
1531 fn try_catch_up_with_primary(&self) -> Result<(), Self::Error> {
1532 self.db.try_catch_up_with_primary()
1533 }
1534}
1535
1536pub struct TaggedDBMap<K, V> {
1589 tag: u8,
1590 map: DBMap<(u8, K), V>,
1591}
1592
1593impl<K, V> TaggedDBMap<K, V> {
1594 fn strip_tag(row: Result<((u8, K), V), TypedStoreError>) -> Result<(K, V), TypedStoreError> {
1597 row.map(|((_, key), value)| (key, value))
1598 }
1599
1600 pub fn reopen(
1603 db: &Arc<Database>,
1604 cf_name: &str,
1605 tag: u8,
1606 rw_options: &ReadWriteOptions,
1607 skip_metrics_reporting: bool,
1608 ) -> Result<Self, TypedStoreError> {
1609 Ok(Self {
1610 tag,
1611 map: DBMap::reopen(db, Some(cf_name), rw_options, skip_metrics_reporting)?,
1612 })
1613 }
1614
1615 pub fn batch(&self) -> DBBatch {
1617 self.map.batch()
1618 }
1619}
1620
1621impl<'a, K, V> Map<'a, K, V> for TaggedDBMap<K, V>
1622where
1623 K: Serialize + DeserializeOwned,
1624 V: Serialize + DeserializeOwned,
1625{
1626 type Error = TypedStoreError;
1627
1628 fn contains_key(&self, key: &K) -> Result<bool, TypedStoreError> {
1629 self.map.contains_raw_key(be_fix_int_ser(&(self.tag, key)))
1630 }
1631
1632 fn multi_contains_keys<J>(
1633 &self,
1634 keys: impl IntoIterator<Item = J>,
1635 ) -> Result<Vec<bool>, TypedStoreError>
1636 where
1637 J: Borrow<K>,
1638 {
1639 self.map.multi_contains_raw_keys(
1640 keys.into_iter()
1641 .map(|key| be_fix_int_ser(&(self.tag, key.borrow()))),
1642 )
1643 }
1644
1645 fn get(&self, key: &K) -> Result<Option<V>, TypedStoreError> {
1646 self.map.get_raw_key(be_fix_int_ser(&(self.tag, key)))
1647 }
1648
1649 fn multi_get<J>(
1650 &self,
1651 keys: impl IntoIterator<Item = J>,
1652 ) -> Result<Vec<Option<V>>, TypedStoreError>
1653 where
1654 J: Borrow<K>,
1655 {
1656 self.map.multi_get_raw_keys(
1657 keys.into_iter()
1658 .map(|key| be_fix_int_ser(&(self.tag, key.borrow()))),
1659 )
1660 }
1661
1662 fn insert(&self, key: &K, value: &V) -> Result<(), TypedStoreError> {
1663 self.map
1664 .insert_raw_key(be_fix_int_ser(&(self.tag, key)), value)
1665 }
1666
1667 fn remove(&self, key: &K) -> Result<(), TypedStoreError> {
1668 self.map.remove_raw_key(be_fix_int_ser(&(self.tag, key)))
1669 }
1670
1671 #[instrument(level = "trace", skip_all, err)]
1672 fn multi_insert<J, U>(
1673 &self,
1674 key_val_pairs: impl IntoIterator<Item = (J, U)>,
1675 ) -> Result<(), TypedStoreError>
1676 where
1677 J: Borrow<K>,
1678 U: Borrow<V>,
1679 {
1680 let mut batch = self.batch();
1681 batch.insert_batch_tagged(self, key_val_pairs)?;
1682 batch.write()
1683 }
1684
1685 #[instrument(level = "trace", skip_all, err)]
1686 fn multi_remove<J>(&self, keys: impl IntoIterator<Item = J>) -> Result<(), TypedStoreError>
1687 where
1688 J: Borrow<K>,
1689 {
1690 let mut batch = self.batch();
1691 batch.delete_batch_tagged(self, keys)?;
1692 batch.write()
1693 }
1694
1695 #[instrument(level = "trace", skip_all, err)]
1701 fn schedule_delete_all(&self) -> Result<(), TypedStoreError> {
1702 let from = be_fix_int_ser(&self.tag);
1703 let to = match prefix_iterator_bounds(&self.tag).1 {
1704 Some(to) => {
1705 if self.is_empty() {
1706 return Ok(());
1707 }
1708 to
1709 }
1710 None => match self.safe_range_iter_reversed(..).next().transpose()? {
1715 Some((last_key, _)) => {
1716 let mut to = be_fix_int_ser(&(self.tag, last_key));
1717 to.push(0);
1718 to
1719 }
1720 None => return Ok(()),
1721 },
1722 };
1723
1724 let mut batch = self.batch();
1725 batch.schedule_delete_range_raw(&self.map, from, to)?;
1726 batch.write()
1727 }
1728
1729 fn is_empty(&self) -> bool {
1730 self.safe_iter().next().is_none()
1731 }
1732
1733 fn safe_iter(&'a self) -> DbIterator<'a, (K, V)> {
1734 Box::new(
1735 self.map
1736 .safe_iter_with_prefix(&self.tag)
1737 .map(Self::strip_tag),
1738 )
1739 }
1740
1741 fn safe_iter_with_bounds(
1742 &'a self,
1743 lower_bound: Option<K>,
1744 upper_bound: Option<K>,
1745 ) -> DbIterator<'a, (K, V)> {
1746 let range = (
1747 lower_bound.map(Bound::Included).unwrap_or(Bound::Unbounded),
1748 upper_bound.map(Bound::Excluded).unwrap_or(Bound::Unbounded),
1749 );
1750 self.safe_range_iter(range)
1751 }
1752
1753 fn safe_range_iter(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
1754 let (lower_bound, upper_bound) = prefix_iterator_bounds_with_range(&self.tag, range);
1755 Box::new(
1756 self.map
1757 .iter_forward_raw(lower_bound, upper_bound)
1758 .map(Self::strip_tag),
1759 )
1760 }
1761
1762 fn safe_range_iter_reversed(&'a self, range: impl RangeBounds<K>) -> DbIterator<'a, (K, V)> {
1763 let (lower_bound, upper_bound) = prefix_iterator_bounds_with_range(&self.tag, range);
1764 Box::new(
1765 self.map
1766 .iter_reversed_raw(lower_bound, upper_bound)
1767 .map(Self::strip_tag),
1768 )
1769 }
1770
1771 fn try_catch_up_with_primary(&self) -> Result<(), TypedStoreError> {
1772 self.map.try_catch_up_with_primary()
1773 }
1774}
1775
1776fn default_hash(value: &[u8]) -> Digest<32> {
1777 let mut hasher = fastcrypto::hash::Blake2b256::default();
1778 hasher.update(value);
1779 hasher.finalize()
1780}
1781
1782const THROUGHPUT_FLOOR_BYTES_PER_SEC: f64 = 32.0 * 1024.0 * 1024.0; const MIN_VERY_SLOW_BATCH_WRITE_SECS: f64 = 1.0;
1788
1789fn very_slow_batch_write_threshold_secs(batch_size_bytes: usize) -> f64 {
1794 (batch_size_bytes as f64 / THROUGHPUT_FLOOR_BYTES_PER_SEC).max(MIN_VERY_SLOW_BATCH_WRITE_SECS)
1795}
1796
1797#[cfg(test)]
1798mod tests {
1799 use super::very_slow_batch_write_threshold_secs;
1800
1801 const MIB: usize = 1024 * 1024;
1802
1803 #[test]
1804 fn very_slow_batch_write_threshold_never_below_one_second() {
1805 assert_eq!(very_slow_batch_write_threshold_secs(0), 1.0);
1806 assert_eq!(very_slow_batch_write_threshold_secs(1), 1.0);
1807 assert_eq!(very_slow_batch_write_threshold_secs(MIB), 1.0);
1808 assert_eq!(very_slow_batch_write_threshold_secs(32 * MIB), 1.0);
1809 }
1810
1811 #[test]
1812 fn very_slow_batch_write_threshold_grows_with_batch_size() {
1813 assert_eq!(very_slow_batch_write_threshold_secs(48 * MIB), 1.5);
1814 assert_eq!(very_slow_batch_write_threshold_secs(64 * MIB), 2.0);
1815 assert_eq!(very_slow_batch_write_threshold_secs(320 * MIB), 10.0);
1816 }
1817}