refactor: clean shared list operation consumers (#3563)

This commit is contained in:
安正超
2026-06-18 11:57:46 +08:00
committed by GitHub
parent 46e200c22b
commit 24443c829d
6 changed files with 93 additions and 46 deletions
+5 -5
View File
@@ -22,8 +22,7 @@ use crate::{
};
use futures::future::join_all;
use rustfs_credentials::get_global_action_cred;
use rustfs_ecstore::error::{StorageError, classify_system_path_failure_reason};
use rustfs_ecstore::store_api::{ObjectInfoOrErr, WalkOptions};
use rustfs_ecstore::error::{Error as EcstoreError, StorageError, classify_system_path_failure_reason};
use rustfs_ecstore::{
config::{
RUSTFS_CONFIG_PREFIX,
@@ -34,8 +33,7 @@ use rustfs_ecstore::{
};
use rustfs_io_metrics::record_system_path_failure;
use rustfs_policy::{auth::UserIdentity, policy::PolicyDoc};
use rustfs_storage_api::HTTPPreconditions;
use rustfs_storage_api::ListOperations as _;
use rustfs_storage_api::{HTTPPreconditions, ListOperations as _, ObjectInfoOrErr as StorageObjectInfoOrErr};
use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf};
use serde::{Serialize, de::DeserializeOwned};
use std::sync::{LazyLock, Mutex};
@@ -62,6 +60,8 @@ pub static IAM_CONFIG_POLICY_DB_SERVICE_ACCOUNTS_PREFIX: LazyLock<String> =
pub static IAM_CONFIG_POLICY_DB_GROUPS_PREFIX: LazyLock<String> =
LazyLock::new(|| format!("{RUSTFS_CONFIG_PREFIX}/iam/policydb/groups/"));
type ObjectInfoOrErr = StorageObjectInfoOrErr<ObjectInfo, EcstoreError>;
const IAM_IDENTITY_FILE: &str = "identity.json";
const IAM_POLICY_FILE: &str = "policy.json";
const IAM_GROUP_MEMBERS_FILE: &str = "members.json";
@@ -367,7 +367,7 @@ impl ObjectStore {
let sender_on_error = sender.clone();
tokio::spawn(async move {
if let Err(err) = store
.walk(ctx.clone(), Self::BUCKET_NAME, &path, tx, WalkOptions::default())
.walk(ctx.clone(), Self::BUCKET_NAME, &path, tx, Default::default())
.await
{
let reason = classify_system_path_failure_reason(&err);
+5
View File
@@ -106,3 +106,8 @@ and the separate `NamespaceLocking` operation group.
The old `StorageAPI` aggregate facade must not reappear in production
`crates/ecstore/src` or `rustfs/src` code after the storage operation groups
have been made explicit.
Outer RustFS/IAM consumers must use `rustfs-storage-api` generic list response
contracts directly for `ListObjectsV2Info`, `ListObjectVersionsInfo`, and
`ObjectInfoOrErr`; ECStore keeps the concrete aliases only for internal
implementation and compatibility.
+53 -33
View File
@@ -5,19 +5,19 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
## Current Context
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
- Branch: `overtrue/arch-storage-operation-consumer-cleanup`
- Baseline: `main` at `e5cad7ed207265f5312c58d8dac6042e31ba95a3`
after the object and multipart operation contract merge.
- PR type for this branch: `api-extraction`
- Branch: `overtrue/arch-storage-shared-operation-bounds`
- Baseline: `main` at `46e200c290e3fc0887f4c19172bcc146e019d737`
after the heal/namespace-lock operation contract merge.
- PR type for this branch: `consumer-migration`
- Runtime behavior changes: no external behavior change expected.
- Rust code changes: move `HealOperations` and `NamespaceLocking` into
`rustfs-storage-api` as generic operation contracts with associated
ECStore-bound types, then keep ECStore's existing public names as fixed
associated-type compatibility subtraits.
- CI/script changes: extend migration guards for the heal/namespace-lock
operation public re-exports and ECStore local-method regressions.
- Docs changes: record the heal and namespace-lock operation contract
extraction slice.
- Rust code changes: migrate RustFS bucket response builders and IAM walk
channel typing away from ECStore list response aliases to the generic
`rustfs-storage-api` list contracts while keeping ECStore-owned
`ObjectInfo`, `ObjectOptions`, readers, delete DTOs, and implementation
behavior in ECStore.
- CI/script changes: extend migration guards so outer RustFS/IAM list response
consumers do not drift back to ECStore alias imports.
- Docs changes: record the shared list operation consumer cleanup slice.
## Phase 0 Tasks
@@ -732,6 +732,27 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
checks, migration/layer guards, formatting, diff hygiene, Rust risk scan,
full pre-commit, and required three-expert review passed.
- [x] `API-024` Clean shared list operation consumer bounds.
- Completed slice: migrate RustFS S3/bucket usecase list response builders from
ECStore `ListObjectVersionsInfo`/`ListObjectsV2Info` aliases to
`rustfs-storage-api` generic list response contracts bound to ECStore
`ObjectInfo`; migrate IAM walk channel typing from ECStore
`ObjectInfoOrErr` alias to the shared generic item contract.
- Acceptance: outer RustFS/IAM consumers use storage-api list response
contracts directly, ECStore keeps concrete aliases for internal
implementation and compatibility, and migration guards reject restoring the
old outer-consumer imports.
- Must preserve: S3 list v2/version output mapping, IAM config walk channel
item/error handling, ECStore concrete object metadata shape, walk options
inference, and storage error conversion behavior.
- Risk defense: this slice moves only low-coupling generic response/channel
typing; ECStore still owns `ObjectInfo`, `ObjectOptions`, readers,
filemeta-bound walk filter type, delete DTOs, and list/walk implementation
bodies.
- Verification: focused RustFS/IAM compile and tests, migration/layer guards,
formatting, diff hygiene, Rust risk scan, full pre-commit, and required
three-expert review passed.
## Phase 8 Background Controller Tasks
- [x] `BGC-001` Inventory background services.
@@ -1008,17 +1029,17 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
| Expert | Status | Notes |
|---|---|---|
| Quality/architecture | passed | Generic heal and namespace-lock traits now live in `rustfs-storage-api`; ECStore still owns concrete type bindings and implementations. |
| Migration preservation | passed | Existing ECStore public trait names remain as fixed associated-type compatibility subtraits while method resolution moves to shared traits where needed. |
| Testing/verification | passed | Focused storage-api tests, ECStore compat tests, downstream compile checks, migration/layer guards, formatting, diff hygiene, Rust risk scan, and full `make pre-commit` passed. |
| Quality/architecture | passed | Outer RustFS/IAM list response consumers now use the generic storage-api contracts; ECStore keeps concrete aliases only for implementation and compatibility. |
| Migration preservation | passed | S3 list response mapping and IAM walk item/error handling keep the same ECStore `ObjectInfo`, error conversion, and walk option inference behavior. |
| Testing/verification | passed | Focused RustFS/IAM compile/tests, migration/layer guards, formatting, diff hygiene, Rust risk scan, and full `make pre-commit` passed. |
## Verification Notes
Passed before push:
- `cargo test -p rustfs-storage-api`: passed.
- `cargo test -p rustfs-ecstore --test ecstore_contract_compat_test`: passed.
- `cargo check --tests -p rustfs-storage-api -p rustfs-ecstore -p rustfs -p rustfs-heal -p rustfs-scanner`: passed.
- `cargo check --tests -p rustfs -p rustfs-iam`: passed.
- `cargo test -p rustfs --lib storage::s3_api::bucket`: passed; 22 passed.
- `cargo test -p rustfs-iam`: passed; 150 passed.
- `./scripts/check_architecture_migration_rules.sh`: passed.
- `./scripts/check_layer_dependencies.sh`: passed.
- `cargo fmt --all --check`: passed.
@@ -1031,21 +1052,20 @@ Passed before push:
Notes:
- This slice follows the object and multipart operation contract branch and keeps the old
aggregate facade, bucket DTO, multipart DTO, bucket operation contract, object
helper, range helper, list helper, object precondition, list response, and
walk/list/object/multipart operation contract guards active.
- The shared heal and namespace-lock operation contracts are now owned by
`rustfs-storage-api`; ECStore keeps the concrete associated type bindings,
`HealOpts`, `HealResultItem`, `NamespaceLockWrapper`, lock implementation,
peer heal behavior, set/pool dispatch, and storage error mapping.
- The slice does not alter heal, namespace-lock, object, list, walk,
multipart, bucket, delete, reader, or tag/metadata runtime behavior.
- This slice follows the heal and namespace-lock operation contract branch and
keeps the old aggregate facade, DTO/helper, response, and operation contract
guards active.
- Outer RustFS/IAM consumers now bind low-coupling list response/channel
containers through `rustfs-storage-api`; ECStore keeps the concrete aliases,
object metadata, filemeta-bound walk filter, readers, delete DTOs, and list
implementation behavior.
- The slice does not alter list, walk, bucket, object, delete, reader,
tag/metadata, heal, namespace-lock, multipart, or storage error runtime
behavior.
## Handoff Notes
- Heal and namespace-lock operation contract cleanup is based on the merged
object and multipart operation contract branch.
- After this lands, remaining storage work can continue by removing low-value
ECStore compatibility imports or extracting larger low-coupling object
metadata/option/reader slices.
- Shared list operation consumer cleanup is rebased onto `main` after
`rustfs/rustfs#3560` merged.
- After this lands, continue with larger consumer-migration batches instead of
single-alias slices.
+8 -3
View File
@@ -57,15 +57,17 @@ use rustfs_ecstore::client::object_api_utils::to_s3s_etag;
use rustfs_ecstore::error::StorageError;
use rustfs_ecstore::notification_sys::get_global_notification_sys;
use rustfs_ecstore::store::ECStore;
use rustfs_ecstore::store_api::{ListObjectVersionsInfo, ListObjectsV2Info, ObjectInfo};
use rustfs_ecstore::store_api::ObjectInfo;
use rustfs_madmin::{SITE_REPL_API_VERSION, SRBucketMeta};
use rustfs_policy::policy::{
action::{Action, S3Action},
{BucketPolicy, BucketPolicyArgs, Effect, Validator},
};
use rustfs_s3_ops::S3Operation;
use rustfs_storage_api::ListOperations as _;
use rustfs_storage_api::{BucketOperations, BucketOptions, DeleteBucketOptions, MakeBucketOptions};
use rustfs_storage_api::{
BucketOperations, BucketOptions, DeleteBucketOptions, ListObjectVersionsInfo as StorageListObjectVersionsInfo,
ListObjectsV2Info as StorageListObjectsV2Info, ListOperations as _, MakeBucketOptions,
};
use rustfs_targets::{
EventName,
arn::{ARN, TargetIDError},
@@ -89,6 +91,9 @@ const LOG_COMPONENT_APP: &str = "app";
const LOG_SUBSYSTEM_BUCKET: &str = "bucket";
use urlencoding::encode;
type ListObjectVersionsInfo = StorageListObjectVersionsInfo<ObjectInfo>;
type ListObjectsV2Info = StorageListObjectsV2Info<ObjectInfo>;
fn serialize_config<T: xml::Serialize>(value: &T) -> S3Result<Vec<u8>> {
serialize(value).map_err(to_internal_error)
}
+11 -5
View File
@@ -15,8 +15,10 @@
use crate::storage::s3_api::common::rustfs_owner;
use percent_encoding::percent_decode_str;
use rustfs_ecstore::client::object_api_utils::to_s3s_etag;
use rustfs_ecstore::store_api::{ListObjectVersionsInfo, ListObjectsV2Info};
use rustfs_storage_api::BucketInfo;
use rustfs_ecstore::store_api::ObjectInfo;
use rustfs_storage_api::{
BucketInfo, ListObjectVersionsInfo as StorageListObjectVersionsInfo, ListObjectsV2Info as StorageListObjectsV2Info,
};
use s3s::dto::{
Bucket, CommonPrefix, DeleteMarkerEntry, EncodingType, ListBucketsOutput, ListObjectVersionsOutput, ListObjectsOutput,
ListObjectsV2Output, Object, ObjectStorageClass, ObjectVersion, ObjectVersionStorageClass, Timestamp,
@@ -27,6 +29,9 @@ use urlencoding::encode;
const S3_MAX_KEYS: i32 = 1000;
type ListObjectVersionsInfo = StorageListObjectVersionsInfo<ObjectInfo>;
type ListObjectsV2Info = StorageListObjectsV2Info<ObjectInfo>;
fn normalize_max_keys(max_keys: i32) -> i32 {
max_keys.min(S3_MAX_KEYS)
}
@@ -384,11 +389,12 @@ fn calculate_next_marker(v2: &ListObjectsV2Output) -> Option<String> {
#[cfg(test)]
mod tests {
use super::{
ListObjectVersionsParams, build_list_buckets_output, build_list_object_versions_output, build_list_objects_output,
build_list_objects_v2_output, parse_list_object_versions_params, parse_list_objects_v2_params,
ListObjectVersionsInfo, ListObjectVersionsParams, ListObjectsV2Info, build_list_buckets_output,
build_list_object_versions_output, build_list_objects_output, build_list_objects_v2_output,
parse_list_object_versions_params, parse_list_objects_v2_params,
};
use crate::storage::s3_api::common::rustfs_owner;
use rustfs_ecstore::store_api::{ListObjectVersionsInfo, ListObjectsV2Info, ObjectInfo};
use rustfs_ecstore::store_api::ObjectInfo;
use rustfs_storage_api::BucketInfo;
use s3s::S3ErrorCode;
use s3s::dto::{CommonPrefix, EncodingType, ListObjectsV2Output, Object};
@@ -57,6 +57,7 @@ STORE_API_OBJECT_HELPER_REEXPORTS_FILE="${TMP_DIR}/store_api_object_helper_reexp
STORE_API_RANGE_HELPER_REEXPORTS_FILE="${TMP_DIR}/store_api_range_helper_reexports.txt"
STORE_API_LIST_HELPER_REEXPORTS_FILE="${TMP_DIR}/store_api_list_helper_reexports.txt"
STORE_API_LIST_RESPONSE_REEXPORTS_FILE="${TMP_DIR}/store_api_list_response_reexports.txt"
STORE_API_EXTERNAL_LIST_CONSUMER_HITS_FILE="${TMP_DIR}/store_api_external_list_consumer_hits.txt"
STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE="${TMP_DIR}/store_api_object_operation_local_method_hits.txt"
awk '
@@ -298,6 +299,16 @@ if [[ -s "$STORE_API_LIST_RESPONSE_REEXPORTS_FILE" ]]; then
report_failure "old ecstore store_api list response path reintroduced: $(paste -sd '; ' "$STORE_API_LIST_RESPONSE_REEXPORTS_FILE")"
fi
(
cd "$ROOT_DIR"
rg -n --no-heading 'rustfs_ecstore::store_api(?:::\{[^}]*\b(?:ListObjectVersionsInfo|ListObjectsV2Info|ObjectInfoOrErr)\b|::(?:ListObjectVersionsInfo|ListObjectsV2Info|ObjectInfoOrErr)\b)' \
rustfs/src crates/iam/src || true
) >"$STORE_API_EXTERNAL_LIST_CONSUMER_HITS_FILE"
if [[ -s "$STORE_API_EXTERNAL_LIST_CONSUMER_HITS_FILE" ]]; then
report_failure "external list response consumers must use rustfs-storage-api contracts: $(paste -sd '; ' "$STORE_API_EXTERNAL_LIST_CONSUMER_HITS_FILE")"
fi
(
cd "$ROOT_DIR"
rg -n --no-heading 'async fn (get_object_reader|put_object|get_object_info|verify_object_integrity|copy_object|delete_object_version|delete_object|delete_objects|put_object_metadata|get_object_tags|put_object_tags|delete_object_tags|add_partial|transition_object|restore_transitioned_object|list_multipart_uploads|new_multipart_upload|copy_object_part|put_object_part|get_multipart_info|list_object_parts|abort_multipart_upload|complete_multipart_upload|heal_format|heal_bucket|heal_object|get_pool_and_set|check_abandoned_parts|new_ns_lock)\b' \