refactor: Reimplement bucket replication system with enhanced architecture (#590)

* feat:refactor replication

* use aws sdk for replication client

* refactor/replication

* merge main

* fix lifecycle test
This commit is contained in:
weisd
2025-09-26 14:27:53 +08:00
committed by GitHub
parent 9b029d18b2
commit 90f21a9102
91 changed files with 10532 additions and 4917 deletions
@@ -44,6 +44,8 @@ pub struct GetObjectOptions {
pub internal: AdvancedGetOptions,
}
pub type StatObjectOptions = GetObjectOptions;
impl Default for GetObjectOptions {
fn default() -> Self {
Self {
+7 -8
View File
@@ -46,11 +46,11 @@ pub struct RemoveBucketOptions {
#[derive(Debug)]
#[allow(dead_code)]
pub struct AdvancedRemoveOptions {
replication_delete_marker: bool,
replication_status: ReplicationStatus,
replication_mtime: OffsetDateTime,
replication_request: bool,
replication_validity_check: bool,
pub replication_delete_marker: bool,
pub replication_status: ReplicationStatus,
pub replication_mtime: Option<OffsetDateTime>,
pub replication_request: bool,
pub replication_validity_check: bool,
}
impl Default for AdvancedRemoveOptions {
@@ -58,7 +58,7 @@ impl Default for AdvancedRemoveOptions {
Self {
replication_delete_marker: false,
replication_status: ReplicationStatus::from_static(ReplicationStatus::PENDING),
replication_mtime: OffsetDateTime::now_utc(),
replication_mtime: None,
replication_request: false,
replication_validity_check: false,
}
@@ -140,8 +140,7 @@ impl TransitionClient {
}
pub async fn remove_object(&self, bucket_name: &str, object_name: &str, opts: RemoveObjectOptions) -> Option<std::io::Error> {
let res = self.remove_object_inner(bucket_name, object_name, opts).await.expect("err");
res.err
self.remove_object_inner(bucket_name, object_name, opts).await.err()
}
pub async fn remove_object_inner(
+59 -13
View File
@@ -23,6 +23,7 @@ use http::{HeaderMap, HeaderValue};
use rustfs_utils::EMPTY_STRING_SHA256_HASH;
use std::{collections::HashMap, str::FromStr};
use tokio::io::BufReader;
use tracing::warn;
use uuid::Uuid;
use crate::client::{
@@ -30,7 +31,10 @@ use crate::client::{
api_get_options::GetObjectOptions,
transition_api::{ObjectInfo, ReadCloser, ReaderImpl, RequestMetadata, TransitionClient, to_object_info},
};
use s3s::header::{X_AMZ_DELETE_MARKER, X_AMZ_VERSION_ID};
use s3s::{
dto::VersioningConfiguration,
header::{X_AMZ_DELETE_MARKER, X_AMZ_VERSION_ID},
};
impl TransitionClient {
pub async fn bucket_exists(&self, bucket_name: &str) -> Result<bool, std::io::Error> {
@@ -58,8 +62,14 @@ impl TransitionClient {
.await;
if let Ok(resp) = resp {
if resp.status() != http::StatusCode::OK {
return Ok(false);
}
let b = resp.body().bytes().expect("err").to_vec();
let resperr = http_resp_to_error_response(&resp, b, bucket_name, "");
warn!("bucket exists, resp: {:?}, resperr: {:?}", resp, resperr);
/*if to_error_response(resperr).code == "NoSuchBucket" {
return Ok(false);
}
@@ -70,6 +80,46 @@ impl TransitionClient {
Ok(true)
}
pub async fn get_bucket_versioning(&self, bucket_name: &str) -> Result<VersioningConfiguration, std::io::Error> {
let mut query_values = HashMap::new();
query_values.insert("versioning".to_string(), "".to_string());
let resp = self
.execute_method(
http::Method::GET,
&mut RequestMetadata {
bucket_name: bucket_name.to_string(),
object_name: "".to_string(),
query_values,
custom_header: HeaderMap::new(),
content_sha256_hex: EMPTY_STRING_SHA256_HASH.to_string(),
content_md5_base64: "".to_string(),
content_body: ReaderImpl::Body(Bytes::new()),
content_length: 0,
stream_sha256: false,
trailer: HeaderMap::new(),
pre_sign_url: Default::default(),
add_crc: Default::default(),
extra_pre_sign_header: Default::default(),
bucket_location: Default::default(),
expires: Default::default(),
},
)
.await;
match resp {
Ok(resp) => {
let b = resp.body().bytes().expect("get bucket versioning err").to_vec();
let resperr = http_resp_to_error_response(&resp, b, bucket_name, "");
warn!("get bucket versioning, resp: {:?}, resperr: {:?}", resp, resperr);
Ok(VersioningConfiguration::default())
}
Err(err) => Err(std::io::Error::other(err)),
}
}
pub async fn stat_object(
&self,
bucket_name: &str,
@@ -131,24 +181,20 @@ impl TransitionClient {
..Default::default()
};
return Ok(ObjectInfo {
version_id: match Uuid::from_str(h.get(X_AMZ_VERSION_ID).unwrap().to_str().unwrap()) {
Ok(v) => v,
Err(e) => {
return Err(std::io::Error::other(e));
}
},
version_id: h
.get(X_AMZ_VERSION_ID)
.and_then(|v| v.to_str().ok())
.and_then(|s| Uuid::from_str(s).ok()),
is_delete_marker: delete_marker,
..Default::default()
});
//err_resp
}
return Ok(ObjectInfo {
version_id: match Uuid::from_str(h.get(X_AMZ_VERSION_ID).unwrap().to_str().unwrap()) {
Ok(v) => v,
Err(e) => {
return Err(std::io::Error::other(e));
}
},
version_id: h
.get(X_AMZ_VERSION_ID)
.and_then(|v| v.to_str().ok())
.and_then(|s| Uuid::from_str(s).ok()),
is_delete_marker: delete_marker,
replication_ready: replication_ready,
..Default::default()
@@ -36,6 +36,7 @@ use s3s::S3ErrorCode;
use super::constants::UNSIGNED_PAYLOAD;
use super::credentials::SignatureType;
#[derive(Debug, Clone)]
pub struct BucketLocationCache {
items: HashMap<String, String>,
}
+10 -8
View File
@@ -89,6 +89,7 @@ pub enum ReaderImpl {
pub type ReadCloser = BufReader<Cursor<Vec<u8>>>;
#[derive(Debug)]
pub struct TransitionClient {
pub endpoint_url: Url,
pub creds_provider: Arc<Mutex<Credentials<Static>>>,
@@ -809,6 +810,7 @@ impl TransitionCore {
}
}
#[derive(Debug, Clone, Default)]
pub struct PutObjectPartOptions {
pub md5_base64: String,
pub sha256_hex: String,
@@ -820,23 +822,23 @@ pub struct PutObjectPartOptions {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ObjectInfo {
pub etag: String,
pub etag: Option<String>,
pub name: String,
pub mod_time: OffsetDateTime,
pub size: usize,
pub mod_time: Option<OffsetDateTime>,
pub size: i64,
pub content_type: Option<String>,
#[serde(skip)]
pub metadata: HeaderMap,
pub user_metadata: HashMap<String, String>,
pub user_tags: String,
pub user_tag_count: i64,
pub user_tag_count: usize,
#[serde(skip)]
pub owner: Owner,
//pub grant: Vec<Grant>,
pub storage_class: String,
pub is_latest: bool,
pub is_delete_marker: bool,
pub version_id: Uuid,
pub version_id: Option<Uuid>,
#[serde(skip, default = "replication_status_default")]
pub replication_status: ReplicationStatus,
@@ -862,9 +864,9 @@ fn replication_status_default() -> ReplicationStatus {
impl Default for ObjectInfo {
fn default() -> Self {
Self {
etag: "".to_string(),
etag: None,
name: "".to_string(),
mod_time: OffsetDateTime::now_utc(),
mod_time: None,
size: 0,
content_type: None,
metadata: HeaderMap::new(),
@@ -875,7 +877,7 @@ impl Default for ObjectInfo {
storage_class: "".to_string(),
is_latest: false,
is_delete_marker: false,
version_id: Uuid::nil(),
version_id: None,
replication_status: ReplicationStatus::from_static(ReplicationStatus::PENDING),
replication_ready: false,
expiration: OffsetDateTime::now_utc(),