1use super::*;
5use crate::authority::authority_store::LockDetailsWrapperDeprecated;
6#[cfg(tidehunter)]
7use crate::authority::epoch_marker_key::EPOCH_MARKER_KEY_SIZE;
8use crate::authority::epoch_marker_key::EpochMarkerKey;
9use serde::{Deserialize, Serialize};
10use std::path::Path;
11use std::sync::atomic::AtomicU64;
12use sui_types::base_types::SequenceNumber;
13use sui_types::effects::{TransactionEffects, TransactionEvents};
14use sui_types::global_state_hash::GlobalStateHash;
15use sui_types::storage::MarkerValue;
16use typed_store::metrics::SamplingInterval;
17use typed_store::rocks::{DBBatch, DBMap, MetricConf};
18#[cfg(not(tidehunter))]
19use typed_store::rocks::{DBMapTableConfigMap, DBOptions, default_db_options, read_size_from_env};
20use typed_store::traits::Map;
21
22use crate::authority::authority_store_types::{
23 StoreObject, StoreObjectValue, StoreObjectWrapper, get_store_object, try_construct_object,
24};
25use crate::authority::epoch_start_configuration::EpochStartConfiguration;
26use typed_store::{DBMapUtils, DbIterator};
27
28#[cfg(not(tidehunter))]
29const ENV_VAR_OBJECTS_BLOCK_CACHE_SIZE: &str = "OBJECTS_BLOCK_CACHE_MB";
30#[cfg(not(tidehunter))]
31pub(crate) const ENV_VAR_LOCKS_BLOCK_CACHE_SIZE: &str = "LOCKS_BLOCK_CACHE_MB";
32#[cfg(not(tidehunter))]
33const ENV_VAR_TRANSACTIONS_BLOCK_CACHE_SIZE: &str = "TRANSACTIONS_BLOCK_CACHE_MB";
34#[cfg(not(tidehunter))]
35const ENV_VAR_EFFECTS_BLOCK_CACHE_SIZE: &str = "EFFECTS_BLOCK_CACHE_MB";
36
37#[derive(Default)]
39pub struct AuthorityPerpetualTablesOptions {
40 pub enable_write_stall: bool,
42 pub enable_objects_compactor: bool,
46}
47
48impl AuthorityPerpetualTablesOptions {
49 #[cfg(not(tidehunter))]
50 fn apply_to(&self, mut db_options: DBOptions) -> DBOptions {
51 if !self.enable_write_stall {
52 db_options = db_options.disable_write_throttling();
53 }
54 db_options
55 }
56}
57
58#[derive(DBMapUtils)]
60#[cfg_attr(tidehunter, tidehunter)]
61pub struct AuthorityPerpetualTables {
62 pub(crate) objects: DBMap<ObjectKey, StoreObjectWrapper>,
75
76 #[rename = "owned_object_transaction_locks"]
81 pub(crate) live_owned_object_markers: DBMap<ObjectRef, Option<LockDetailsWrapperDeprecated>>,
82
83 pub(crate) transactions: DBMap<TransactionDigest, TrustedTransaction>,
87
88 pub(crate) effects: DBMap<TransactionEffectsDigest, TransactionEffects>,
97
98 pub(crate) executed_effects: DBMap<TransactionDigest, TransactionEffectsDigest>,
103
104 pub(crate) events_2: DBMap<TransactionDigest, TransactionEvents>,
106
107 pub(crate) unchanged_loaded_runtime_objects: DBMap<TransactionDigest, Vec<ObjectKey>>,
109
110 pub(crate) executed_transactions_to_checkpoint:
114 DBMap<TransactionDigest, (EpochId, CheckpointSequenceNumber)>,
115
116 pub(crate) root_state_hash_by_epoch:
120 DBMap<EpochId, (CheckpointSequenceNumber, GlobalStateHash)>,
121
122 pub(crate) epoch_start_configuration: DBMap<(), EpochStartConfiguration>,
124
125 pub(crate) pruned_checkpoint: DBMap<(), CheckpointSequenceNumber>,
127
128 pub(crate) expected_network_sui_amount: DBMap<(), u64>,
133
134 pub(crate) expected_storage_fund_imbalance: DBMap<(), i64>,
138
139 pub(crate) object_per_epoch_marker_table: DBMap<(EpochId, ObjectKey), MarkerValue>,
145 pub(crate) object_per_epoch_marker_table_v2: DBMap<EpochMarkerKey, MarkerValue>,
146
147 pub(crate) executed_transaction_digests: DBMap<(EpochId, TransactionDigest), ()>,
151
152 pub(crate) highest_committed_checkpoint: DBMap<(), CheckpointSequenceNumber>,
168}
169
170impl AuthorityPerpetualTables {
171 pub fn path(parent_path: &Path) -> PathBuf {
172 parent_path.join("perpetual")
173 }
174
175 #[cfg(not(tidehunter))]
176 pub fn open(
177 parent_path: &Path,
178 db_options_override: Option<AuthorityPerpetualTablesOptions>,
179 _pruner_watermark: Option<Arc<AtomicU64>>,
180 ) -> Self {
181 let db_options_override = db_options_override.unwrap_or_default();
182 let db_options = db_options_override
183 .apply_to(default_db_options().optimize_db_for_write_throughput(4, false));
184 let table_options = DBMapTableConfigMap::new(BTreeMap::from([
185 (
186 "objects".to_string(),
187 objects_table_config(db_options.clone()),
188 ),
189 (
190 "owned_object_transaction_locks".to_string(),
191 owned_object_transaction_locks_table_config(db_options.clone()),
192 ),
193 (
194 "transactions".to_string(),
195 transactions_table_config(db_options.clone()),
196 ),
197 (
198 "effects".to_string(),
199 effects_table_config(db_options.clone()),
200 ),
201 ]));
202
203 Self::open_tables_read_write(
204 Self::path(parent_path),
205 MetricConf::new("perpetual")
206 .with_sampling(SamplingInterval::new(Duration::from_secs(60), 0)),
207 Some(db_options.options),
208 Some(table_options),
209 )
210 }
211
212 #[cfg(tidehunter)]
213 pub fn open(
214 parent_path: &Path,
215 db_options_override: Option<AuthorityPerpetualTablesOptions>,
216 pruner_watermark: Option<Arc<AtomicU64>>,
217 ) -> Self {
218 use crate::authority::authority_store_pruner::apply_relocation_filter;
219 tracing::warn!("AuthorityPerpetualTables using tidehunter");
220 use typed_store::tidehunter_util::{
221 Bytes, Decision, KeyIndexing, KeySpaceConfig, KeyType, ThConfig,
222 default_cells_per_mutex, default_max_dirty_keys, default_mutex_count,
223 default_value_cache_size,
224 };
225 let mutexes = default_mutex_count() * 2;
226 let transaction_mutexes = mutexes * 4;
227 let value_cache_size = default_value_cache_size();
228 let pruner_watermark = pruner_watermark.unwrap_or(Arc::new(AtomicU64::new(0)));
230
231 let bloom_config = KeySpaceConfig::new().with_bloom_filter(0.001, 32_000);
232 let objects_compactor = |iter: &mut dyn DoubleEndedIterator<Item = &Bytes>| {
233 let mut retain = HashSet::new();
234 let mut previous: Option<&[u8]> = None;
235 const OID_SIZE: usize = 32;
236 for key in iter.rev() {
237 if let Some(prev) = previous
238 && prev == &key[..OID_SIZE]
239 {
240 continue;
241 }
242 previous = Some(&key[..OID_SIZE]);
243 retain.insert(key.clone());
244 }
245 retain
246 };
247 let mut digest_prefix = vec![0; 8];
248 digest_prefix[7] = 32;
249 let uniform_key = KeyType::uniform(default_cells_per_mutex());
250 let epoch_prefix_key = KeyType::from_prefix_bits(9 * 8 + 4);
251 let epoch_tx_digest_prefix_key =
253 KeyType::from_prefix_bits((8+ 8) * 8 + 12);
254 let object_indexing = KeyIndexing::fixed(32 + 8); let obj_ref_size = 32 + 8 + 32 + 8;
257 let owned_object_transaction_locks_indexing =
258 KeyIndexing::key_reduction(obj_ref_size, 16..(obj_ref_size - 16));
259
260 let mut objects_config = KeySpaceConfig::new()
261 .with_max_dirty_keys(16 * default_max_dirty_keys())
262 .with_value_cache_size(value_cache_size);
263 if matches!(db_options_override, Some(options) if options.enable_objects_compactor) {
264 objects_config = objects_config.with_compactor(Box::new(objects_compactor));
265 }
266
267 let configs = vec![
268 (
269 "objects".to_string(),
270 ThConfig::new_with_config_indexing(
271 object_indexing,
272 mutexes * 4,
273 KeyType::uniform(1),
274 objects_config,
275 ),
276 ),
277 (
278 "owned_object_transaction_locks".to_string(),
279 ThConfig::new_with_config_indexing(
280 owned_object_transaction_locks_indexing,
281 mutexes * 16,
282 KeyType::uniform(default_cells_per_mutex()),
283 bloom_config
284 .clone()
285 .with_max_dirty_keys(16 * default_max_dirty_keys()),
286 ),
287 ),
288 (
289 "transactions".to_string(),
290 ThConfig::new_with_rm_prefix_indexing(
291 KeyIndexing::key_reduction(32, 0..16),
292 transaction_mutexes,
293 uniform_key,
294 KeySpaceConfig::new()
295 .with_value_cache_size(value_cache_size)
296 .with_relocation_filter(|_, _| Decision::Remove),
297 digest_prefix.clone(),
298 ),
299 ),
300 (
301 "effects".to_string(),
302 ThConfig::new_with_rm_prefix_indexing(
303 KeyIndexing::key_reduction(32, 0..16),
304 transaction_mutexes,
305 uniform_key,
306 apply_relocation_filter(
307 bloom_config.clone().with_value_cache_size(value_cache_size),
308 pruner_watermark.clone(),
309 |effects: TransactionEffects| effects.executed_epoch(),
310 false,
311 ),
312 digest_prefix.clone(),
313 ),
314 ),
315 (
316 "executed_effects".to_string(),
317 ThConfig::new_with_rm_prefix_indexing(
318 KeyIndexing::key_reduction(32, 0..16),
319 transaction_mutexes,
320 uniform_key,
321 bloom_config
322 .clone()
323 .with_value_cache_size(value_cache_size)
324 .with_relocation_filter(|_, _| Decision::Remove),
325 digest_prefix.clone(),
326 ),
327 ),
328 (
329 "events".to_string(),
330 ThConfig::new_with_rm_prefix(
331 32 + 8,
332 mutexes,
333 uniform_key,
334 KeySpaceConfig::default().with_relocation_filter(|_, _| Decision::Remove),
335 digest_prefix.clone(),
336 ),
337 ),
338 (
339 "events_2".to_string(),
340 ThConfig::new_with_rm_prefix(
341 32,
342 mutexes,
343 uniform_key,
344 KeySpaceConfig::default().with_relocation_filter(|_, _| Decision::Remove),
345 digest_prefix.clone(),
346 ),
347 ),
348 (
349 "unchanged_loaded_runtime_objects".to_string(),
350 ThConfig::new_with_rm_prefix(
351 32,
352 mutexes,
353 uniform_key,
354 KeySpaceConfig::default().with_relocation_filter(|_, _| Decision::Remove),
355 digest_prefix.clone(),
356 ),
357 ),
358 (
359 "executed_transactions_to_checkpoint".to_string(),
360 ThConfig::new_with_rm_prefix(
361 32,
362 mutexes,
363 uniform_key,
364 apply_relocation_filter(
365 KeySpaceConfig::default(),
366 pruner_watermark.clone(),
367 |(epoch_id, _): (EpochId, CheckpointSequenceNumber)| epoch_id,
368 false,
369 ),
370 digest_prefix.clone(),
371 ),
372 ),
373 (
374 "root_state_hash_by_epoch".to_string(),
375 ThConfig::new(8, 1, KeyType::uniform(1)),
376 ),
377 (
378 "epoch_start_configuration".to_string(),
379 ThConfig::new(0, 1, KeyType::uniform(1)),
380 ),
381 (
382 "pruned_checkpoint".to_string(),
383 ThConfig::new(0, 1, KeyType::uniform(1)),
384 ),
385 (
386 "expected_network_sui_amount".to_string(),
387 ThConfig::new(0, 1, KeyType::uniform(1)),
388 ),
389 (
390 "expected_storage_fund_imbalance".to_string(),
391 ThConfig::new(0, 1, KeyType::uniform(1)),
392 ),
393 (
394 "object_per_epoch_marker_table".to_string(),
395 ThConfig::new_with_config_indexing(
396 KeyIndexing::VariableLength,
397 mutexes,
398 epoch_prefix_key,
399 apply_relocation_filter(
400 KeySpaceConfig::default(),
401 pruner_watermark.clone(),
402 |(epoch_id, _): (EpochId, ObjectKey)| epoch_id,
403 true,
404 ),
405 ),
406 ),
407 (
408 "object_per_epoch_marker_table_v2".to_string(),
409 ThConfig::new_with_config_indexing(
410 KeyIndexing::fixed(EPOCH_MARKER_KEY_SIZE),
411 mutexes,
412 epoch_prefix_key,
413 apply_relocation_filter(
414 bloom_config.clone(),
415 pruner_watermark.clone(),
416 |k: EpochMarkerKey| k.0,
417 true,
418 ),
419 ),
420 ),
421 (
422 "executed_transaction_digests".to_string(),
423 ThConfig::new_with_config_indexing(
424 KeyIndexing::fixed(8 + (32 + 8)),
426 transaction_mutexes,
427 epoch_tx_digest_prefix_key,
428 apply_relocation_filter(
429 bloom_config.clone(),
430 pruner_watermark.clone(),
431 |(epoch_id, _): (EpochId, TransactionDigest)| epoch_id,
432 true,
433 ),
434 ),
435 ),
436 (
437 "highest_committed_checkpoint".to_string(),
438 ThConfig::new(0, 1, KeyType::uniform(1)),
439 ),
440 ];
441 Self::open_tables_read_write(
442 Self::path(parent_path),
443 MetricConf::new("perpetual")
444 .with_sampling(SamplingInterval::new(Duration::from_secs(60), 0)),
445 configs.into_iter().collect(),
446 )
447 }
448
449 #[cfg(not(tidehunter))]
450 pub fn open_readonly(parent_path: &Path) -> AuthorityPerpetualTablesReadOnly {
451 Self::get_read_only_handle(
452 Self::path(parent_path),
453 None,
454 None,
455 MetricConf::new("perpetual_readonly"),
456 )
457 }
458
459 #[cfg(tidehunter)]
460 pub fn open_readonly(parent_path: &Path) -> Self {
461 Self::open(parent_path, None, None)
462 }
463
464 #[cfg(tidehunter)]
465 pub fn force_rebuild_control_region(&self) -> anyhow::Result<()> {
466 self.objects.db.force_rebuild_control_region()
467 }
468
469 #[cfg(tidehunter)]
474 pub fn wait_for_tidehunter_background_threads(self: Arc<Self>) {
475 let strong = Arc::strong_count(&self);
476 if strong != 1 {
477 println!(
478 "WARNING: wait_for_tidehunter_background_threads called with Arc<AuthorityPerpetualTables> strong_count={} (expected 1); other clones will keep DBMap.db Arc<Database> alive past drop(self) and the inner Database wait will warn/timeout",
479 strong,
480 );
481 }
482 let db = self.objects.db.clone();
483 drop(self);
484 db.wait_for_tidehunter_background_threads();
485 }
486
487 pub fn find_object_lt_or_eq_version(
491 &self,
492 object_id: ObjectID,
493 version: SequenceNumber,
494 ) -> SuiResult<Option<Object>> {
495 let mut iter = self.objects.reversed_safe_iter_with_bounds(
496 Some(ObjectKey::min_for_id(&object_id)),
497 Some(ObjectKey(object_id, version)),
498 )?;
499 match iter.next() {
500 Some(Ok((key, o))) => self.object(&key, o),
501 Some(Err(e)) => Err(e.into()),
502 None => Ok(None),
503 }
504 }
505
506 fn construct_object(
507 &self,
508 object_key: &ObjectKey,
509 store_object: StoreObjectValue,
510 ) -> Result<Object, SuiError> {
511 try_construct_object(object_key, store_object)
512 }
513
514 pub fn object(
517 &self,
518 object_key: &ObjectKey,
519 store_object: StoreObjectWrapper,
520 ) -> Result<Option<Object>, SuiError> {
521 let StoreObject::Value(store_object) = store_object.migrate().into_inner() else {
522 return Ok(None);
523 };
524 Ok(Some(self.construct_object(object_key, *store_object)?))
525 }
526
527 pub fn object_reference(
528 &self,
529 object_key: &ObjectKey,
530 store_object: StoreObjectWrapper,
531 ) -> Result<ObjectRef, SuiError> {
532 let obj_ref = match store_object.migrate().into_inner() {
533 StoreObject::Value(object) => self
534 .construct_object(object_key, *object)?
535 .compute_object_reference(),
536 StoreObject::Deleted => (
537 object_key.0,
538 object_key.1,
539 ObjectDigest::OBJECT_DIGEST_DELETED,
540 ),
541 StoreObject::Wrapped => (
542 object_key.0,
543 object_key.1,
544 ObjectDigest::OBJECT_DIGEST_WRAPPED,
545 ),
546 };
547 Ok(obj_ref)
548 }
549
550 pub fn tombstone_reference(
551 &self,
552 object_key: &ObjectKey,
553 store_object: &StoreObjectWrapper,
554 ) -> Result<Option<ObjectRef>, SuiError> {
555 let obj_ref = match store_object.inner() {
556 StoreObject::Deleted => Some((
557 object_key.0,
558 object_key.1,
559 ObjectDigest::OBJECT_DIGEST_DELETED,
560 )),
561 StoreObject::Wrapped => Some((
562 object_key.0,
563 object_key.1,
564 ObjectDigest::OBJECT_DIGEST_WRAPPED,
565 )),
566 _ => None,
567 };
568 Ok(obj_ref)
569 }
570
571 pub fn get_latest_object_ref_or_tombstone(
572 &self,
573 object_id: ObjectID,
574 ) -> Result<Option<ObjectRef>, SuiError> {
575 let mut iterator = self.objects.reversed_safe_iter_with_bounds(
576 Some(ObjectKey::min_for_id(&object_id)),
577 Some(ObjectKey::max_for_id(&object_id)),
578 )?;
579
580 if let Some(Ok((object_key, value))) = iterator.next()
581 && object_key.0 == object_id
582 {
583 return Ok(Some(self.object_reference(&object_key, value)?));
584 }
585 Ok(None)
586 }
587
588 pub fn get_latest_object_or_tombstone(
589 &self,
590 object_id: ObjectID,
591 ) -> Result<Option<(ObjectKey, StoreObjectWrapper)>, SuiError> {
592 let mut iterator = self.objects.reversed_safe_iter_with_bounds(
593 Some(ObjectKey::min_for_id(&object_id)),
594 Some(ObjectKey::max_for_id(&object_id)),
595 )?;
596
597 if let Some(Ok((object_key, value))) = iterator.next()
598 && object_key.0 == object_id
599 {
600 return Ok(Some((object_key, value)));
601 }
602 Ok(None)
603 }
604
605 pub fn get_recovery_epoch_at_restart(&self) -> SuiResult<EpochId> {
606 Ok(self
607 .epoch_start_configuration
608 .get(&())?
609 .expect("Must have current epoch.")
610 .epoch_start_state()
611 .epoch())
612 }
613
614 pub fn set_epoch_start_configuration(
615 &self,
616 epoch_start_configuration: &EpochStartConfiguration,
617 ) -> SuiResult {
618 let mut wb = self.epoch_start_configuration.batch();
619 wb.insert_batch(
620 &self.epoch_start_configuration,
621 std::iter::once(((), epoch_start_configuration)),
622 )?;
623 wb.write()?;
624 Ok(())
625 }
626
627 pub fn get_highest_pruned_checkpoint(
628 &self,
629 ) -> Result<Option<CheckpointSequenceNumber>, TypedStoreError> {
630 self.pruned_checkpoint.get(&())
631 }
632
633 pub fn set_highest_pruned_checkpoint(
634 &self,
635 wb: &mut DBBatch,
636 checkpoint_number: CheckpointSequenceNumber,
637 ) -> SuiResult {
638 wb.insert_batch(&self.pruned_checkpoint, [((), checkpoint_number)])?;
639 Ok(())
640 }
641
642 pub fn get_highest_committed_checkpoint(
643 &self,
644 ) -> Result<Option<CheckpointSequenceNumber>, TypedStoreError> {
645 self.highest_committed_checkpoint.get(&())
646 }
647
648 pub fn set_highest_committed_checkpoint(
652 &self,
653 wb: &mut DBBatch,
654 checkpoint_number: CheckpointSequenceNumber,
655 ) -> SuiResult {
656 wb.insert_batch(
657 &self.highest_committed_checkpoint,
658 [((), checkpoint_number)],
659 )?;
660 Ok(())
661 }
662
663 pub fn get_transaction(
664 &self,
665 digest: &TransactionDigest,
666 ) -> SuiResult<Option<TrustedTransaction>> {
667 let Some(transaction) = self.transactions.get(digest)? else {
668 return Ok(None);
669 };
670 Ok(Some(transaction))
671 }
672
673 pub fn list_transactions_from(
674 &self,
675 start: Option<TransactionDigest>,
676 limit: usize,
677 ) -> Result<Vec<TransactionDigest>, typed_store::TypedStoreError> {
678 let iter = self.transactions.safe_iter_with_bounds(start, None);
679 let mut result = Vec::with_capacity(limit);
680 for item in iter.take(limit) {
681 let (digest, _) = item?;
682 result.push(digest);
683 }
684 Ok(result)
685 }
686
687 pub fn get_executed_effects_digest(
688 &self,
689 tx_digest: &TransactionDigest,
690 ) -> Result<Option<TransactionEffectsDigest>, typed_store::TypedStoreError> {
691 self.executed_effects.get(tx_digest)
692 }
693
694 pub fn get_effects_by_digest(
695 &self,
696 effects_digest: &TransactionEffectsDigest,
697 ) -> Result<Option<TransactionEffects>, typed_store::TypedStoreError> {
698 self.effects.get(effects_digest)
699 }
700
701 pub fn insert_executed_transaction_digests_batch(
704 &self,
705 epoch: EpochId,
706 digests: impl Iterator<Item = TransactionDigest>,
707 ) -> SuiResult {
708 let mut batch = self.executed_transaction_digests.batch();
709 batch.insert_batch(
710 &self.executed_transaction_digests,
711 digests.map(|digest| ((epoch, digest), ())),
712 )?;
713 batch.write()?;
714 Ok(())
715 }
716
717 pub fn get_effects(&self, digest: &TransactionDigest) -> SuiResult<Option<TransactionEffects>> {
718 let Some(effect_digest) = self.executed_effects.get(digest)? else {
719 return Ok(None);
720 };
721 Ok(self.effects.get(&effect_digest)?)
722 }
723
724 pub(crate) fn was_transaction_executed_in_last_epoch(
725 &self,
726 digest: &TransactionDigest,
727 current_epoch: EpochId,
728 ) -> bool {
729 if current_epoch == 0 {
730 return false;
731 }
732 self.executed_transaction_digests
733 .contains_key(&(current_epoch - 1, *digest))
734 .expect("db error")
735 }
736
737 pub fn get_checkpoint_sequence_number(
740 &self,
741 digest: &TransactionDigest,
742 ) -> SuiResult<Option<(EpochId, CheckpointSequenceNumber)>> {
743 Ok(self.executed_transactions_to_checkpoint.get(digest)?)
744 }
745
746 pub fn set_highest_pruned_checkpoint_without_wb(
747 &self,
748 checkpoint_number: CheckpointSequenceNumber,
749 ) -> SuiResult {
750 let mut wb = self.pruned_checkpoint.batch();
751 self.set_highest_pruned_checkpoint(&mut wb, checkpoint_number)?;
752 wb.write()?;
753 Ok(())
754 }
755
756 pub fn database_is_empty(&self) -> SuiResult<bool> {
757 Ok(self.objects.safe_iter().next().is_none())
758 }
759
760 pub fn iter_live_object_set(&self, include_wrapped_object: bool) -> LiveSetIter<'_> {
761 LiveSetIter {
762 iter: Box::new(self.objects.safe_iter()),
763 tables: self,
764 prev: None,
765 include_wrapped_object,
766 }
767 }
768
769 pub fn range_iter_live_object_set(
770 &self,
771 lower_bound: Option<ObjectID>,
772 upper_bound: Option<ObjectID>,
773 include_wrapped_object: bool,
774 ) -> LiveSetIter<'_> {
775 let lower_bound = lower_bound.as_ref().map(ObjectKey::min_for_id);
776 let upper_bound = upper_bound.as_ref().map(ObjectKey::max_for_id);
777
778 LiveSetIter {
779 iter: Box::new(self.objects.safe_iter_with_bounds(lower_bound, upper_bound)),
780 tables: self,
781 prev: None,
782 include_wrapped_object,
783 }
784 }
785
786 pub fn checkpoint_db(&self, path: &Path) -> SuiResult {
787 self.objects.checkpoint_db(path).map_err(Into::into)
789 }
790
791 pub fn insert_root_state_hash(
792 &self,
793 epoch: EpochId,
794 last_checkpoint_of_epoch: CheckpointSequenceNumber,
795 hash: GlobalStateHash,
796 ) -> SuiResult {
797 self.root_state_hash_by_epoch
798 .insert(&epoch, &(last_checkpoint_of_epoch, hash))?;
799 Ok(())
800 }
801
802 pub fn insert_object_test_only(&self, object: Object) -> SuiResult {
803 let object_reference = object.compute_object_reference();
804 let wrapper = get_store_object(object);
805 let mut wb = self.objects.batch();
806 wb.insert_batch(
807 &self.objects,
808 std::iter::once((ObjectKey::from(object_reference), wrapper)),
809 )?;
810 wb.write()?;
811 Ok(())
812 }
813
814 pub fn get_object_fallible(&self, object_id: &ObjectID) -> SuiResult<Option<Object>> {
816 let obj_entry = self
817 .objects
818 .reversed_safe_iter_with_bounds(None, Some(ObjectKey::max_for_id(object_id)))?
819 .next();
820
821 match obj_entry.transpose()? {
822 Some((ObjectKey(obj_id, version), obj)) if obj_id == *object_id => {
823 Ok(self.object(&ObjectKey(obj_id, version), obj)?)
824 }
825 _ => Ok(None),
826 }
827 }
828
829 pub fn get_object_by_key_fallible(
830 &self,
831 object_id: &ObjectID,
832 version: VersionNumber,
833 ) -> SuiResult<Option<Object>> {
834 Ok(self
835 .objects
836 .get(&ObjectKey(*object_id, version))?
837 .and_then(|object| {
838 self.object(&ObjectKey(*object_id, version), object)
839 .expect("object construction error")
840 }))
841 }
842}
843
844impl ObjectStore for AuthorityPerpetualTables {
845 fn get_object(&self, object_id: &ObjectID) -> Option<Object> {
847 self.get_object_fallible(object_id).expect("db error")
848 }
849
850 fn get_object_by_key(&self, object_id: &ObjectID, version: VersionNumber) -> Option<Object> {
851 self.get_object_by_key_fallible(object_id, version)
852 .expect("db error")
853 }
854}
855
856pub struct LiveSetIter<'a> {
857 iter: DbIterator<'a, (ObjectKey, StoreObjectWrapper)>,
858 tables: &'a AuthorityPerpetualTables,
859 prev: Option<(ObjectKey, StoreObjectWrapper)>,
860 include_wrapped_object: bool,
862}
863
864#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
865pub enum LiveObject {
866 Normal(Object),
867 Wrapped(ObjectKey),
868}
869
870impl LiveObject {
871 pub fn object_id(&self) -> ObjectID {
872 match self {
873 LiveObject::Normal(obj) => obj.id(),
874 LiveObject::Wrapped(key) => key.0,
875 }
876 }
877
878 pub fn version(&self) -> SequenceNumber {
879 match self {
880 LiveObject::Normal(obj) => obj.version(),
881 LiveObject::Wrapped(key) => key.1,
882 }
883 }
884
885 pub fn object_reference(&self) -> ObjectRef {
886 match self {
887 LiveObject::Normal(obj) => obj.compute_object_reference(),
888 LiveObject::Wrapped(key) => (key.0, key.1, ObjectDigest::OBJECT_DIGEST_WRAPPED),
889 }
890 }
891}
892
893impl LiveSetIter<'_> {
894 fn store_object_wrapper_to_live_object(
895 &self,
896 object_key: ObjectKey,
897 store_object: StoreObjectWrapper,
898 ) -> Option<LiveObject> {
899 match store_object.migrate().into_inner() {
900 StoreObject::Value(object) => {
901 let object = self
902 .tables
903 .construct_object(&object_key, *object)
904 .expect("Constructing object from store cannot fail");
905 Some(LiveObject::Normal(object))
906 }
907 StoreObject::Wrapped => {
908 if self.include_wrapped_object {
909 Some(LiveObject::Wrapped(object_key))
910 } else {
911 None
912 }
913 }
914 StoreObject::Deleted => None,
915 }
916 }
917}
918
919impl Iterator for LiveSetIter<'_> {
920 type Item = LiveObject;
921
922 fn next(&mut self) -> Option<Self::Item> {
923 loop {
924 if let Some(Ok((next_key, next_value))) = self.iter.next() {
925 let prev = self.prev.take();
926 self.prev = Some((next_key, next_value));
927
928 if let Some((prev_key, prev_value)) = prev
929 && prev_key.0 != next_key.0
930 {
931 let live_object =
932 self.store_object_wrapper_to_live_object(prev_key, prev_value);
933 if live_object.is_some() {
934 return live_object;
935 }
936 }
937 continue;
938 }
939 if let Some((key, value)) = self.prev.take() {
940 let live_object = self.store_object_wrapper_to_live_object(key, value);
941 if live_object.is_some() {
942 return live_object;
943 }
944 }
945 return None;
946 }
947 }
948}
949
950#[cfg(not(tidehunter))]
952fn owned_object_transaction_locks_table_config(db_options: DBOptions) -> DBOptions {
953 DBOptions {
954 options: db_options
955 .clone()
956 .optimize_for_write_throughput()
957 .optimize_for_read(read_size_from_env(ENV_VAR_LOCKS_BLOCK_CACHE_SIZE).unwrap_or(1024))
958 .options,
959 rw_options: db_options.rw_options.set_ignore_range_deletions(false),
960 }
961}
962
963#[cfg(not(tidehunter))]
964fn objects_table_config(db_options: DBOptions) -> DBOptions {
965 db_options
966 .optimize_for_write_throughput()
967 .optimize_for_read(read_size_from_env(ENV_VAR_OBJECTS_BLOCK_CACHE_SIZE).unwrap_or(5 * 1024))
968}
969
970#[cfg(not(tidehunter))]
971fn transactions_table_config(db_options: DBOptions) -> DBOptions {
972 db_options
973 .optimize_for_write_throughput()
974 .optimize_for_point_lookup(
975 read_size_from_env(ENV_VAR_TRANSACTIONS_BLOCK_CACHE_SIZE).unwrap_or(512),
976 )
977}
978
979#[cfg(not(tidehunter))]
980fn effects_table_config(db_options: DBOptions) -> DBOptions {
981 db_options
982 .optimize_for_write_throughput()
983 .optimize_for_point_lookup(
984 read_size_from_env(ENV_VAR_EFFECTS_BLOCK_CACHE_SIZE).unwrap_or(1024),
985 )
986}