mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-03 02:38:12 +00:00
fix(scanner): own publication mutations through storage drain (#6867)
* fix(scanner): own publication mutations through storage drain Co-Authored-By: heihutu <heihutu@gmail.com> * fix(storage): remove unused rename data shim Co-Authored-By: heihutu <heihutu@gmail.com> --------- Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
@@ -3657,6 +3657,7 @@ pub(in crate::set_disk) struct RenameTailOutcome {
|
||||
pub(in crate::set_disk) struct RenameDataFenceOptions<'a> {
|
||||
write_quorum: usize,
|
||||
scanner_publication_lease_tokens: Option<&'a HashMap<String, Uuid>>,
|
||||
scanner_publication_commit_scope: Option<crate::object_api::ScannerPublicationCommitScope>,
|
||||
}
|
||||
|
||||
impl<'a> RenameDataFenceOptions<'a> {
|
||||
@@ -3667,8 +3668,17 @@ impl<'a> RenameDataFenceOptions<'a> {
|
||||
Self {
|
||||
write_quorum,
|
||||
scanner_publication_lease_tokens,
|
||||
scanner_publication_commit_scope: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::set_disk) fn with_publication_scope(
|
||||
mut self,
|
||||
scanner_publication_commit_scope: Option<crate::object_api::ScannerPublicationCommitScope>,
|
||||
) -> Self {
|
||||
self.scanner_publication_commit_scope = scanner_publication_commit_scope;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code, reason = "asserted by this file's tests (backlog#1823)")]
|
||||
@@ -3778,6 +3788,37 @@ pub(in crate::set_disk) async fn finish_rename_tail_heal<
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_scanner_publication_delete_owner<F, Fut>(
|
||||
scope: Option<crate::object_api::ScannerPublicationCommitScope>,
|
||||
operation: F,
|
||||
) -> disk::error::Result<()>
|
||||
where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: Future<Output = disk::error::Result<()>> + Send + 'static,
|
||||
{
|
||||
if scope.is_none() {
|
||||
return operation().await;
|
||||
}
|
||||
if let Some(scope) = scope.as_ref() {
|
||||
scope.attach_mutation_owner();
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
let result = operation().await;
|
||||
if let Some(scope) = scope.as_ref() {
|
||||
if result.is_ok() {
|
||||
let _ = scope.mark_committed();
|
||||
} else {
|
||||
// A failed quorum does not prove that no replica committed;
|
||||
// keep the permit indeterminate for supervisor reconciliation.
|
||||
let _ = scope.mark_indeterminate();
|
||||
}
|
||||
}
|
||||
result
|
||||
})
|
||||
.await
|
||||
.map_err(|_| DiskError::other("scanner publication delete owner failed"))?
|
||||
}
|
||||
|
||||
impl SetDisks {
|
||||
pub(in crate::set_disk) fn default_read_quorum(&self) -> usize {
|
||||
self.set_drive_count - self.default_parity_count
|
||||
@@ -3995,6 +4036,7 @@ impl SetDisks {
|
||||
let RenameDataFenceOptions {
|
||||
write_quorum,
|
||||
scanner_publication_lease_tokens,
|
||||
scanner_publication_commit_scope: _scanner_publication_commit_scope,
|
||||
} = fence_options;
|
||||
if let Some(file_info) = disks
|
||||
.iter()
|
||||
@@ -4352,6 +4394,7 @@ impl SetDisks {
|
||||
let RenameDataFenceOptions {
|
||||
write_quorum,
|
||||
scanner_publication_lease_tokens,
|
||||
scanner_publication_commit_scope,
|
||||
} = fence_options;
|
||||
if let Some(file_info) = disks
|
||||
.iter()
|
||||
@@ -4383,11 +4426,15 @@ impl SetDisks {
|
||||
let fanout_src_object = src_object.clone();
|
||||
let fanout_dst_bucket = dst_bucket.clone();
|
||||
let fanout_dst_object = dst_object.clone();
|
||||
let fanout_publication_scope = scanner_publication_commit_scope.clone();
|
||||
// Keep one coordinator task so a cancelled caller cannot drop partially
|
||||
// completed disk mutations. Per-disk futures stay ordered in `join_all`,
|
||||
// preserving slot-indexed quorum and convergence accounting without a
|
||||
// scheduler task for every disk.
|
||||
let fanout = tokio::spawn(async move {
|
||||
// Keep the storage-owned movement permit attached to the actual
|
||||
// fan-out owner, even if the caller future is cancelled.
|
||||
let _fanout_publication_scope = fanout_publication_scope;
|
||||
let successful_rename_completion_rank =
|
||||
rustfs_io_metrics::put_stage_metrics_enabled().then(|| Arc::new(AtomicUsize::new(0)));
|
||||
let futures = fanout_disks
|
||||
@@ -4401,6 +4448,7 @@ impl SetDisks {
|
||||
let dst_object = fanout_dst_object.clone();
|
||||
let dst_bucket = fanout_dst_bucket.clone();
|
||||
let successful_rename_completion_rank = successful_rename_completion_rank.clone();
|
||||
let publication_scope = scanner_publication_commit_scope.clone();
|
||||
|
||||
std::panic::AssertUnwindSafe(async move {
|
||||
// Test-only introspection guard: counts this operation as
|
||||
@@ -4433,6 +4481,13 @@ impl SetDisks {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
if let Some(scope) = publication_scope.as_ref()
|
||||
&& !scope.can_commit()
|
||||
{
|
||||
let _ = scope.mark_indeterminate();
|
||||
return Err(DiskError::other("scanner publication commit scope deadline or cancellation reached"));
|
||||
}
|
||||
|
||||
let disk_wait_started = rustfs_io_metrics::put_stage_timer();
|
||||
let result = disk
|
||||
.rename_data_borrowed_with_fence(
|
||||
@@ -5841,7 +5896,8 @@ impl SetDisks {
|
||||
|
||||
#[cfg(test)]
|
||||
pub(in crate::set_disk) async fn delete_prefix(&self, bucket: &str, prefix: &str) -> disk::error::Result<()> {
|
||||
self.delete_prefix_with_scanner_publication_lease(bucket, prefix, None).await
|
||||
self.delete_prefix_with_scanner_publication_lease(bucket, prefix, None, None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Delete a prefix with an optional per-remote-disk scanner publication
|
||||
@@ -5852,6 +5908,7 @@ impl SetDisks {
|
||||
bucket: &str,
|
||||
prefix: &str,
|
||||
scanner_publication_lease_tokens: Option<&HashMap<String, Uuid>>,
|
||||
scanner_publication_commit_scope: Option<crate::object_api::ScannerPublicationCommitScope>,
|
||||
) -> disk::error::Result<()> {
|
||||
let disks = self.get_disks_internal().await;
|
||||
let write_quorum = disks.len() / 2 + 1;
|
||||
@@ -5860,11 +5917,21 @@ impl SetDisks {
|
||||
let mut futures = Vec::with_capacity(disks.len());
|
||||
|
||||
for (disk_op, scanner_publication_lease_token) in disks.iter().zip(fanout_fence_tokens) {
|
||||
let disk_op = disk_op.clone();
|
||||
let bucket = bucket.to_string();
|
||||
let prefix = prefix.to_string();
|
||||
let scanner_publication_commit_scope = scanner_publication_commit_scope.clone();
|
||||
futures.push(async move {
|
||||
if let Some(disk) = disk_op {
|
||||
disk.delete_with_scanner_publication_lease(
|
||||
if let Some(scope) = scanner_publication_commit_scope.as_ref()
|
||||
&& !scope.can_commit()
|
||||
{
|
||||
return Err(DiskError::other("scanner publication delete scope cannot commit"));
|
||||
}
|
||||
let external_guard = scanner_publication_commit_scope
|
||||
.as_ref()
|
||||
.map(|scope| Arc::new(scope.clone()) as Arc<dyn Send + Sync>);
|
||||
disk.delete_with_scanner_publication_lease_and_guard(
|
||||
&bucket,
|
||||
&prefix,
|
||||
DeleteOptions {
|
||||
@@ -5873,6 +5940,7 @@ impl SetDisks {
|
||||
..Default::default()
|
||||
},
|
||||
scanner_publication_lease_token,
|
||||
external_guard,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
@@ -5881,7 +5949,10 @@ impl SetDisks {
|
||||
});
|
||||
}
|
||||
|
||||
Self::reduce_delete_prefix_results(join_all(futures).await, write_quorum)
|
||||
run_scanner_publication_delete_owner(scanner_publication_commit_scope, move || async move {
|
||||
Self::reduce_delete_prefix_results(join_all(futures).await, write_quorum)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Scan a single disk's copy of `prefix` and decide whether it is an orphan
|
||||
@@ -6809,6 +6880,63 @@ mod tests {
|
||||
use tempfile::TempDir;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn scanner_delete_owner_survives_waiter_cancellation() {
|
||||
let movement_gate = Arc::new(tokio::sync::RwLock::new(()));
|
||||
let movement_permit = movement_gate.clone().read_owned().await;
|
||||
let scope = crate::object_api::ScannerPublicationCommitScope::new_storage_owned(
|
||||
7,
|
||||
tokio::time::Instant::now() + std::time::Duration::from_secs(30),
|
||||
Vec::new(),
|
||||
movement_permit,
|
||||
);
|
||||
scope.try_begin().expect("delete scope should enter flight");
|
||||
let scope_guard = crate::object_api::ScannerPublicationCommitScopeGuard::new(scope.clone());
|
||||
let (started_tx, started_rx) = tokio::sync::oneshot::channel();
|
||||
let (release_tx, release_rx) = tokio::sync::oneshot::channel();
|
||||
let (finished_tx, finished_rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
let waiter = tokio::spawn(run_scanner_publication_delete_owner(Some(scope.clone()), move || async move {
|
||||
started_tx.send(()).expect("delete owner should start");
|
||||
release_rx.await.expect("delete owner should be released");
|
||||
finished_tx.send(()).expect("delete owner should finish");
|
||||
Ok(())
|
||||
}));
|
||||
started_rx.await.expect("delete owner should run");
|
||||
drop(scope_guard);
|
||||
waiter.abort();
|
||||
assert_eq!(
|
||||
scope.state(),
|
||||
crate::object_api::ScannerPublicationCommitState::InFlight,
|
||||
"caller cancellation must not classify an owned delete as indeterminate"
|
||||
);
|
||||
|
||||
let mut movement_writer = Box::pin(movement_gate.write_owned());
|
||||
assert!(
|
||||
tokio::time::timeout(std::time::Duration::from_millis(20), &mut movement_writer)
|
||||
.await
|
||||
.is_err(),
|
||||
"movement transition must remain fenced while delete owner drains"
|
||||
);
|
||||
release_tx.send(()).expect("delete owner should remain alive");
|
||||
finished_rx.await.expect("delete owner should drain");
|
||||
tokio::time::timeout(std::time::Duration::from_secs(1), async {
|
||||
loop {
|
||||
if scope.state() == crate::object_api::ScannerPublicationCommitState::Committed {
|
||||
break;
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("delete owner should report a terminal result");
|
||||
assert!(
|
||||
scope.release_movement_permit().await,
|
||||
"terminal delete should release its movement permit"
|
||||
);
|
||||
movement_writer.await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_precondition_lookup_errors_fail_closed_unless_absence_is_known() {
|
||||
let create_only = HTTPPreconditions {
|
||||
|
||||
@@ -3938,6 +3938,44 @@ impl SetDisks {
|
||||
owner.scanner_data_usage_publication_admission_guard().await
|
||||
}
|
||||
|
||||
pub async fn scanner_data_usage_publication_commit_scope(
|
||||
&self,
|
||||
expected_movement_epoch: u64,
|
||||
safe_deadline: tokio::time::Instant,
|
||||
remote_lease_tokens: Vec<Uuid>,
|
||||
) -> Option<crate::object_api::ScannerPublicationCommitScope> {
|
||||
let (movement_permit, epoch) = self.scanner_data_usage_publication_admission_guard().await?;
|
||||
if epoch != expected_movement_epoch {
|
||||
return None;
|
||||
}
|
||||
Some(crate::object_api::ScannerPublicationCommitScope::new_storage_owned(
|
||||
epoch,
|
||||
safe_deadline,
|
||||
remote_lease_tokens,
|
||||
movement_permit,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn scanner_data_usage_publication_commit_scope_with_release_flag(
|
||||
&self,
|
||||
expected_movement_epoch: u64,
|
||||
safe_deadline: tokio::time::Instant,
|
||||
remote_lease_tokens: Vec<Uuid>,
|
||||
lease_release_safe: Arc<std::sync::atomic::AtomicBool>,
|
||||
) -> Option<crate::object_api::ScannerPublicationCommitScope> {
|
||||
let (movement_permit, epoch) = self.scanner_data_usage_publication_admission_guard().await?;
|
||||
if epoch != expected_movement_epoch {
|
||||
return None;
|
||||
}
|
||||
Some(crate::object_api::ScannerPublicationCommitScope::new_storage_owned_with_release_flag(
|
||||
epoch,
|
||||
safe_deadline,
|
||||
remote_lease_tokens,
|
||||
movement_permit,
|
||||
lease_release_safe,
|
||||
))
|
||||
}
|
||||
|
||||
/// Whether both sets' namespace-lock implementations cover the same object key.
|
||||
pub(crate) async fn shares_namespace_lock_domain(&self, other: &Self) -> bool {
|
||||
match (self.ctx.is_dist_erasure().await, other.ctx.is_dist_erasure().await) {
|
||||
|
||||
@@ -66,6 +66,7 @@ use crate::bucket::lifecycle::bucket_lifecycle_ops::LifecycleOps;
|
||||
use crate::bucket::utils::is_meta_bucketname;
|
||||
use crate::bucket::versioning::VersioningApi;
|
||||
use crate::disk::DiskAPI;
|
||||
use crate::object_api::ScannerPublicationCommitScopeGuard;
|
||||
use crate::set_disk::coding;
|
||||
use crate::set_disk::core::io_primitives::GetCodecStreamingReaderBuildOutcome;
|
||||
use crate::set_disk::mem;
|
||||
@@ -272,6 +273,22 @@ const OLD_DATA_CLEANUP_RECEIPT_FILE: &str = ".rustfs-old-data-cleanup-receipt.js
|
||||
const SCANNER_PUBLICATION_LEASE_FENCE_MAX_BYTES: usize = 64 * 1024;
|
||||
const SCANNER_PUBLICATION_LEASE_FENCE_MAX_ENTRIES: usize = 256;
|
||||
|
||||
fn begin_scanner_publication_delete_mutation(scope: Option<&crate::object_api::ScannerPublicationCommitScope>) -> Result<()> {
|
||||
let Some(scope) = scope else {
|
||||
return Ok(());
|
||||
};
|
||||
if scope.state() == crate::object_api::ScannerPublicationCommitState::Admitted {
|
||||
scope
|
||||
.try_begin()
|
||||
.map_err(|_| Error::other("scanner publication delete scope cannot start"))?;
|
||||
}
|
||||
if !scope.can_commit() {
|
||||
let _ = scope.mark_indeterminate();
|
||||
return Err(StorageError::OperationCanceled);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn take_scanner_publication_lease_tokens(user_defined: &mut HashMap<String, String>) -> Result<Option<HashMap<String, Uuid>>> {
|
||||
let Some(encoded) = user_defined.remove(SCANNER_PUBLICATION_LEASE_FENCE_METADATA_KEY) else {
|
||||
return Ok(None);
|
||||
@@ -2627,6 +2644,10 @@ impl SetDisks {
|
||||
opts: &ObjectOptions,
|
||||
) -> Result<(ObjectInfo, Option<OldCurrentSize>)> {
|
||||
crate::hp_guard!("SetDisks::put_object");
|
||||
let mut scope_outcome_guard = opts
|
||||
.scanner_publication_commit_scope
|
||||
.clone()
|
||||
.map(ScannerPublicationCommitScopeGuard::new);
|
||||
let storage_class_config = self.storage_class_config_snapshot();
|
||||
self.invalidate_get_object_metadata_cache(bucket, object).await;
|
||||
|
||||
@@ -3397,8 +3418,16 @@ impl SetDisks {
|
||||
let commit_tmp_dir = tmp_dir.clone();
|
||||
let commit_object_lock_guard = object_lock_guard.take();
|
||||
let commit_bucket_lifecycle_guard = bucket_lifecycle_guard.take();
|
||||
let commit_allows_early_ack = commit_object_lock_guard.is_some();
|
||||
let detach_commit_owner = commit_allows_early_ack || commit_bucket_lifecycle_guard.is_some() || quota_mutation_fence;
|
||||
let commit_scanner_publication_scope = opts.scanner_publication_commit_scope.clone();
|
||||
// A scanner publication scope owns the movement permit until the
|
||||
// complete rename fan-out drains. Keep this path synchronous so
|
||||
// its terminal state is known before the coordinator releases
|
||||
// remote leases.
|
||||
let commit_allows_early_ack = commit_object_lock_guard.is_some() && commit_scanner_publication_scope.is_none();
|
||||
let detach_commit_owner = commit_scanner_publication_scope.is_some()
|
||||
|| commit_allows_early_ack
|
||||
|| commit_bucket_lifecycle_guard.is_some()
|
||||
|| quota_mutation_fence;
|
||||
let commit_write_path_label = write_path.metric_label();
|
||||
let commit_is_versioned = opts.versioned || opts.version_suspended;
|
||||
let commit_versioned = opts.versioned;
|
||||
@@ -3494,7 +3523,7 @@ impl SetDisks {
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
let pre_rename_result = if cancellation.is_some() || request_cancellation.is_some() {
|
||||
let mut pre_rename_result = if cancellation.is_some() || request_cancellation.is_some() {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = wait_for_put_object_commit_cancellation(cancellation.as_ref(), request_cancellation.as_ref()) => {
|
||||
@@ -3505,6 +3534,20 @@ impl SetDisks {
|
||||
} else {
|
||||
pre_rename.await
|
||||
};
|
||||
if pre_rename_result.is_ok()
|
||||
&& let Some(scope) = commit_scanner_publication_scope.as_ref()
|
||||
&& let Err(err) = scope.try_begin()
|
||||
{
|
||||
let _ = scope.mark_aborted_before_commit();
|
||||
pre_rename_result = Err(Error::other(format!("scanner publication commit scope cannot start: {err:?}")));
|
||||
}
|
||||
if pre_rename_result.is_ok()
|
||||
&& let Some(scope) = commit_scanner_publication_scope.as_ref()
|
||||
&& !scope.can_commit()
|
||||
{
|
||||
let _ = scope.mark_indeterminate();
|
||||
pre_rename_result = Err(StorageError::OperationCanceled);
|
||||
}
|
||||
if let Err(err) = pre_rename_result {
|
||||
SetDisks::abort_quota_reservation_after_fence(
|
||||
quota_reservation,
|
||||
@@ -3540,9 +3583,17 @@ impl SetDisks {
|
||||
crate::set_disk::core::io_primitives::RenameDataFenceOptions::new(
|
||||
write_quorum,
|
||||
commit_scanner_publication_lease_tokens.as_ref(),
|
||||
),
|
||||
)
|
||||
.with_publication_scope(commit_scanner_publication_scope.clone()),
|
||||
)
|
||||
.await;
|
||||
if let Some(scope) = commit_scanner_publication_scope.as_ref() {
|
||||
if rename_result.is_ok() {
|
||||
let _ = scope.mark_committed();
|
||||
} else {
|
||||
let _ = scope.mark_indeterminate();
|
||||
}
|
||||
}
|
||||
#[cfg(any(test, feature = "test-util"))]
|
||||
if rename_result.is_ok() {
|
||||
pause_put_object_commit(&commit_bucket, &commit_object, PutObjectCommitPause::AfterRenameQuorum).await;
|
||||
@@ -3857,6 +3908,11 @@ impl SetDisks {
|
||||
let _ = handoff.send(());
|
||||
}
|
||||
if detach_commit_owner {
|
||||
if let Some(scope_outcome_guard) = scope_outcome_guard.as_mut() {
|
||||
// The spawned commit closure owns the scope clone and is
|
||||
// now responsible for its terminal outcome.
|
||||
scope_outcome_guard.disarm();
|
||||
}
|
||||
let mut cancellation = PutObjectCommitCancellation::new();
|
||||
let child_token = cancellation.child_token();
|
||||
let result = tokio::spawn(async move { Box::pin(commit(Some(child_token))).await })
|
||||
@@ -7054,6 +7110,11 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
|
||||
#[tracing::instrument(skip(self, opts))]
|
||||
async fn delete_object(&self, bucket: &str, object: &str, mut opts: ObjectOptions) -> Result<ObjectInfo> {
|
||||
let _scope_outcome_guard = opts
|
||||
.scanner_publication_commit_scope
|
||||
.clone()
|
||||
.map(ScannerPublicationCommitScopeGuard::new);
|
||||
let scanner_publication_commit_scope = opts.scanner_publication_commit_scope.clone();
|
||||
// Scanner cleanup carries the per-peer lease fence as transient
|
||||
// request metadata. Consume it before any delete-prefix fanout so it
|
||||
// cannot be persisted or treated as user metadata.
|
||||
@@ -7148,6 +7209,7 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
}
|
||||
delete_request.set_skip_tier_free_version();
|
||||
}
|
||||
begin_scanner_publication_delete_mutation(scanner_publication_commit_scope.as_ref())?;
|
||||
self.delete_object_version(bucket, object, &delete_request, false).await?;
|
||||
if let Some((_, deleted_object)) = replication_delete {
|
||||
ReplicationLifecycleBridge::schedule_delete(bucket.to_string(), deleted_object).await;
|
||||
@@ -7162,6 +7224,7 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
..Default::default()
|
||||
};
|
||||
delete_request.set_tier_free_version_id(&Uuid::new_v4().to_string());
|
||||
begin_scanner_publication_delete_mutation(scanner_publication_commit_scope.as_ref())?;
|
||||
self.delete_object_version(bucket, object, &delete_request, false).await?;
|
||||
}
|
||||
for version in &versions.free_versions {
|
||||
@@ -7173,10 +7236,14 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
..Default::default()
|
||||
};
|
||||
delete_request.set_tier_free_version();
|
||||
begin_scanner_publication_delete_mutation(scanner_publication_commit_scope.as_ref())?;
|
||||
self.delete_object_version(bucket, object, &delete_request, false).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(scope) = scanner_publication_commit_scope.as_ref() {
|
||||
let _ = scope.mark_committed();
|
||||
}
|
||||
self.invalidate_get_object_metadata_cache(bucket, object).await;
|
||||
return Ok(ObjectInfo::default());
|
||||
}
|
||||
@@ -7184,10 +7251,19 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
self.validate_bucket_incarnation(bucket, expected_incarnation_id).await?;
|
||||
}
|
||||
ensure_delete_commit_locks_held(_lock_guard.as_ref(), bucket, object, &opts)?;
|
||||
self.delete_prefix_with_scanner_publication_lease(bucket, object, scanner_publication_lease_tokens.as_ref())
|
||||
.await
|
||||
.map_err(|e| to_object_err(e.into(), vec![bucket, object]))?;
|
||||
begin_scanner_publication_delete_mutation(scanner_publication_commit_scope.as_ref())?;
|
||||
self.delete_prefix_with_scanner_publication_lease(
|
||||
bucket,
|
||||
object,
|
||||
scanner_publication_lease_tokens.as_ref(),
|
||||
scanner_publication_commit_scope.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| to_object_err(e.into(), vec![bucket, object]))?;
|
||||
|
||||
if let Some(scope) = scanner_publication_commit_scope.as_ref() {
|
||||
let _ = scope.mark_committed();
|
||||
}
|
||||
self.invalidate_all_get_object_metadata_cache();
|
||||
return Ok(ObjectInfo::default());
|
||||
}
|
||||
@@ -7260,10 +7336,14 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
..Default::default()
|
||||
};
|
||||
ensure_delete_commit_locks_held(_lock_guard.as_ref(), bucket, object, &opts)?;
|
||||
begin_scanner_publication_delete_mutation(scanner_publication_commit_scope.as_ref())?;
|
||||
self.delete_object_version(bucket, object, &dfi, false)
|
||||
.await
|
||||
.map_err(|e| to_object_err(e, vec![bucket, object]))?;
|
||||
self.invalidate_get_object_metadata_cache(bucket, object).await;
|
||||
if let Some(scope) = scanner_publication_commit_scope.as_ref() {
|
||||
let _ = scope.mark_committed();
|
||||
}
|
||||
return Ok(ObjectInfo::from_file_info(&dfi, bucket, object, opts.versioned || opts.version_suspended));
|
||||
}
|
||||
|
||||
@@ -7337,6 +7417,7 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
};
|
||||
|
||||
ensure_delete_commit_locks_held(_lock_guard.as_ref(), bucket, object, &opts)?;
|
||||
begin_scanner_publication_delete_mutation(scanner_publication_commit_scope.as_ref())?;
|
||||
self.delete_object_version(bucket, object, &fi, should_force_delete_marker_for_missing_version(&opts))
|
||||
.await
|
||||
.map_err(|e| to_object_err(e, vec![bucket, object]))?;
|
||||
@@ -7348,6 +7429,9 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
oi.user_tags = Arc::clone(&goi.user_tags);
|
||||
oi.replication_decision = goi.replication_decision;
|
||||
self.invalidate_get_object_metadata_cache(bucket, object).await;
|
||||
if let Some(scope) = scanner_publication_commit_scope.as_ref() {
|
||||
let _ = scope.mark_committed();
|
||||
}
|
||||
return Ok(oi);
|
||||
}
|
||||
|
||||
@@ -7373,6 +7457,7 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
}
|
||||
|
||||
ensure_delete_commit_locks_held(_lock_guard.as_ref(), bucket, object, &opts)?;
|
||||
begin_scanner_publication_delete_mutation(scanner_publication_commit_scope.as_ref())?;
|
||||
self.delete_object_version(bucket, object, &dfi, opts.delete_marker)
|
||||
.await
|
||||
.map_err(|e| to_object_err(e, vec![bucket, object]))?;
|
||||
@@ -7398,6 +7483,9 @@ impl crate::storage_api_contracts::object::ObjectOperations for SetDisks {
|
||||
obj_info.delete_marker = true;
|
||||
}
|
||||
self.invalidate_get_object_metadata_cache(bucket, object).await;
|
||||
if let Some(scope) = scanner_publication_commit_scope.as_ref() {
|
||||
let _ = scope.mark_committed();
|
||||
}
|
||||
Ok(obj_info)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user