fix: listobjects v2 pagination (#1607)

This commit is contained in:
安正超
2026-01-25 09:30:08 +08:00
committed by GitHub
parent 3838a13606
commit 4890fb25c1
5 changed files with 461 additions and 34 deletions
+3 -1
View File
@@ -2784,8 +2784,10 @@ mod test {
let disk_info = disk.disk_info(&disk_info_opts).await.unwrap();
// Basic checks on disk info
assert!(!disk_info.fs_type.is_empty());
assert!(disk_info.total > 0);
assert!(disk_info.free <= disk_info.total);
assert!(!disk_info.mount_path.is_empty());
assert!(!disk_info.endpoint.is_empty());
// Clean up the test directory
let _ = fs::remove_dir_all(&test_dir).await;
+28 -32
View File
@@ -256,19 +256,21 @@ impl ECStore {
max_keys: i32,
incl_deleted: bool,
) -> Result<ListObjectsInfo> {
let effective_max_keys = if max_keys <= 0 { 0 } else { max_keys_plus_one(max_keys, true) };
let opts = ListPathOptions {
bucket: bucket.to_owned(),
prefix: prefix.to_owned(),
separator: delimiter.clone(),
limit: max_keys_plus_one(max_keys, marker.is_some()),
// Always request max_keys + 1 to detect if there are more results
limit: effective_max_keys,
marker,
incl_deleted,
ask_disks: "strict".to_owned(), //TODO: from config
..Default::default()
};
// use get
if !opts.prefix.is_empty() && opts.limit == 1 && opts.marker.is_none() {
// Optimization: use get for single object lookup with exact prefix
if !opts.prefix.is_empty() && max_keys == 1 && opts.marker.is_none() {
match self
.get_object_info(
&opts.bucket,
@@ -322,14 +324,16 @@ impl ECStore {
)
.await;
let is_truncated = {
if max_keys > 0 && get_objects.len() > max_keys as usize {
get_objects.truncate(max_keys as usize);
true
} else {
list_result.err.is_none() && !get_objects.is_empty()
}
};
// Determine if there are more results: we requested max_keys + 1, so if we got more
// than max_keys, there are more results available
let mut is_truncated = false;
if max_keys <= 0 {
get_objects.clear();
} else if get_objects.len() > max_keys as usize {
is_truncated = true;
// Truncate to max_keys if we have more results
get_objects.truncate(max_keys as usize);
}
let next_marker = {
if is_truncated {
@@ -397,12 +401,13 @@ impl ECStore {
None
};
// if marker set, limit +1
let effective_max_keys = if max_keys <= 0 { 0 } else { max_keys_plus_one(max_keys, true) };
// Always request max_keys + 1 to detect if there are more results
let opts = ListPathOptions {
bucket: bucket.to_owned(),
prefix: prefix.to_owned(),
separator: delimiter.clone(),
limit: max_keys_plus_one(max_keys, marker.is_some()),
limit: effective_max_keys,
marker,
incl_deleted: true,
ask_disks: "strict".to_owned(),
@@ -428,13 +433,6 @@ impl ECStore {
result.forward_past(opts.marker);
}
// Check if list_path returned entries equal to limit, which indicates more objects exist
// This is more accurate than checking get_objects.len() because get_objects may be filtered
// (e.g., directories filtered out), so its length may be less than the actual entries count
// We need to check this before calling from_meta_cache_entries_sorted_versions which consumes entries
let entries_count = list_result.entries.as_ref().map(|e| e.entries().len()).unwrap_or(0);
let limit = opts.limit;
let mut get_objects = ObjectInfo::from_meta_cache_entries_sorted_versions(
&list_result.entries.unwrap_or_default(),
bucket,
@@ -444,18 +442,16 @@ impl ECStore {
)
.await;
let is_truncated = {
if max_keys > 0 && get_objects.len() > max_keys as usize {
get_objects.truncate(max_keys as usize);
true
} else if entries_count >= limit as usize {
// If entries count equals limit, there are more objects
true
} else {
// Otherwise, check if there are any objects and no error
list_result.err.is_none() && !get_objects.is_empty()
}
};
// Determine if there are more results: we requested max_keys + 1, so if we got more
// than max_keys, there are more results available
let mut is_truncated = false;
if max_keys <= 0 {
get_objects.clear();
} else if get_objects.len() > max_keys as usize {
is_truncated = true;
// Truncate to max_keys if we have more results
get_objects.truncate(max_keys as usize);
}
let (next_marker, next_version_idmarker) = {
if is_truncated {