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 futures::future::join_all;
use rustfs_credentials::get_global_action_cred; use rustfs_credentials::get_global_action_cred;
use rustfs_ecstore::error::{StorageError, classify_system_path_failure_reason}; use rustfs_ecstore::error::{Error as EcstoreError, StorageError, classify_system_path_failure_reason};
use rustfs_ecstore::store_api::{ObjectInfoOrErr, WalkOptions};
use rustfs_ecstore::{ use rustfs_ecstore::{
config::{ config::{
RUSTFS_CONFIG_PREFIX, RUSTFS_CONFIG_PREFIX,
@@ -34,8 +33,7 @@ use rustfs_ecstore::{
}; };
use rustfs_io_metrics::record_system_path_failure; use rustfs_io_metrics::record_system_path_failure;
use rustfs_policy::{auth::UserIdentity, policy::PolicyDoc}; use rustfs_policy::{auth::UserIdentity, policy::PolicyDoc};
use rustfs_storage_api::HTTPPreconditions; use rustfs_storage_api::{HTTPPreconditions, ListOperations as _, ObjectInfoOrErr as StorageObjectInfoOrErr};
use rustfs_storage_api::ListOperations as _;
use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf}; use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf};
use serde::{Serialize, de::DeserializeOwned}; use serde::{Serialize, de::DeserializeOwned};
use std::sync::{LazyLock, Mutex}; 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> = pub static IAM_CONFIG_POLICY_DB_GROUPS_PREFIX: LazyLock<String> =
LazyLock::new(|| format!("{RUSTFS_CONFIG_PREFIX}/iam/policydb/groups/")); LazyLock::new(|| format!("{RUSTFS_CONFIG_PREFIX}/iam/policydb/groups/"));
type ObjectInfoOrErr = StorageObjectInfoOrErr<ObjectInfo, EcstoreError>;
const IAM_IDENTITY_FILE: &str = "identity.json"; const IAM_IDENTITY_FILE: &str = "identity.json";
const IAM_POLICY_FILE: &str = "policy.json"; const IAM_POLICY_FILE: &str = "policy.json";
const IAM_GROUP_MEMBERS_FILE: &str = "members.json"; const IAM_GROUP_MEMBERS_FILE: &str = "members.json";
@@ -367,7 +367,7 @@ impl ObjectStore {
let sender_on_error = sender.clone(); let sender_on_error = sender.clone();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(err) = store 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 .await
{ {
let reason = classify_system_path_failure_reason(&err); 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 The old `StorageAPI` aggregate facade must not reappear in production
`crates/ecstore/src` or `rustfs/src` code after the storage operation groups `crates/ecstore/src` or `rustfs/src` code after the storage operation groups
have been made explicit. 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 ## Current Context
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660) - Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
- Branch: `overtrue/arch-storage-operation-consumer-cleanup` - Branch: `overtrue/arch-storage-shared-operation-bounds`
- Baseline: `main` at `e5cad7ed207265f5312c58d8dac6042e31ba95a3` - Baseline: `main` at `46e200c290e3fc0887f4c19172bcc146e019d737`
after the object and multipart operation contract merge. after the heal/namespace-lock operation contract merge.
- PR type for this branch: `api-extraction` - PR type for this branch: `consumer-migration`
- Runtime behavior changes: no external behavior change expected. - Runtime behavior changes: no external behavior change expected.
- Rust code changes: move `HealOperations` and `NamespaceLocking` into - Rust code changes: migrate RustFS bucket response builders and IAM walk
`rustfs-storage-api` as generic operation contracts with associated channel typing away from ECStore list response aliases to the generic
ECStore-bound types, then keep ECStore's existing public names as fixed `rustfs-storage-api` list contracts while keeping ECStore-owned
associated-type compatibility subtraits. `ObjectInfo`, `ObjectOptions`, readers, delete DTOs, and implementation
- CI/script changes: extend migration guards for the heal/namespace-lock behavior in ECStore.
operation public re-exports and ECStore local-method regressions. - CI/script changes: extend migration guards so outer RustFS/IAM list response
- Docs changes: record the heal and namespace-lock operation contract consumers do not drift back to ECStore alias imports.
extraction slice. - Docs changes: record the shared list operation consumer cleanup slice.
## Phase 0 Tasks ## 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, checks, migration/layer guards, formatting, diff hygiene, Rust risk scan,
full pre-commit, and required three-expert review passed. 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 ## Phase 8 Background Controller Tasks
- [x] `BGC-001` Inventory background services. - [x] `BGC-001` Inventory background services.
@@ -1008,17 +1029,17 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
| Expert | Status | Notes | | 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. | | 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 | Existing ECStore public trait names remain as fixed associated-type compatibility subtraits while method resolution moves to shared traits where needed. | | 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 storage-api tests, ECStore compat tests, downstream compile checks, migration/layer guards, formatting, diff hygiene, Rust risk scan, and full `make pre-commit` passed. | | 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 ## Verification Notes
Passed before push: Passed before push:
- `cargo test -p rustfs-storage-api`: passed. - `cargo check --tests -p rustfs -p rustfs-iam`: passed.
- `cargo test -p rustfs-ecstore --test ecstore_contract_compat_test`: passed. - `cargo test -p rustfs --lib storage::s3_api::bucket`: passed; 22 passed.
- `cargo check --tests -p rustfs-storage-api -p rustfs-ecstore -p rustfs -p rustfs-heal -p rustfs-scanner`: passed. - `cargo test -p rustfs-iam`: passed; 150 passed.
- `./scripts/check_architecture_migration_rules.sh`: passed. - `./scripts/check_architecture_migration_rules.sh`: passed.
- `./scripts/check_layer_dependencies.sh`: passed. - `./scripts/check_layer_dependencies.sh`: passed.
- `cargo fmt --all --check`: passed. - `cargo fmt --all --check`: passed.
@@ -1031,21 +1052,20 @@ Passed before push:
Notes: Notes:
- This slice follows the object and multipart operation contract branch and keeps the old - This slice follows the heal and namespace-lock operation contract branch and
aggregate facade, bucket DTO, multipart DTO, bucket operation contract, object keeps the old aggregate facade, DTO/helper, response, and operation contract
helper, range helper, list helper, object precondition, list response, and guards active.
walk/list/object/multipart operation contract guards active. - Outer RustFS/IAM consumers now bind low-coupling list response/channel
- The shared heal and namespace-lock operation contracts are now owned by containers through `rustfs-storage-api`; ECStore keeps the concrete aliases,
`rustfs-storage-api`; ECStore keeps the concrete associated type bindings, object metadata, filemeta-bound walk filter, readers, delete DTOs, and list
`HealOpts`, `HealResultItem`, `NamespaceLockWrapper`, lock implementation, implementation behavior.
peer heal behavior, set/pool dispatch, and storage error mapping. - The slice does not alter list, walk, bucket, object, delete, reader,
- The slice does not alter heal, namespace-lock, object, list, walk, tag/metadata, heal, namespace-lock, multipart, or storage error runtime
multipart, bucket, delete, reader, or tag/metadata runtime behavior. behavior.
## Handoff Notes ## Handoff Notes
- Heal and namespace-lock operation contract cleanup is based on the merged - Shared list operation consumer cleanup is rebased onto `main` after
object and multipart operation contract branch. `rustfs/rustfs#3560` merged.
- After this lands, remaining storage work can continue by removing low-value - After this lands, continue with larger consumer-migration batches instead of
ECStore compatibility imports or extracting larger low-coupling object single-alias slices.
metadata/option/reader 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::error::StorageError;
use rustfs_ecstore::notification_sys::get_global_notification_sys; use rustfs_ecstore::notification_sys::get_global_notification_sys;
use rustfs_ecstore::store::ECStore; 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_madmin::{SITE_REPL_API_VERSION, SRBucketMeta};
use rustfs_policy::policy::{ use rustfs_policy::policy::{
action::{Action, S3Action}, action::{Action, S3Action},
{BucketPolicy, BucketPolicyArgs, Effect, Validator}, {BucketPolicy, BucketPolicyArgs, Effect, Validator},
}; };
use rustfs_s3_ops::S3Operation; use rustfs_s3_ops::S3Operation;
use rustfs_storage_api::ListOperations as _; use rustfs_storage_api::{
use rustfs_storage_api::{BucketOperations, BucketOptions, DeleteBucketOptions, MakeBucketOptions}; BucketOperations, BucketOptions, DeleteBucketOptions, ListObjectVersionsInfo as StorageListObjectVersionsInfo,
ListObjectsV2Info as StorageListObjectsV2Info, ListOperations as _, MakeBucketOptions,
};
use rustfs_targets::{ use rustfs_targets::{
EventName, EventName,
arn::{ARN, TargetIDError}, arn::{ARN, TargetIDError},
@@ -89,6 +91,9 @@ const LOG_COMPONENT_APP: &str = "app";
const LOG_SUBSYSTEM_BUCKET: &str = "bucket"; const LOG_SUBSYSTEM_BUCKET: &str = "bucket";
use urlencoding::encode; use urlencoding::encode;
type ListObjectVersionsInfo = StorageListObjectVersionsInfo<ObjectInfo>;
type ListObjectsV2Info = StorageListObjectsV2Info<ObjectInfo>;
fn serialize_config<T: xml::Serialize>(value: &T) -> S3Result<Vec<u8>> { fn serialize_config<T: xml::Serialize>(value: &T) -> S3Result<Vec<u8>> {
serialize(value).map_err(to_internal_error) serialize(value).map_err(to_internal_error)
} }
+11 -5
View File
@@ -15,8 +15,10 @@
use crate::storage::s3_api::common::rustfs_owner; use crate::storage::s3_api::common::rustfs_owner;
use percent_encoding::percent_decode_str; use percent_encoding::percent_decode_str;
use rustfs_ecstore::client::object_api_utils::to_s3s_etag; use rustfs_ecstore::client::object_api_utils::to_s3s_etag;
use rustfs_ecstore::store_api::{ListObjectVersionsInfo, ListObjectsV2Info}; use rustfs_ecstore::store_api::ObjectInfo;
use rustfs_storage_api::BucketInfo; use rustfs_storage_api::{
BucketInfo, ListObjectVersionsInfo as StorageListObjectVersionsInfo, ListObjectsV2Info as StorageListObjectsV2Info,
};
use s3s::dto::{ use s3s::dto::{
Bucket, CommonPrefix, DeleteMarkerEntry, EncodingType, ListBucketsOutput, ListObjectVersionsOutput, ListObjectsOutput, Bucket, CommonPrefix, DeleteMarkerEntry, EncodingType, ListBucketsOutput, ListObjectVersionsOutput, ListObjectsOutput,
ListObjectsV2Output, Object, ObjectStorageClass, ObjectVersion, ObjectVersionStorageClass, Timestamp, ListObjectsV2Output, Object, ObjectStorageClass, ObjectVersion, ObjectVersionStorageClass, Timestamp,
@@ -27,6 +29,9 @@ use urlencoding::encode;
const S3_MAX_KEYS: i32 = 1000; const S3_MAX_KEYS: i32 = 1000;
type ListObjectVersionsInfo = StorageListObjectVersionsInfo<ObjectInfo>;
type ListObjectsV2Info = StorageListObjectsV2Info<ObjectInfo>;
fn normalize_max_keys(max_keys: i32) -> i32 { fn normalize_max_keys(max_keys: i32) -> i32 {
max_keys.min(S3_MAX_KEYS) max_keys.min(S3_MAX_KEYS)
} }
@@ -384,11 +389,12 @@ fn calculate_next_marker(v2: &ListObjectsV2Output) -> Option<String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::{
ListObjectVersionsParams, build_list_buckets_output, build_list_object_versions_output, build_list_objects_output, ListObjectVersionsInfo, ListObjectVersionsParams, ListObjectsV2Info, build_list_buckets_output,
build_list_objects_v2_output, parse_list_object_versions_params, parse_list_objects_v2_params, 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 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 rustfs_storage_api::BucketInfo;
use s3s::S3ErrorCode; use s3s::S3ErrorCode;
use s3s::dto::{CommonPrefix, EncodingType, ListObjectsV2Output, Object}; 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_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_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_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" STORE_API_OBJECT_OPERATION_LOCAL_METHOD_HITS_FILE="${TMP_DIR}/store_api_object_operation_local_method_hits.txt"
awk ' 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")" report_failure "old ecstore store_api list response path reintroduced: $(paste -sd '; ' "$STORE_API_LIST_RESPONSE_REEXPORTS_FILE")"
fi 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" 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' \ 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' \