mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-09 22:59:59 +00:00
feat(replication): add bandwidth-aware reporting for bucket replication metrics (#2141)
This commit is contained in:
@@ -24,7 +24,11 @@ use rustfs_config::MAX_ADMIN_REQUEST_BODY_SIZE;
|
||||
use rustfs_ecstore::bucket::bucket_target_sys::BucketTargetSys;
|
||||
use rustfs_ecstore::bucket::metadata::BUCKET_TARGETS_FILE;
|
||||
use rustfs_ecstore::bucket::metadata_sys;
|
||||
use rustfs_ecstore::bucket::metadata_sys::get_replication_config;
|
||||
use rustfs_ecstore::bucket::replication::BucketStats;
|
||||
use rustfs_ecstore::bucket::replication::GLOBAL_REPLICATION_STATS;
|
||||
use rustfs_ecstore::bucket::target::BucketTarget;
|
||||
use rustfs_ecstore::error::StorageError;
|
||||
use rustfs_ecstore::global::global_rustfs_port;
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::store_api::{BucketOperations, BucketOptions};
|
||||
@@ -32,7 +36,7 @@ use rustfs_policy::policy::action::{Action, AdminAction};
|
||||
use s3s::header::CONTENT_TYPE;
|
||||
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, s3_error};
|
||||
use std::collections::HashMap;
|
||||
use tracing::{debug, error, warn};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use url::Host;
|
||||
|
||||
fn extract_query_params(uri: &Uri) -> HashMap<String, String> {
|
||||
@@ -98,12 +102,52 @@ pub struct GetReplicationMetricsHandler {}
|
||||
#[async_trait::async_trait]
|
||||
impl Operation for GetReplicationMetricsHandler {
|
||||
async fn call(&self, req: S3Request<Body>, _params: Params<'_, '_>) -> S3Result<S3Response<(StatusCode, Body)>> {
|
||||
error!("GetReplicationMetricsHandler");
|
||||
validate_replication_admin_request(&req, AdminAction::GetReplicationMetricsAction).await?;
|
||||
|
||||
let queries = extract_query_params(&req.uri);
|
||||
if let Some(bucket) = queries.get("bucket") {
|
||||
error!("get bucket:{} metrics", bucket);
|
||||
|
||||
let Some(bucket) = queries.get("bucket") else {
|
||||
return Err(s3_error!(InvalidRequest, "bucket is required"));
|
||||
};
|
||||
|
||||
if bucket.is_empty() {
|
||||
return Err(s3_error!(InvalidRequest, "bucket is required"));
|
||||
}
|
||||
Ok(S3Response::new((StatusCode::OK, Body::from("Ok".to_string()))))
|
||||
|
||||
let Some(store) = new_object_layer_fn() else {
|
||||
return Err(S3Error::with_message(S3ErrorCode::InternalError, "Not init".to_string()));
|
||||
};
|
||||
|
||||
store
|
||||
.get_bucket_info(bucket, &BucketOptions::default())
|
||||
.await
|
||||
.map_err(ApiError::from)?;
|
||||
|
||||
if let Err(err) = get_replication_config(bucket).await {
|
||||
if err == StorageError::ConfigNotFound {
|
||||
info!("replication configuration not found for bucket '{}'", bucket);
|
||||
return Err(S3Error::with_message(
|
||||
S3ErrorCode::ReplicationConfigurationNotFoundError,
|
||||
"replication not found".to_string(),
|
||||
));
|
||||
}
|
||||
error!("get_replication_config unexpected error: {:?}", err);
|
||||
return Err(ApiError::from(err).into());
|
||||
}
|
||||
|
||||
// TODO cluster cache
|
||||
// In actual implementation, statistics would be obtained from cluster
|
||||
// This is simplified to get from local cache
|
||||
let bucket_stats = match GLOBAL_REPLICATION_STATS.get() {
|
||||
Some(s) => s.get_latest_replication_stats(bucket).await,
|
||||
None => BucketStats::default(),
|
||||
};
|
||||
|
||||
let data = serde_json::to_vec(&bucket_stats.replication_stats)
|
||||
.map_err(|_| S3Error::with_message(S3ErrorCode::InternalError, "serialize failed"))?;
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
Ok(S3Response::with_headers((StatusCode::OK, Body::from(data)), headers))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user