mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 15:37:02 +00:00
fix(ilm): preserve lifecycle version groups (#5239)
* fix(ilm): evaluate complete version groups Co-Authored-By: heihutu <heihutu@gmail.com> * feat(ilm): log replication expiry blocks Co-Authored-By: heihutu <heihutu@gmail.com> * fix(ilm): diagnose incomplete noncurrent chains Co-Authored-By: heihutu <heihutu@gmail.com> * test(ilm): cover purge-pending version groups Add regression coverage for lifecycle-only version listing so purge-pending versions stay present for ILM evaluation while the public ListObjectVersions projection remains filtered. Refs rustfs/backlog#1500 Co-Authored-By: heihutu <heihutu@gmail.com> * feat(ilm): log lifecycle evaluation failures Emit structured lifecycle_evaluation_failed events when version group loading or evaluation fails during immediate and existing-object expiry scans. Refs rustfs/backlog#1503 Co-Authored-By: heihutu <heihutu@gmail.com> * test(ilm): cover incomplete noncurrent chains Pin the fail-closed behavior for noncurrent expiration when successor_mod_time is missing and reuse a stable structured event name for that skip path. Refs rustfs/backlog#1502 Co-Authored-By: heihutu <heihutu@gmail.com> * test(ilm): cover one-day noncurrent expiry boundary Add a runtime regression test proving NoncurrentVersionExpiration Days=1 stays inactive before expected_expiry_time and deletes exactly at the computed due boundary. Refs rustfs/backlog#1504 Co-Authored-By: heihutu <heihutu@gmail.com> * fix(ilm): keep transition checks after incomplete expiry Co-Authored-By: heihutu <heihutu@gmail.com> --------- Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
@@ -55,6 +55,19 @@ impl ECStore {
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn list_object_versions_for_lifecycle(
|
||||
self: Arc<Self>,
|
||||
bucket: &str,
|
||||
prefix: &str,
|
||||
marker: Option<String>,
|
||||
version_marker: Option<String>,
|
||||
delimiter: Option<String>,
|
||||
max_keys: i32,
|
||||
) -> Result<ListObjectVersionsInfo> {
|
||||
self.inner_list_object_versions_for_lifecycle(bucket, prefix, marker, version_marker, delimiter, max_keys)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(super) async fn handle_walk(
|
||||
self: Arc<Self>,
|
||||
rx: CancellationToken,
|
||||
|
||||
@@ -81,6 +81,17 @@ type ListObjectsV2Info = StorageListObjectsV2Info<ObjectInfo>;
|
||||
type ListObjectVersionsInfo = StorageListObjectVersionsInfo<ObjectInfo>;
|
||||
type ObjectInfoOrErr = StorageObjectInfoOrErr<ObjectInfo, Error>;
|
||||
type WalkOptions = StorageWalkOptions<fn(&rustfs_filemeta::FileInfo) -> bool>;
|
||||
|
||||
struct ListObjectVersionsInput<'a> {
|
||||
bucket: &'a str,
|
||||
prefix: &'a str,
|
||||
marker: Option<String>,
|
||||
version_marker: Option<String>,
|
||||
delimiter: Option<String>,
|
||||
max_keys: i32,
|
||||
include_version_purge: bool,
|
||||
}
|
||||
|
||||
const LIST_MERGED_INPUT_BUFFER: usize = 1;
|
||||
|
||||
fn list_merged_entry_channel() -> (Sender<MetaCacheEntry>, Receiver<MetaCacheEntry>) {
|
||||
@@ -3888,6 +3899,52 @@ impl ECStore {
|
||||
delimiter: Option<String>,
|
||||
max_keys: i32,
|
||||
) -> Result<ListObjectVersionsInfo> {
|
||||
self.inner_list_object_versions_with_projection(ListObjectVersionsInput {
|
||||
bucket,
|
||||
prefix,
|
||||
marker,
|
||||
version_marker,
|
||||
delimiter,
|
||||
max_keys,
|
||||
include_version_purge: false,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn inner_list_object_versions_for_lifecycle(
|
||||
self: Arc<Self>,
|
||||
bucket: &str,
|
||||
prefix: &str,
|
||||
marker: Option<String>,
|
||||
version_marker: Option<String>,
|
||||
delimiter: Option<String>,
|
||||
max_keys: i32,
|
||||
) -> Result<ListObjectVersionsInfo> {
|
||||
self.inner_list_object_versions_with_projection(ListObjectVersionsInput {
|
||||
bucket,
|
||||
prefix,
|
||||
marker,
|
||||
version_marker,
|
||||
delimiter,
|
||||
max_keys,
|
||||
include_version_purge: true,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn inner_list_object_versions_with_projection(
|
||||
self: Arc<Self>,
|
||||
input: ListObjectVersionsInput<'_>,
|
||||
) -> Result<ListObjectVersionsInfo> {
|
||||
let ListObjectVersionsInput {
|
||||
bucket,
|
||||
prefix,
|
||||
marker,
|
||||
version_marker,
|
||||
delimiter,
|
||||
max_keys,
|
||||
include_version_purge,
|
||||
} = input;
|
||||
let max_keys = normalize_max_keys(max_keys);
|
||||
if marker.is_none() && version_marker.is_some() {
|
||||
return Err(StorageError::NotImplemented);
|
||||
@@ -3947,14 +4004,19 @@ impl ECStore {
|
||||
// Last RAW scanned key, captured before folding (ECA-03 / #944).
|
||||
let last_scanned_key = last_scanned_entry_name(list_result.entries.as_ref());
|
||||
|
||||
let get_objects = ObjectInfo::from_meta_cache_entries_sorted_versions(
|
||||
&list_result.entries.unwrap_or_default(),
|
||||
bucket,
|
||||
prefix,
|
||||
delimiter.clone(),
|
||||
version_marker,
|
||||
)
|
||||
.await;
|
||||
let entries = list_result.entries.unwrap_or_default();
|
||||
let get_objects = if include_version_purge {
|
||||
ObjectInfo::from_meta_cache_entries_sorted_versions_for_lifecycle(
|
||||
&entries,
|
||||
bucket,
|
||||
prefix,
|
||||
delimiter.clone(),
|
||||
version_marker,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
ObjectInfo::from_meta_cache_entries_sorted_versions(&entries, bucket, prefix, delimiter.clone(), version_marker).await
|
||||
};
|
||||
|
||||
let (objects, prefixes, is_truncated, next_marker, next_version_idmarker) = list_objects_paginate(
|
||||
get_objects,
|
||||
|
||||
Reference in New Issue
Block a user