refactor(app): converge lower-priority global reads via AppContext (#1958)

This commit is contained in:
安正超
2026-02-25 22:30:53 +08:00
committed by GitHub
parent dc795a494a
commit 4c08e18812
6 changed files with 82 additions and 46 deletions
+8 -3
View File
@@ -27,12 +27,17 @@ use crate::{
router::{AdminOperation, Operation, S3Router}, router::{AdminOperation, Operation, S3Router},
}, },
app::admin_usecase::{DefaultAdminUsecase, QueryPoolStatusRequest}, app::admin_usecase::{DefaultAdminUsecase, QueryPoolStatusRequest},
app::context::get_global_app_context,
auth::{check_key_valid, get_session_token}, auth::{check_key_valid, get_session_token},
error::ApiError, error::ApiError,
server::{ADMIN_PREFIX, RemoteAddr}, server::{ADMIN_PREFIX, RemoteAddr},
}; };
use hyper::Method; use hyper::Method;
use rustfs_ecstore::{GLOBAL_Endpoints, new_object_layer_fn}; use rustfs_ecstore::new_object_layer_fn;
fn endpoints_from_context() -> Option<rustfs_ecstore::endpoints::EndpointServerPools> {
get_global_app_context().and_then(|context| context.endpoints().handle())
}
pub fn register_pool_route(r: &mut S3Router<AdminOperation>) -> std::io::Result<()> { pub fn register_pool_route(r: &mut S3Router<AdminOperation>) -> std::io::Result<()> {
r.insert( r.insert(
@@ -196,7 +201,7 @@ impl Operation for StartDecommission {
) )
.await?; .await?;
let Some(endpoints) = GLOBAL_Endpoints.get() else { let Some(endpoints) = endpoints_from_context() else {
return Err(s3_error!(NotImplemented)); return Err(s3_error!(NotImplemented));
}; };
@@ -295,7 +300,7 @@ impl Operation for CancelDecommission {
) )
.await?; .await?;
let Some(endpoints) = GLOBAL_Endpoints.get() else { let Some(endpoints) = endpoints_from_context() else {
return Err(s3_error!(NotImplemented)); return Err(s3_error!(NotImplemented));
}; };
+34 -38
View File
@@ -16,16 +16,20 @@
use crate::admin::auth::{validate_admin_request, validate_admin_request_with_bucket}; use crate::admin::auth::{validate_admin_request, validate_admin_request_with_bucket};
use crate::admin::router::{AdminOperation, Operation, S3Router}; use crate::admin::router::{AdminOperation, Operation, S3Router};
use crate::app::context::get_global_app_context;
use crate::auth::{check_key_valid, get_session_token}; use crate::auth::{check_key_valid, get_session_token};
use crate::server::ADMIN_PREFIX; use crate::server::ADMIN_PREFIX;
use hyper::{Method, StatusCode}; use hyper::{Method, StatusCode};
use matchit::Params; use matchit::Params;
use rustfs_ecstore::bucket::metadata_sys::BucketMetadataSys;
use rustfs_ecstore::bucket::quota::checker::QuotaChecker; use rustfs_ecstore::bucket::quota::checker::QuotaChecker;
use rustfs_ecstore::bucket::quota::{BucketQuota, QuotaError, QuotaOperation}; use rustfs_ecstore::bucket::quota::{BucketQuota, QuotaError, QuotaOperation};
use rustfs_policy::policy::action::{Action, AdminAction, S3Action}; use rustfs_policy::policy::action::{Action, AdminAction, S3Action};
use s3s::{Body, S3Request, S3Response, S3Result, s3_error}; use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json; use serde_json;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@@ -81,6 +85,25 @@ pub struct ClearBucketQuotaHandler;
pub struct GetBucketQuotaStatsHandler; pub struct GetBucketQuotaStatsHandler;
pub struct CheckBucketQuotaHandler; pub struct CheckBucketQuotaHandler;
fn bucket_metadata_from_context() -> Option<Arc<RwLock<BucketMetadataSys>>> {
get_global_app_context().and_then(|context| context.bucket_metadata().handle())
}
async fn current_usage_from_context(bucket: &str) -> u64 {
let Some(store) = get_global_app_context().map(|context| context.object_store()) else {
return 0;
};
match rustfs_ecstore::data_usage::load_data_usage_from_backend(store).await {
Ok(data_usage_info) => data_usage_info
.buckets_usage
.get(bucket)
.map(|bucket_usage| bucket_usage.size)
.unwrap_or(0),
Err(_) => 0,
}
}
pub fn register_quota_route(r: &mut S3Router<AdminOperation>) -> std::io::Result<()> { pub fn register_quota_route(r: &mut S3Router<AdminOperation>) -> std::io::Result<()> {
r.insert( r.insert(
Method::PUT, Method::PUT,
@@ -164,8 +187,7 @@ impl Operation for SetBucketQuotaHandler {
let quota = BucketQuota::new(request.quota); let quota = BucketQuota::new(request.quota);
let metadata_sys_lock = rustfs_ecstore::bucket::metadata_sys::GLOBAL_BucketMetadataSys let metadata_sys_lock = bucket_metadata_from_context()
.get()
.ok_or_else(|| s3_error!(InternalError, "{}", rustfs_config::QUOTA_METADATA_SYSTEM_ERROR_MSG))?; .ok_or_else(|| s3_error!(InternalError, "{}", rustfs_config::QUOTA_METADATA_SYSTEM_ERROR_MSG))?;
let mut quota_checker = QuotaChecker::new(metadata_sys_lock.clone()); let mut quota_checker = QuotaChecker::new(metadata_sys_lock.clone());
@@ -175,18 +197,7 @@ impl Operation for SetBucketQuotaHandler {
.map_err(|e| s3_error!(InternalError, "Failed to set quota: {}", e))?; .map_err(|e| s3_error!(InternalError, "Failed to set quota: {}", e))?;
// Get real-time usage from data usage system // Get real-time usage from data usage system
let current_usage = if let Some(store) = rustfs_ecstore::global::GLOBAL_OBJECT_API.get() { let current_usage = current_usage_from_context(&bucket).await;
match rustfs_ecstore::data_usage::load_data_usage_from_backend(store.clone()).await {
Ok(data_usage_info) => data_usage_info
.buckets_usage
.get(&bucket)
.map(|bucket_usage| bucket_usage.size)
.unwrap_or(0),
Err(_) => 0,
}
} else {
0
};
let response = BucketQuotaResponse { let response = BucketQuotaResponse {
bucket, bucket,
@@ -231,9 +242,8 @@ impl Operation for GetBucketQuotaHandler {
) )
.await?; .await?;
let metadata_sys_lock = rustfs_ecstore::bucket::metadata_sys::GLOBAL_BucketMetadataSys let metadata_sys_lock =
.get() bucket_metadata_from_context().ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
.ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
let quota_checker = QuotaChecker::new(metadata_sys_lock.clone()); let quota_checker = QuotaChecker::new(metadata_sys_lock.clone());
@@ -288,9 +298,8 @@ impl Operation for ClearBucketQuotaHandler {
info!("Clearing quota for bucket: {}", bucket); info!("Clearing quota for bucket: {}", bucket);
let metadata_sys_lock = rustfs_ecstore::bucket::metadata_sys::GLOBAL_BucketMetadataSys let metadata_sys_lock =
.get() bucket_metadata_from_context().ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
.ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
let mut quota_checker = QuotaChecker::new(metadata_sys_lock.clone()); let mut quota_checker = QuotaChecker::new(metadata_sys_lock.clone());
@@ -304,18 +313,7 @@ impl Operation for ClearBucketQuotaHandler {
info!("Successfully cleared quota for bucket: {}", bucket); info!("Successfully cleared quota for bucket: {}", bucket);
// Get real-time usage from data usage system // Get real-time usage from data usage system
let current_usage = if let Some(store) = rustfs_ecstore::global::GLOBAL_OBJECT_API.get() { let current_usage = current_usage_from_context(&bucket).await;
match rustfs_ecstore::data_usage::load_data_usage_from_backend(store.clone()).await {
Ok(data_usage_info) => data_usage_info
.buckets_usage
.get(&bucket)
.map(|bucket_usage| bucket_usage.size)
.unwrap_or(0),
Err(_) => 0,
}
} else {
0
};
let response = BucketQuotaResponse { let response = BucketQuotaResponse {
bucket, bucket,
@@ -360,9 +358,8 @@ impl Operation for GetBucketQuotaStatsHandler {
) )
.await?; .await?;
let metadata_sys_lock = rustfs_ecstore::bucket::metadata_sys::GLOBAL_BucketMetadataSys let metadata_sys_lock =
.get() bucket_metadata_from_context().ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
.ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
let quota_checker = QuotaChecker::new(metadata_sys_lock.clone()); let quota_checker = QuotaChecker::new(metadata_sys_lock.clone());
@@ -445,9 +442,8 @@ impl Operation for CheckBucketQuotaHandler {
bucket, request.operation_type, request.operation_size bucket, request.operation_type, request.operation_size
); );
let metadata_sys_lock = rustfs_ecstore::bucket::metadata_sys::GLOBAL_BucketMetadataSys let metadata_sys_lock =
.get() bucket_metadata_from_context().ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
.ok_or_else(|| s3_error!(InternalError, "Bucket metadata system not initialized"))?;
let quota_checker = QuotaChecker::new(metadata_sys_lock.clone()); let quota_checker = QuotaChecker::new(metadata_sys_lock.clone());
+3 -2
View File
@@ -13,10 +13,11 @@
// limitations under the License. // limitations under the License.
use crate::admin::router::Operation; use crate::admin::router::Operation;
use crate::app::context::get_global_app_context;
use http::StatusCode; use http::StatusCode;
use hyper::Uri; use hyper::Uri;
use matchit::Params; use matchit::Params;
use rustfs_ecstore::{GLOBAL_Endpoints, rpc::PeerRestClient}; use rustfs_ecstore::rpc::PeerRestClient;
use rustfs_madmin::service_commands::ServiceTraceOpts; use rustfs_madmin::service_commands::ServiceTraceOpts;
use s3s::{Body, S3Request, S3Response, S3Result, s3_error}; use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
use tracing::warn; use tracing::warn;
@@ -42,7 +43,7 @@ impl Operation for Trace {
let _trace_opts = extract_trace_options(&req.uri)?; let _trace_opts = extract_trace_options(&req.uri)?;
// let (tx, rx) = mpsc::channel(10000); // let (tx, rx) = mpsc::channel(10000);
let _peers = match GLOBAL_Endpoints.get() { let _peers = match get_global_app_context().and_then(|context| context.endpoints().handle()) {
Some(ep) => PeerRestClient::new_clients(ep.clone()).await, Some(ep) => PeerRestClient::new_clients(ep.clone()).await,
None => (Vec::new(), Vec::new()), None => (Vec::new(), Vec::new()),
}; };
+6 -2
View File
@@ -165,6 +165,10 @@ impl DefaultBucketUsecase {
self.context.clone() self.context.clone()
} }
fn global_region(&self) -> Option<String> {
self.context.as_ref().and_then(|context| context.region().get())
}
#[instrument( #[instrument(
level = "debug", level = "debug",
skip(self, req), skip(self, req),
@@ -425,7 +429,7 @@ impl DefaultBucketUsecase {
.await .await
.map_err(ApiError::from)?; .map_err(ApiError::from)?;
if let Some(region) = rustfs_ecstore::global::get_global_region() { if let Some(region) = self.global_region() {
return Ok(S3Response::new(GetBucketLocationOutput { return Ok(S3Response::new(GetBucketLocationOutput {
location_constraint: Some(BucketLocationConstraint::from(region)), location_constraint: Some(BucketLocationConstraint::from(region)),
})); }));
@@ -1187,7 +1191,7 @@ impl DefaultBucketUsecase {
.await .await
.map_err(ApiError::from)?; .map_err(ApiError::from)?;
let region = resolve_notification_region(rustfs_ecstore::global::get_global_region(), request_region); let region = resolve_notification_region(self.global_region(), request_region);
let notify = self let notify = self
.context .context
.as_ref() .as_ref()
+26
View File
@@ -21,6 +21,7 @@ use async_trait::async_trait;
use rustfs_ecstore::GLOBAL_Endpoints; use rustfs_ecstore::GLOBAL_Endpoints;
use rustfs_ecstore::bucket::metadata_sys::{BucketMetadataSys, GLOBAL_BucketMetadataSys}; use rustfs_ecstore::bucket::metadata_sys::{BucketMetadataSys, GLOBAL_BucketMetadataSys};
use rustfs_ecstore::endpoints::EndpointServerPools; use rustfs_ecstore::endpoints::EndpointServerPools;
use rustfs_ecstore::global::get_global_region;
use rustfs_ecstore::store::ECStore; use rustfs_ecstore::store::ECStore;
use rustfs_iam::{store::object::ObjectStore, sys::IamSys}; use rustfs_iam::{store::object::ObjectStore, sys::IamSys};
use rustfs_kms::KmsServiceManager; use rustfs_kms::KmsServiceManager;
@@ -65,6 +66,11 @@ pub trait EndpointsInterface: Send + Sync {
fn handle(&self) -> Option<EndpointServerPools>; fn handle(&self) -> Option<EndpointServerPools>;
} }
/// Region interface for application-layer use-cases.
pub trait RegionInterface: Send + Sync {
fn get(&self) -> Option<String>;
}
/// Default IAM interface adapter. /// Default IAM interface adapter.
pub struct IamHandle { pub struct IamHandle {
iam: Arc<IamSys<ObjectStore>>, iam: Arc<IamSys<ObjectStore>>,
@@ -147,6 +153,16 @@ impl EndpointsInterface for EndpointsHandle {
} }
} }
/// Default region interface adapter.
#[derive(Default)]
pub struct RegionHandle;
impl RegionInterface for RegionHandle {
fn get(&self) -> Option<String> {
get_global_region()
}
}
/// Application-layer context with explicit dependencies. /// Application-layer context with explicit dependencies.
#[derive(Clone)] #[derive(Clone)]
pub struct AppContext { pub struct AppContext {
@@ -156,6 +172,7 @@ pub struct AppContext {
notify: Arc<dyn NotifyInterface>, notify: Arc<dyn NotifyInterface>,
bucket_metadata: Arc<dyn BucketMetadataInterface>, bucket_metadata: Arc<dyn BucketMetadataInterface>,
endpoints: Arc<dyn EndpointsInterface>, endpoints: Arc<dyn EndpointsInterface>,
region: Arc<dyn RegionInterface>,
} }
impl AppContext { impl AppContext {
@@ -167,6 +184,7 @@ impl AppContext {
notify: default_notify_interface(), notify: default_notify_interface(),
bucket_metadata: default_bucket_metadata_interface(), bucket_metadata: default_bucket_metadata_interface(),
endpoints: default_endpoints_interface(), endpoints: default_endpoints_interface(),
region: default_region_interface(),
} }
} }
@@ -201,6 +219,10 @@ impl AppContext {
pub fn endpoints(&self) -> Arc<dyn EndpointsInterface> { pub fn endpoints(&self) -> Arc<dyn EndpointsInterface> {
self.endpoints.clone() self.endpoints.clone()
} }
pub fn region(&self) -> Arc<dyn RegionInterface> {
self.region.clone()
}
} }
pub fn default_notify_interface() -> Arc<dyn NotifyInterface> { pub fn default_notify_interface() -> Arc<dyn NotifyInterface> {
@@ -215,6 +237,10 @@ pub fn default_endpoints_interface() -> Arc<dyn EndpointsInterface> {
Arc::new(EndpointsHandle) Arc::new(EndpointsHandle)
} }
pub fn default_region_interface() -> Arc<dyn RegionInterface> {
Arc::new(RegionHandle)
}
static GLOBAL_APP_CONTEXT: OnceLock<Arc<AppContext>> = OnceLock::new(); static GLOBAL_APP_CONTEXT: OnceLock<Arc<AppContext>> = OnceLock::new();
/// Initialize global application context once and return the canonical instance. /// Initialize global application context once and return the canonical instance.
+5 -1
View File
@@ -164,6 +164,10 @@ impl DefaultMultipartUsecase {
self.context.as_ref().and_then(|context| context.bucket_metadata().handle()) self.context.as_ref().and_then(|context| context.bucket_metadata().handle())
} }
fn global_region(&self) -> Option<String> {
self.context.as_ref().and_then(|context| context.region().get())
}
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
pub async fn execute_abort_multipart_upload( pub async fn execute_abort_multipart_upload(
&self, &self,
@@ -418,7 +422,7 @@ impl DefaultMultipartUsecase {
} }
} }
let region = rustfs_ecstore::global::get_global_region().unwrap_or_else(|| "us-east-1".to_string()); let region = self.global_region().unwrap_or_else(|| "us-east-1".to_string());
let output = CompleteMultipartUploadOutput { let output = CompleteMultipartUploadOutput {
bucket: Some(bucket.clone()), bucket: Some(bucket.clone()),
key: Some(key.clone()), key: Some(key.clone()),