mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-10 15:16:56 +00:00
1511f9eacb
Co-authored-by: loverustfs <hello@rustfs.com>
147 lines
5.7 KiB
Rust
147 lines
5.7 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use std::sync::Arc;
|
|
use tracing::warn;
|
|
|
|
use crate::bucket::lifecycle::lifecycle;
|
|
use crate::bucket::replication::{DeletedObjectReplicationInfo, check_replicate_delete, schedule_replication_delete};
|
|
use crate::bucket::versioning::VersioningApi;
|
|
use crate::bucket::versioning_sys::BucketVersioningSys;
|
|
use crate::store::ECStore;
|
|
use crate::store_api::{ObjectOperations, ObjectOptions, ObjectToDelete};
|
|
use rustfs_filemeta::{REPLICATE_INCOMING_DELETE, ReplicationState, version_purge_statuses_map};
|
|
use rustfs_lock::MAX_DELETE_LIST;
|
|
|
|
fn lifecycle_version_delete_replication_state(
|
|
replicate_decision_str: String,
|
|
pending_status: Option<String>,
|
|
) -> ReplicationState {
|
|
ReplicationState {
|
|
replicate_decision_str,
|
|
version_purge_status_internal: pending_status.clone(),
|
|
purge_targets: version_purge_statuses_map(pending_status.as_deref().unwrap_or_default()),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub async fn delete_object_versions(api: &Arc<ECStore>, bucket: &str, to_del: &[ObjectToDelete], _lc_event: lifecycle::Event) {
|
|
let version_suspended = match BucketVersioningSys::get(bucket).await {
|
|
Ok(vc) => vc.suspended(),
|
|
Err(err) => {
|
|
warn!(bucket, error = ?err, "failed to get versioning config during lifecycle noncurrent version cleanup");
|
|
return;
|
|
}
|
|
};
|
|
let mut remaining = to_del;
|
|
loop {
|
|
let mut to_del = remaining;
|
|
if to_del.len() > MAX_DELETE_LIST {
|
|
remaining = &to_del[MAX_DELETE_LIST..];
|
|
to_del = &to_del[..MAX_DELETE_LIST];
|
|
} else {
|
|
remaining = &[];
|
|
}
|
|
|
|
let mut replication_candidates: Vec<Option<ReplicationState>> = Vec::with_capacity(to_del.len());
|
|
for object in to_del.iter() {
|
|
let version_id = object.version_id.map(|vid| vid.to_string());
|
|
let opts = ObjectOptions {
|
|
version_id: version_id.clone(),
|
|
versioned: true,
|
|
version_suspended,
|
|
..Default::default()
|
|
};
|
|
let candidate = match api.get_object_info(bucket, &object.object_name, &opts).await {
|
|
Ok(info) => {
|
|
let dsc = check_replicate_delete(bucket, object, &info, &opts, None).await;
|
|
dsc.replicate_any()
|
|
.then(|| lifecycle_version_delete_replication_state(dsc.to_string(), dsc.pending_status()))
|
|
}
|
|
Err(err) => {
|
|
warn!(
|
|
bucket,
|
|
object = %object.object_name,
|
|
version_id = ?version_id,
|
|
error = ?err,
|
|
"failed to get object info during lifecycle noncurrent version cleanup; skipping delete replication scheduling"
|
|
);
|
|
None
|
|
}
|
|
};
|
|
replication_candidates.push(candidate);
|
|
}
|
|
|
|
let (mut deleted_objs, errors) = api
|
|
.delete_objects(
|
|
bucket,
|
|
to_del.to_vec(),
|
|
ObjectOptions {
|
|
version_suspended,
|
|
..Default::default()
|
|
},
|
|
)
|
|
.await;
|
|
|
|
for (i, deleted_obj) in deleted_objs.iter_mut().enumerate() {
|
|
if errors.get(i).and_then(|err| err.as_ref()).is_some() {
|
|
continue;
|
|
}
|
|
let Some(replication_state) = replication_candidates.get(i).and_then(|c| c.clone()) else {
|
|
continue;
|
|
};
|
|
deleted_obj.replication_state = Some(replication_state);
|
|
schedule_replication_delete(DeletedObjectReplicationInfo {
|
|
delete_object: deleted_obj.clone(),
|
|
bucket: bucket.to_string(),
|
|
event_type: REPLICATE_INCOMING_DELETE.to_string(),
|
|
..Default::default()
|
|
})
|
|
.await;
|
|
}
|
|
|
|
for (i, err) in errors.iter().enumerate() {
|
|
if let Some(e) = err {
|
|
let obj_name = to_del.get(i).map(|o| o.object_name.as_str()).unwrap_or("<unknown>");
|
|
let vid = to_del
|
|
.get(i)
|
|
.and_then(|o| o.version_id)
|
|
.map(|v| v.to_string())
|
|
.unwrap_or_default();
|
|
warn!(bucket, object = obj_name, version_id = %vid, error = ?e, "failed to delete noncurrent version during lifecycle cleanup");
|
|
}
|
|
}
|
|
if remaining.is_empty() {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::lifecycle_version_delete_replication_state;
|
|
|
|
#[test]
|
|
fn lifecycle_version_delete_replication_state_tracks_pending_purge_targets() {
|
|
let state = lifecycle_version_delete_replication_state(
|
|
"arn:aws:s3:::target=true;false;arn:aws:s3:::target;".to_string(),
|
|
Some("arn:aws:s3:::target=PENDING;".to_string()),
|
|
);
|
|
|
|
assert_eq!(state.version_purge_status_internal.as_deref(), Some("arn:aws:s3:::target=PENDING;"));
|
|
assert!(state.purge_targets.contains_key("arn:aws:s3:::target"));
|
|
assert_eq!(state.replicate_decision_str, "arn:aws:s3:::target=true;false;arn:aws:s3:::target;");
|
|
}
|
|
}
|