Skip to content

Commit

Permalink
Delete remove tx command (MystenLabs#16328)
Browse files Browse the repository at this point in the history
  • Loading branch information
mystenmark authored Feb 22, 2024
1 parent d9d83a0 commit 7732e99
Show file tree
Hide file tree
Showing 2 changed files with 1 addition and 140 deletions.
49 changes: 0 additions & 49 deletions crates/sui-core/src/authority/authority_store_tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,29 +353,6 @@ impl AuthorityPerpetualTables {
Ok(objects)
}

/// Removes executed effects and outputs for a transaction,
/// and tries to ensure the transaction is replayable.
///
/// WARNING: This method is very subtle and can corrupt the database if used incorrectly.
/// It should only be used in one-off cases or tests after fully understanding the risk.
pub fn remove_executed_effects_and_outputs_subtle(
&self,
digest: &TransactionDigest,
objects: &[ObjectKey],
) -> SuiResult {
let mut wb = self.objects.batch();
for object in objects {
wb.delete_batch(&self.objects, [object])?;
if self.has_object_lock(object)? {
self.remove_object_lock_batch(&mut wb, object)?;
}
}
wb.delete_batch(&self.executed_transactions_to_checkpoint, [digest])?;
wb.delete_batch(&self.executed_effects, [digest])?;
wb.write()?;
Ok(())
}

pub fn has_object_lock(&self, object: &ObjectKey) -> SuiResult<bool> {
Ok(self
.owned_object_transaction_locks
Expand All @@ -388,32 +365,6 @@ impl AuthorityPerpetualTables {
.is_some())
}

/// Removes owned object locks and set the lock to the previous version of the object.
///
/// WARNING: This method is very subtle and can corrupt the database if used incorrectly.
/// It should only be used in one-off cases or tests after fully understanding the risk.
pub fn remove_object_lock_subtle(&self, object: &ObjectKey) -> SuiResult<ObjectRef> {
let mut wb = self.objects.batch();
let object_ref = self.remove_object_lock_batch(&mut wb, object)?;
wb.write()?;
Ok(object_ref)
}

fn remove_object_lock_batch(
&self,
wb: &mut DBBatch,
object: &ObjectKey,
) -> SuiResult<ObjectRef> {
wb.schedule_delete_range(
&self.owned_object_transaction_locks,
&(object.0, object.1, ObjectDigest::MIN),
&(object.0, object.1, ObjectDigest::MAX),
)?;
let object_ref = self.get_latest_object_ref_or_tombstone(object.0)?.unwrap();
wb.insert_batch(&self.owned_object_transaction_locks, [(object_ref, None)])?;
Ok(object_ref)
}

pub fn set_highest_pruned_checkpoint_without_wb(
&self,
checkpoint_number: CheckpointSequenceNumber,
Expand Down
92 changes: 1 addition & 91 deletions crates/sui-tool/src/db_tool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ use std::path::{Path, PathBuf};
use sui_core::authority::authority_per_epoch_store::AuthorityEpochTables;
use sui_core::authority::authority_store_tables::AuthorityPerpetualTables;
use sui_core::checkpoints::CheckpointStore;
use sui_types::base_types::{EpochId, ObjectID, SequenceNumber};
use sui_types::base_types::{EpochId, ObjectID};
use sui_types::digests::{CheckpointContentsDigest, TransactionDigest};
use sui_types::effects::TransactionEffectsAPI;
use sui_types::messages_checkpoint::{CheckpointDigest, CheckpointSequenceNumber};
use sui_types::storage::ObjectKey;
use sui_types::sui_system_state::{get_sui_system_state, SuiSystemStateTrait};
use typed_store::rocks::MetricConf;
pub mod db_dump;
mod index_search;
Expand All @@ -36,8 +34,6 @@ pub enum DbToolCommand {
PrintTransaction(PrintTransactionOptions),
PrintCheckpoint(PrintCheckpointOptions),
PrintCheckpointContent(PrintCheckpointContentOptions),
RemoveObjectLock(RemoveObjectLockOptions),
RemoveTransaction(RemoveTransactionOptions),
ResetDB,
RewindCheckpointExecution(RewindCheckpointExecutionOptions),
Compact,
Expand Down Expand Up @@ -195,8 +191,6 @@ pub async fn execute_db_tool_command(db_path: PathBuf, cmd: DbToolCommand) -> an
DbToolCommand::PrintCheckpoint(d) => print_checkpoint(&db_path, d),
DbToolCommand::PrintCheckpointContent(d) => print_checkpoint_content(&db_path, d),
DbToolCommand::ResetDB => reset_db_to_genesis(&db_path),
DbToolCommand::RemoveObjectLock(d) => remove_object_lock(&db_path, d),
DbToolCommand::RemoveTransaction(d) => remove_transaction(&db_path, d),
DbToolCommand::RewindCheckpointExecution(d) => {
rewind_checkpoint_execution(&db_path, d.epoch, d.checkpoint_sequence_number)
}
Expand Down Expand Up @@ -323,90 +317,6 @@ pub fn print_checkpoint_content(
Ok(())
}

/// Force removes a transaction and its outputs, if no other dependent transaction has executed yet.
/// Usually this should be paired with rewind_checkpoint_execution() to re-execute the removed
/// transaction, to repair corrupted database.
/// Dry run with: cargo run --package sui-tool -- db-tool --db-path /opt/sui/db/authorities_db/live remove-transaction --digest xxxx
/// Add --confirm to actually remove the transaction.
pub fn remove_transaction(path: &Path, opt: RemoveTransactionOptions) -> anyhow::Result<()> {
let perpetual_db = AuthorityPerpetualTables::open(&path.join("store"), None);
let epoch = if let Some(epoch) = opt.epoch {
epoch
} else {
get_sui_system_state(&perpetual_db)?.epoch()
};
let epoch_store = AuthorityEpochTables::open(epoch, &path.join("store"), None);
let Some(_transaction) = perpetual_db.get_transaction(&opt.digest)? else {
bail!(
"Transaction {:?} not found and cannot be re-executed!",
opt.digest
);
};
let Some(effects) = perpetual_db.get_effects(&opt.digest)? else {
bail!(
"Transaction {:?} not executed or effects have been pruned!",
opt.digest
);
};
let mut objects_to_remove = vec![];
for mutated_obj in effects.modified_at_versions() {
let new_objs = perpetual_db.get_newer_object_keys(&mutated_obj)?;
if new_objs.len() > 1 {
bail!(
"Dependents of transaction {:?} have already executed! Mutated object: {:?}, new objects: {:?}",
opt.digest,
mutated_obj,
new_objs,
);
}
objects_to_remove.extend(new_objs);
}
for (created_obj, _owner) in effects.created() {
let new_objs = perpetual_db.get_newer_object_keys(&(created_obj.0, created_obj.1))?;
if new_objs.len() > 1 {
bail!(
"Dependents of transaction {:?} have already executed! Created object: {:?}, new objects: {:?}",
opt.digest,
created_obj,
new_objs,
);
}
objects_to_remove.extend(new_objs);
}
// TODO: verify there is no newer object for read-only input, before dynamic child mvcc is implemented.
println!(
"Transaction {:?} will be removed from the database. The following output objects will be removed too:\n{:#?}",
opt.digest, objects_to_remove
);
if opt.confirm {
println!("Proceeding to remove transaction {:?} in 5s ..", opt.digest);
std::thread::sleep(std::time::Duration::from_secs(5));
perpetual_db.remove_executed_effects_and_outputs_subtle(&opt.digest, &objects_to_remove)?;
epoch_store.remove_executed_tx_subtle(&opt.digest)?;
println!("Done!");
}
Ok(())
}

pub fn remove_object_lock(path: &Path, opt: RemoveObjectLockOptions) -> anyhow::Result<()> {
let perpetual_db = AuthorityPerpetualTables::open(&path.join("store"), None);
let key = ObjectKey(opt.id, SequenceNumber::from_u64(opt.version));
if !opt.confirm && !perpetual_db.has_object_lock(&key)? {
bail!("Owned object lock for {:?} is not found!", key);
};
println!("Removing owned object lock for {:?}", key);
if opt.confirm {
println!(
"Proceeding to remove owned object lock for {:?} in 5s ..",
key
);
std::thread::sleep(std::time::Duration::from_secs(5));
let created_ref = perpetual_db.remove_object_lock_subtle(&key)?;
println!("Done! Lock is now initialized for {:?}", created_ref);
}
Ok(())
}

pub fn reset_db_to_genesis(path: &Path) -> anyhow::Result<()> {
// Follow the below steps to test:
//
Expand Down

0 comments on commit 7732e99

Please sign in to comment.