From db253c01a96645720304a252fc70ff432b094aa3 Mon Sep 17 00:00:00 2001 From: heihutu <30542132+heihutu@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:24:56 +0800 Subject: [PATCH] refactor: replace chrono with jiff for time handling (#1582) Co-authored-by: houseme --- Cargo.lock | 55 ++++++- Cargo.toml | 1 + crates/kms/Cargo.toml | 2 +- crates/kms/src/backends/local.rs | 23 +-- crates/kms/src/backends/vault.rs | 21 +-- crates/kms/src/cache.rs | 7 +- crates/kms/src/encryption/service.rs | 9 +- crates/kms/src/types.rs | 24 +-- rustfs/Cargo.toml | 1 + rustfs/src/admin/console.rs | 2 +- rustfs/src/admin/handlers.rs | 2 +- rustfs/src/init.rs | 3 +- rustfs/src/main.rs | 14 +- rustfs/src/profiling.rs | 3 +- rustfs/src/server/audit.rs | 4 +- rustfs/src/server/http.rs | 2 +- rustfs/src/server/runtime.rs | 4 +- rustfs/src/storage/ecfs.rs | 17 +-- rustfs/src/update.rs | 14 +- scripts/run.ps1 | 216 ++++++++++++++++++++++----- 20 files changed, 307 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a79da608a..cce8fdbba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4915,6 +4915,47 @@ dependencies = [ "tracing", ] +[[package]] +name = "jiff" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -6594,6 +6635,15 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -7617,6 +7667,7 @@ dependencies = [ "hyper", "hyper-util", "jemalloc_pprof", + "jiff", "libc", "libsystemd", "libunftp", @@ -7959,7 +8010,7 @@ dependencies = [ "async-trait", "base64", "chacha20poly1305", - "chrono", + "jiff", "md5 0.8.0", "moka", "rand 0.10.0-rc.6", @@ -8220,7 +8271,7 @@ dependencies = [ "serde_json", "serial_test", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "tokio", "tokio-test", diff --git a/Cargo.toml b/Cargo.toml index 06442c55c..eb570698d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,6 +163,7 @@ zeroize = { version = "1.8.2", features = ["derive"] } # Time and Date chrono = { version = "0.4.43", features = ["serde"] } humantime = "2.3.0" +jiff = { version = "0.2.18", features = ["serde"] } time = { version = "0.3.45", features = ["std", "parsing", "formatting", "macros", "serde"] } # Utilities and Tools diff --git a/crates/kms/Cargo.toml b/crates/kms/Cargo.toml index 912121c62..2a9b1f0f5 100644 --- a/crates/kms/Cargo.toml +++ b/crates/kms/Cargo.toml @@ -32,7 +32,7 @@ workspace = true async-trait = { workspace = true } tokio = { workspace = true, features = ["full"] } uuid = { workspace = true, features = ["serde"] } -chrono = { workspace = true, features = ["serde"] } +jiff = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tracing = { workspace = true } diff --git a/crates/kms/src/backends/local.rs b/crates/kms/src/backends/local.rs index d01ab5aa1..f96553004 100644 --- a/crates/kms/src/backends/local.rs +++ b/crates/kms/src/backends/local.rs @@ -24,6 +24,7 @@ use aes_gcm::{ aead::{Aead, KeyInit}, }; use async_trait::async_trait; +use jiff::Zoned; use rand::Rng; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -51,8 +52,8 @@ struct StoredMasterKey { status: KeyStatus, description: Option, metadata: HashMap, - created_at: chrono::DateTime, - rotated_at: Option>, + created_at: Zoned, + rotated_at: Option, created_by: Option, /// Encrypted key material (32 bytes for AES-256) encrypted_key_material: Vec, @@ -69,7 +70,7 @@ struct DataKeyEnvelope { encrypted_key: Vec, nonce: Vec, encryption_context: HashMap, - created_at: chrono::DateTime, + created_at: Zoned, } impl LocalKmsClient { @@ -182,8 +183,8 @@ impl LocalKmsClient { status: master_key.status.clone(), description: master_key.description.clone(), metadata: master_key.metadata.clone(), - created_at: master_key.created_at, - rotated_at: master_key.rotated_at, + created_at: master_key.created_at.clone(), + rotated_at: master_key.rotated_at.clone(), created_by: master_key.created_by.clone(), encrypted_key_material, nonce, @@ -317,7 +318,7 @@ impl KmsClient for LocalKmsClient { encrypted_key: encrypted_key.clone(), nonce, encryption_context: request.encryption_context.clone(), - created_at: chrono::Utc::now(), + created_at: Zoned::now(), }; // Serialize the envelope as the ciphertext @@ -561,7 +562,7 @@ impl KmsClient for LocalKmsClient { let mut master_key = self.load_master_key(key_id).await?; master_key.version += 1; - master_key.rotated_at = Some(chrono::Utc::now()); + master_key.rotated_at = Some(Zoned::now()); // Generate new key material let key_material = Self::generate_key_material(); @@ -648,7 +649,7 @@ impl KmsBackend for LocalKmsBackend { key_state: KeyState::Enabled, key_usage: request.key_usage, description: request.description, - creation_date: chrono::Utc::now(), + creation_date: Zoned::now(), deletion_date: None, origin: "KMS".to_string(), key_manager: "CUSTOMER".to_string(), @@ -768,7 +769,7 @@ impl KmsBackend for LocalKmsBackend { key_usage: master_key.usage, key_state: KeyState::PendingDeletion, // AWS KMS compatibility creation_date: master_key.created_at, - deletion_date: Some(chrono::Utc::now()), + deletion_date: Some(Zoned::now()), key_manager: "CUSTOMER".to_string(), origin: "AWS_KMS".to_string(), tags: master_key.metadata, @@ -786,10 +787,10 @@ impl KmsBackend for LocalKmsBackend { return Err(KmsError::invalid_parameter("pending_window_in_days must be between 7 and 30".to_string())); } - let deletion_date = chrono::Utc::now() + chrono::Duration::days(days as i64); + let deletion_date = Zoned::now() + jiff::Span::new().days(days as i64); master_key.status = KeyStatus::PendingDeletion; - (Some(deletion_date.to_rfc3339()), Some(deletion_date)) + (Some(deletion_date.to_string()), Some(deletion_date)) }; // Save the updated key to disk - preserve existing key material! diff --git a/crates/kms/src/backends/vault.rs b/crates/kms/src/backends/vault.rs index 1d1768bf9..9ccd233e1 100644 --- a/crates/kms/src/backends/vault.rs +++ b/crates/kms/src/backends/vault.rs @@ -20,6 +20,7 @@ use crate::error::{KmsError, Result}; use crate::types::*; use async_trait::async_trait; use base64::{Engine as _, engine::general_purpose}; +use jiff::Zoned; use rand::RngCore; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -47,7 +48,7 @@ struct VaultKeyData { /// Key usage type usage: KeyUsage, /// Key creation timestamp - created_at: chrono::DateTime, + created_at: Zoned, /// Key status status: KeyStatus, /// Key version @@ -156,7 +157,7 @@ impl VaultKmsClient { let key_data = VaultKeyData { algorithm: "AES_256".to_string(), usage: request.key_usage.clone(), - created_at: chrono::Utc::now(), + created_at: Zoned::now(), status: KeyStatus::Active, version: 1, description: request.description.clone(), @@ -252,7 +253,7 @@ impl KmsClient for VaultKmsClient { .map_err(|e| KmsError::cryptographic_error("decode", e.to_string()))?, key_spec: request.key_spec.clone(), metadata: request.encryption_context.clone(), - created_at: chrono::Utc::now(), + created_at: Zoned::now(), }) } @@ -302,7 +303,7 @@ impl KmsClient for VaultKmsClient { let key_data = VaultKeyData { algorithm: algorithm.to_string(), usage: KeyUsage::EncryptDecrypt, - created_at: chrono::Utc::now(), + created_at: Zoned::now(), status: KeyStatus::Active, version: 1, description: None, @@ -458,7 +459,7 @@ impl KmsClient for VaultKmsClient { description: None, // Rotate preserves existing description (would need key lookup) metadata: key_data.metadata, created_at: key_data.created_at, - rotated_at: Some(chrono::Utc::now()), + rotated_at: Some(Zoned::now()), created_by: None, }; @@ -549,7 +550,7 @@ impl KmsBackend for VaultKmsBackend { key_state: KeyState::Enabled, key_usage: request.key_usage, description: request.description, - creation_date: chrono::Utc::now(), + creation_date: Zoned::now(), deletion_date: None, origin: "VAULT".to_string(), key_manager: "VAULT".to_string(), @@ -664,7 +665,7 @@ impl KmsBackend for VaultKmsBackend { } else { // For non-pending keys, mark as PendingDeletion key_metadata.key_state = KeyState::PendingDeletion; - key_metadata.deletion_date = Some(chrono::Utc::now()); + key_metadata.deletion_date = Some(Zoned::now()); // Update the key metadata in Vault storage to reflect the new state self.update_key_metadata_in_storage(key_id, &key_metadata).await?; @@ -680,14 +681,14 @@ impl KmsBackend for VaultKmsBackend { )); } - let deletion_date = chrono::Utc::now() + chrono::Duration::days(days as i64); + let deletion_date = Zoned::now() + jiff::Span::new().days(days as i64); key_metadata.key_state = KeyState::PendingDeletion; - key_metadata.deletion_date = Some(deletion_date); + key_metadata.deletion_date = Some(deletion_date.clone()); // Update the key metadata in Vault storage to reflect the new state self.update_key_metadata_in_storage(key_id, &key_metadata).await?; - Some(deletion_date.to_rfc3339()) + Some(deletion_date.to_string()) }; Ok(DeleteKeyResponse { diff --git a/crates/kms/src/cache.rs b/crates/kms/src/cache.rs index e8d445856..858d7a6e4 100644 --- a/crates/kms/src/cache.rs +++ b/crates/kms/src/cache.rs @@ -154,6 +154,7 @@ impl KmsCache { mod tests { use super::*; use crate::types::{KeyState, KeyUsage}; + use jiff::Zoned; use std::time::Duration; #[derive(Debug, Clone)] @@ -202,7 +203,7 @@ mod tests { key_state: KeyState::Enabled, key_usage: KeyUsage::EncryptDecrypt, description: Some("Test key".to_string()), - creation_date: chrono::Utc::now(), + creation_date: Zoned::now(), deletion_date: None, origin: "KMS".to_string(), key_manager: "CUSTOMER".to_string(), @@ -252,7 +253,7 @@ mod tests { key_state: KeyState::Enabled, key_usage: KeyUsage::EncryptDecrypt, description: Some("TTL test key".to_string()), - creation_date: chrono::Utc::now(), + creation_date: Zoned::now(), deletion_date: None, origin: "KMS".to_string(), key_manager: "CUSTOMER".to_string(), @@ -283,7 +284,7 @@ mod tests { key_state: KeyState::Enabled, key_usage: KeyUsage::EncryptDecrypt, description: None, - creation_date: chrono::Utc::now(), + creation_date: Zoned::now(), deletion_date: None, origin: "KMS".to_string(), key_manager: "CUSTOMER".to_string(), diff --git a/crates/kms/src/encryption/service.rs b/crates/kms/src/encryption/service.rs index f4b973eb0..ef361bc1f 100644 --- a/crates/kms/src/encryption/service.rs +++ b/crates/kms/src/encryption/service.rs @@ -19,6 +19,7 @@ use crate::error::{KmsError, Result}; use crate::manager::KmsManager; use crate::types::*; use base64::Engine; +use jiff::Zoned; use rand::random; use std::collections::HashMap; use std::io::Cursor; @@ -339,7 +340,7 @@ impl ObjectEncryptionService { iv, tag: Some(tag), encryption_context: context, - encrypted_at: chrono::Utc::now(), + encrypted_at: Zoned::now(), original_size, encrypted_data_key: data_key.ciphertext_blob, }; @@ -482,7 +483,7 @@ impl ObjectEncryptionService { iv, tag: Some(tag), encryption_context: context, - encrypted_at: chrono::Utc::now(), + encrypted_at: Zoned::now(), original_size, encrypted_data_key: Vec::new(), // Empty for SSE-C }; @@ -695,7 +696,7 @@ impl ObjectEncryptionService { iv, tag, encryption_context, - encrypted_at: chrono::Utc::now(), + encrypted_at: Zoned::now(), original_size: 0, // Not available from headers encrypted_data_key, }) @@ -809,7 +810,7 @@ mod tests { iv: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], tag: Some(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]), encryption_context: HashMap::from([("bucket".to_string(), "test-bucket".to_string())]), - encrypted_at: chrono::Utc::now(), + encrypted_at: Zoned::now(), original_size: 100, encrypted_data_key: vec![1, 2, 3, 4], }; diff --git a/crates/kms/src/types.rs b/crates/kms/src/types.rs index e53d0b498..d04c4b0b6 100644 --- a/crates/kms/src/types.rs +++ b/crates/kms/src/types.rs @@ -14,7 +14,7 @@ //! Core type definitions for KMS operations -use chrono::{DateTime, Utc}; +use jiff::Zoned; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use uuid::Uuid; @@ -37,7 +37,7 @@ pub struct DataKey { /// Associated metadata pub metadata: HashMap, /// Key creation timestamp - pub created_at: DateTime, + pub created_at: Zoned, } impl DataKey { @@ -61,7 +61,7 @@ impl DataKey { ciphertext, key_spec, metadata: HashMap::new(), - created_at: Utc::now(), + created_at: Zoned::now(), } } @@ -112,9 +112,9 @@ pub struct MasterKey { /// Associated metadata pub metadata: HashMap, /// Key creation timestamp - pub created_at: DateTime, + pub created_at: Zoned, /// Key last rotation timestamp - pub rotated_at: Option>, + pub rotated_at: Option, /// Key creator/owner pub created_by: Option, } @@ -139,7 +139,7 @@ impl MasterKey { status: KeyStatus::Active, description: None, metadata: HashMap::new(), - created_at: Utc::now(), + created_at: Zoned::now(), rotated_at: None, created_by, } @@ -170,7 +170,7 @@ impl MasterKey { status: KeyStatus::Active, description, metadata: HashMap::new(), - created_at: Utc::now(), + created_at: Zoned::now(), rotated_at: None, created_by, } @@ -219,9 +219,9 @@ pub struct KeyInfo { /// Key tags pub tags: HashMap, /// Key creation timestamp - pub created_at: DateTime, + pub created_at: Zoned, /// Key last rotation timestamp - pub rotated_at: Option>, + pub rotated_at: Option, /// Key creator pub created_by: Option, } @@ -612,7 +612,7 @@ pub struct EncryptionMetadata { /// Encryption context pub encryption_context: HashMap, /// Timestamp when encrypted - pub encrypted_at: DateTime, + pub encrypted_at: Zoned, /// Size of original data pub original_size: u64, /// Encrypted data key @@ -697,9 +697,9 @@ pub struct KeyMetadata { /// Key description pub description: Option, /// Key creation timestamp - pub creation_date: DateTime, + pub creation_date: Zoned, /// Key deletion timestamp - pub deletion_date: Option>, + pub deletion_date: Option, /// Key origin pub origin: String, /// Key manager diff --git a/rustfs/Cargo.toml b/rustfs/Cargo.toml index db8a61e1b..1058c260d 100644 --- a/rustfs/Cargo.toml +++ b/rustfs/Cargo.toml @@ -99,6 +99,7 @@ rustls-pemfile = { workspace = true } # Time and Date chrono = { workspace = true } +jiff = { workspace = true } time = { workspace = true, features = ["parsing", "formatting", "serde"] } # Utilities and Tools diff --git a/rustfs/src/admin/console.rs b/rustfs/src/admin/console.rs index 07046d400..bdd8d3b39 100644 --- a/rustfs/src/admin/console.rs +++ b/rustfs/src/admin/console.rs @@ -555,7 +555,7 @@ async fn health_check(method: Method) -> Response { let body_json = json!({ "status": health_status, "service": "rustfs-console", - "timestamp": chrono::Utc::now().to_rfc3339(), + "timestamp": jiff::Zoned::now().to_string(), "version": env!("CARGO_PKG_VERSION"), "details": details, "uptime": std::time::SystemTime::now() diff --git a/rustfs/src/admin/handlers.rs b/rustfs/src/admin/handlers.rs index 455dbf3ef..1afd270f4 100644 --- a/rustfs/src/admin/handlers.rs +++ b/rustfs/src/admin/handlers.rs @@ -132,7 +132,7 @@ impl Operation for HealthCheckHandler { let health_info = json!({ "status": "ok", "service": "rustfs-endpoint", - "timestamp": chrono::Utc::now().to_rfc3339(), + "timestamp": jiff::Zoned::now().to_string(), "version": env!("CARGO_PKG_VERSION") }); diff --git a/rustfs/src/init.rs b/rustfs/src/init.rs index 91aba5139..adb15dc7d 100644 --- a/rustfs/src/init.rs +++ b/rustfs/src/init.rs @@ -14,7 +14,6 @@ use crate::storage::ecfs::{process_lambda_configurations, process_queue_configurations, process_topic_configurations}; use crate::{admin, config, version}; -use chrono::Datelike; use rustfs_config::{DEFAULT_UPDATE_CHECK, ENV_UPDATE_CHECK}; use rustfs_ecstore::bucket::metadata_sys; use rustfs_notify::notifier_global; @@ -26,7 +25,7 @@ use tracing::{debug, error, info, instrument, warn}; #[instrument] pub(crate) fn print_server_info() { - let current_year = chrono::Utc::now().year(); + let current_year = jiff::Zoned::now().year(); // Use custom macros to print server information info!("RustFS Object Storage Server"); info!("Copyright: 2024-{} RustFS, Inc", current_year); diff --git a/rustfs/src/main.rs b/rustfs/src/main.rs index 1cc24c9cf..f4bb67bf0 100644 --- a/rustfs/src/main.rs +++ b/rustfs/src/main.rs @@ -71,11 +71,16 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; -fn main() -> Result<()> { +fn main() { let runtime = server::get_tokio_runtime_builder() .build() .expect("Failed to build Tokio runtime"); - runtime.block_on(async_main()) + let result = runtime.block_on(async_main()); + if let Err(ref e) = result { + eprintln!("{} Server encountered an error and is shutting down: {}", jiff::Zoned::now(), e); + error!("Server encountered an error and is shutting down: {}", e); + std::process::exit(1); + } } async fn async_main() -> Result<()> { // Parse the obtained parameters @@ -365,9 +370,10 @@ async fn run(opt: config::Opt) -> Result<()> { init_update_check(); println!( - "RustFS server started successfully at {}, current time: {}", + "RustFS server version: {} started successfully at {}, current time: {}", + version::get_version(), &server_address, - chrono::offset::Utc::now().to_string() + jiff::Zoned::now() ); info!(target: "rustfs::main::run","server started successfully at {}", &server_address); // 4. Mark as Full Ready now that critical components are warm diff --git a/rustfs/src/profiling.rs b/rustfs/src/profiling.rs index a4c5ce8bb..8f7d7db30 100644 --- a/rustfs/src/profiling.rs +++ b/rustfs/src/profiling.rs @@ -54,7 +54,6 @@ pub async fn dump_memory_pprof_now() -> Result { #[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] mod linux_impl { - use chrono::Utc; use jemalloc_pprof::PROF_CTL; use pprof::protos::Message; use rustfs_config::{ @@ -104,7 +103,7 @@ mod linux_impl { /// Generate timestamp string for filenames fn ts() -> String { - Utc::now().format("%Y%m%dT%H%M%S").to_string() + jiff::Zoned::now().strftime("%Y%m%dT%H%M%S").to_string() } /// Write pprof report to file in protobuf format diff --git a/rustfs/src/server/audit.rs b/rustfs/src/server/audit.rs index 144f74462..baa6cf569 100644 --- a/rustfs/src/server/audit.rs +++ b/rustfs/src/server/audit.rs @@ -85,7 +85,7 @@ pub(crate) async fn start_audit_system() -> AuditResult<()> { info!( target: "rustfs::main::start_audit_system", "Audit system started successfully with time: {}.", - chrono::Utc::now() + jiff::Zoned::now() ); Ok(()) } @@ -114,7 +114,7 @@ pub(crate) async fn stop_audit_system() -> AuditResult<()> { // Prepare before stopping system.close().await?; // Record after stopping - info!("Audit system stopped at {}", chrono::Utc::now()); + info!("Audit system stopped at {}", jiff::Zoned::now()); Ok(()) } else { warn!("Audit system not initialized, cannot stop"); diff --git a/rustfs/src/server/http.rs b/rustfs/src/server/http.rs index b0d85c27b..277126564 100644 --- a/rustfs/src/server/http.rs +++ b/rustfs/src/server/http.rs @@ -169,7 +169,7 @@ pub async fn start_http_server( // Detailed endpoint information (showing all API endpoints) let api_endpoints = format!("{protocol}://{local_ip_str}:{server_port}"); let localhost_endpoint = format!("{protocol}://127.0.0.1:{server_port}"); - let now_time = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); + let now_time = jiff::Zoned::now().strftime("%Y-%m-%d %H:%M:%S").to_string(); if opt.console_enable { admin::console::init_console_cfg(local_ip, server_port); diff --git a/rustfs/src/server/runtime.rs b/rustfs/src/server/runtime.rs index e6f59e156..6a288a55f 100644 --- a/rustfs/src/server/runtime.rs +++ b/rustfs/src/server/runtime.rs @@ -132,7 +132,7 @@ pub(crate) fn get_tokio_runtime_builder() -> tokio::runtime::Builder { let id = std::thread::current().id(); println!( "RustFS Worker Thread running - initializing resources time: {:?}, thread id: {:?}", - chrono::Utc::now().to_rfc3339(), + jiff::Zoned::now().to_string(), id ); }) @@ -140,7 +140,7 @@ pub(crate) fn get_tokio_runtime_builder() -> tokio::runtime::Builder { let id = std::thread::current().id(); println!( "RustFS Worker Thread stopping - cleaning up resources time: {:?}, thread id: {:?}", - chrono::Utc::now().to_rfc3339(), + jiff::Zoned::now().to_string(), id ) }); diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index 58043e856..739a4428a 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -34,7 +34,6 @@ use crate::storage::{ }; use base64::{Engine, engine::general_purpose::STANDARD as BASE64_STANDARD}; use bytes::Bytes; -use chrono::{DateTime, Utc}; use datafusion::arrow::{ csv::WriterBuilder as CsvWriterBuilder, json::WriterBuilder as JsonWriterBuilder, json::writer::JsonArray, }; @@ -319,7 +318,7 @@ async fn create_managed_encryption_material( iv: data_key.nonce.to_vec(), tag: None, encryption_context: context.encryption_context.clone(), - encrypted_at: Utc::now(), + encrypted_at: jiff::Zoned::now(), original_size: if original_size >= 0 { original_size as u64 } else { 0 }, encrypted_data_key, }; @@ -3360,7 +3359,7 @@ impl S3 for FS { // Per S3 API spec, this header should be present in HEAD object response when tags exist if tag_count > 0 { let header_name = http::HeaderName::from_static(AMZ_TAG_COUNT); - if let Ok(header_value) = tag_count.to_string().parse::() { + if let Ok(header_value) = tag_count.to_string().parse::() { response.headers.insert(header_name, header_value); } else { warn!("Failed to parse x-amz-tagging-count header value, skipping"); @@ -4105,9 +4104,7 @@ impl S3 for FS { if dsc.replicate_any() { let k = format!("{}{}", RESERVED_METADATA_PREFIX_LOWER, "replication-timestamp"); - let now: DateTime = Utc::now(); - let formatted_time = now.to_rfc3339(); - opts.user_defined.insert(k, formatted_time); + opts.user_defined.insert(k, jiff::Zoned::now().to_string()); let k = format!("{}{}", RESERVED_METADATA_PREFIX_LOWER, "replication-status"); opts.user_defined.insert(k, dsc.pending_status().unwrap_or_default()); } @@ -7807,7 +7804,7 @@ mod tests { let result = process_queue_configurations( &mut event_rules, - Some(vec![s3s::dto::QueueConfiguration { + Some(vec![QueueConfiguration { events: vec!["s3:ObjectCreated:*".to_string().into()], queue_arn: invalid_arn.to_string(), filter: None, @@ -7833,7 +7830,7 @@ mod tests { let result = process_topic_configurations( &mut event_rules, - Some(vec![s3s::dto::TopicConfiguration { + Some(vec![TopicConfiguration { events: vec!["s3:ObjectCreated:*".to_string().into()], topic_arn: invalid_arn.to_string(), filter: None, @@ -7859,7 +7856,7 @@ mod tests { let result = process_lambda_configurations( &mut event_rules, - Some(vec![s3s::dto::LambdaFunctionConfiguration { + Some(vec![LambdaFunctionConfiguration { events: vec!["s3:ObjectCreated:*".to_string().into()], lambda_function_arn: invalid_arn.to_string(), filter: None, @@ -7885,7 +7882,7 @@ mod tests { let result = process_queue_configurations( &mut event_rules, - Some(vec![s3s::dto::QueueConfiguration { + Some(vec![QueueConfiguration { events: vec!["s3:ObjectCreated:*".to_string().into()], queue_arn: valid_arn.to_string(), filter: None, diff --git a/rustfs/src/update.rs b/rustfs/src/update.rs index c480e630b..4e8b3c6ce 100644 --- a/rustfs/src/update.rs +++ b/rustfs/src/update.rs @@ -55,7 +55,7 @@ pub struct UpdateCheckResult { /// Latest version information pub latest_version: Option, /// Check time - pub check_time: chrono::DateTime, + pub check_time: jiff::Zoned, } /// Version checker @@ -146,7 +146,7 @@ impl VersionChecker { update_available, current_version, latest_version: Some(version_info), - check_time: chrono::Utc::now(), + check_time: jiff::Zoned::now(), }; if result.update_available { @@ -194,8 +194,6 @@ mod tests { #[test] fn test_update_check_result() { - use chrono::Utc; - // Test creating UpdateCheckResult with update available let version_info = VersionInfo { version: "1.2.0".to_string(), @@ -204,12 +202,12 @@ mod tests { download_url: Some("https://github.com/rustfs/rustfs/releases/tag/v1.2.0".to_string()), }; - let check_time = Utc::now(); + let check_time = jiff::Zoned::now(); let result = UpdateCheckResult { update_available: true, current_version: "1.1.0".to_string(), latest_version: Some(version_info.clone()), - check_time, + check_time: check_time.clone(), }; debug!("Update check result: {:?}", serde_json::to_string(&result).unwrap()); @@ -253,7 +251,7 @@ mod tests { release_notes: None, download_url: None, }), - check_time: Utc::now(), + check_time: jiff::Zoned::now(), }; assert!(!no_update_result.update_available); @@ -264,7 +262,7 @@ mod tests { update_available: false, current_version: "1.1.0".to_string(), latest_version: None, - check_time: Utc::now(), + check_time: jiff::Zoned::now(), }; assert!(!error_result.update_available); diff --git a/scripts/run.ps1 b/scripts/run.ps1 index 015c9f787..d686a4502 100644 --- a/scripts/run.ps1 +++ b/scripts/run.ps1 @@ -1,67 +1,201 @@ -<# - Copyright 2024 RustFS Team +# Copyright 2024 RustFS Team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +$ErrorActionPreference = "Stop" - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -#> - -# Check if static files need to be downloaded -if (-not (Test-Path .\rustfs\static\index.html)) { +# Check if ./rustfs/static/index.html exists +if (-not (Test-Path "./rustfs/static/index.html")) { Write-Host "Downloading rustfs-console-latest.zip" + # Download rustfs-console-latest.zip + Invoke-WebRequest -Uri "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" -OutFile "tempfile.zip" - Invoke-WebRequest -Uri "https://dl.rustfs.com/artifacts/console/rustfs-console-latest.zip" -OutFile 'tempfile.zip' - Expand-Archive -Path 'tempfile.zip' -DestinationPath '.\rustfs\static' -Force - Remove-Item tempfile.zip + # Unzip + Expand-Archive -Path "tempfile.zip" -DestinationPath "./rustfs/static" -Force + + # Remove temp file + Remove-Item "tempfile.zip" } -# Check if build should be skipped if (-not $env:SKIP_BUILD) { cargo build -p rustfs --bins } $current_dir = Get-Location +Write-Host "Current directory: $current_dir" -# Create multiple test directories -$testDirs = @("test0", "test1", "test2", "test3", "test4") -foreach ($dir in $testDirs) { - $path = Join-Path -Path ".\target\volume" -ChildPath $dir - if (-not (Test-Path $path)) { - New-Item -ItemType Directory -Path $path -Force | Out-Null - } +# Create directories +1..4 | ForEach-Object { + New-Item -ItemType Directory -Force -Path "./target/volume/test$_" | Out-Null } -# Set environment variables if (-not $env:RUST_LOG) { - $env:RUST_BACKTRACE = 1 - $env:RUST_LOG = "rustfs=debug,ecstore=debug,s3s=debug,iam=debug" + $env:RUST_BACKTRACE = "1" + $env:RUST_LOG = "rustfs=debug,ecstore=info,s3s=debug,iam=info,notify=info" } -# The following environment variables are commented out, uncomment them if needed -# $env:RUSTFS_ERASURE_SET_DRIVE_COUNT = 5 +# $env:RUSTFS_ERASURE_SET_DRIVE_COUNT = "5" # $env:RUSTFS_STORAGE_CLASS_INLINE_BLOCK = "512 KB" -$env:RUSTFS_VOLUMES = ".\target\volume\test{0...4}" -# $env:RUSTFS_VOLUMES = ".\target\volume\test" -$env:RUSTFS_ADDRESS = "127.0.0.1:9000" +$env:RUSTFS_VOLUMES = "./target/volume/test{1...4}" +# $env:RUSTFS_VOLUMES = "./target/volume/test" +$env:RUSTFS_ADDRESS = ":9000" $env:RUSTFS_CONSOLE_ENABLE = "true" -$env:RUSTFS_CONSOLE_ADDRESS = "127.0.0.1:9002" +$env:RUSTFS_CONSOLE_ADDRESS = ":9001" # $env:RUSTFS_SERVER_DOMAINS = "localhost:9000" -# Change to the actual configuration file path, obs.example.toml is for reference only -$env:RUSTFS_OBS_CONFIG = ".\deploy\config\obs.example.toml" +# HTTPS certificate directory +# $env:RUSTFS_TLS_PATH = "./deploy/certs" + +# Observability related configuration +# $env:RUSTFS_OBS_ENDPOINT = "http://localhost:4318" # OpenTelemetry Collector address +# RustFS OR OTEL exporter configuration +# $env:RUSTFS_OBS_TRACE_ENDPOINT = "http://localhost:4318/v1/traces" # OpenTelemetry Collector trace address +# $env:OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = "http://localhost:14318/v1/traces" +# $env:RUSTFS_OBS_METRIC_ENDPOINT = "http://localhost:9090/api/v1/otlp/v1/metrics" # OpenTelemetry Collector metric address +# $env:OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = "http://localhost:9090/api/v1/otlp/v1/metrics" +# $env:RUSTFS_OBS_LOG_ENDPOINT = "http://loki:3100/otlp/v1/logs" # OpenTelemetry Collector logs address +# $env:OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = "http://loki:3100/otlp/v1/logs" +# $env:RUSTFS_OBS_USE_STDOUT = "true" # Whether to use standard output +# $env:RUSTFS_OBS_SAMPLE_RATIO = "2.0" # Sample ratio, between 0.0-1.0 +# $env:RUSTFS_OBS_METER_INTERVAL = "1" # Sampling interval in seconds +# $env:RUSTFS_OBS_SERVICE_NAME = "rustfs" # Service name +# $env:RUSTFS_OBS_SERVICE_VERSION = "0.1.0" # Service version +$env:RUSTFS_OBS_ENVIRONMENT = "develop" # Environment name +$env:RUSTFS_OBS_LOGGER_LEVEL = "info" # Log level +$env:RUSTFS_OBS_LOG_STDOUT_ENABLED = "false" # Whether to enable local stdout logging +$env:RUSTFS_OBS_LOG_DIRECTORY = "$current_dir/deploy/logs" # Log directory +$env:RUSTFS_OBS_LOG_ROTATION_TIME = "hour" # Log rotation time unit +$env:RUSTFS_OBS_LOG_ROTATION_SIZE_MB = "100" # Log rotation size in MB +$env:RUSTFS_OBS_LOG_POOL_CAPA = "10240" # Log pool capacity +$env:RUSTFS_OBS_LOG_MESSAGE_CAPA = "32768" # Log message capacity +$env:RUSTFS_OBS_LOG_FLUSH_MS = "300" # Log flush interval in milliseconds + +# tokio runtime +$env:RUSTFS_RUNTIME_WORKER_THREADS = "16" +$env:RUSTFS_RUNTIME_MAX_BLOCKING_THREADS = "1024" +$env:RUSTFS_RUNTIME_THREAD_PRINT_ENABLED = "false" +$env:RUSTFS_RUNTIME_THREAD_STACK_SIZE = 1024 * 1024 +$env:RUSTFS_RUNTIME_THREAD_KEEP_ALIVE = "60" +$env:RUSTFS_RUNTIME_GLOBAL_QUEUE_INTERVAL = "31" + +$env:OTEL_INSTRUMENTATION_NAME = "rustfs" +$env:OTEL_INSTRUMENTATION_VERSION = "0.1.1" +$env:OTEL_INSTRUMENTATION_SCHEMA_URL = "https://opentelemetry.io/schemas/1.31.0" +$env:OTEL_INSTRUMENTATION_ATTRIBUTES = "env=production" + +# notify +# $env:RUSTFS_NOTIFY_WEBHOOK_ENABLE = "on" +# $env:RUSTFS_NOTIFY_WEBHOOK_ENDPOINT = "http://[::]:3020/webhook" +# $env:RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR = "$current_dir/deploy/logs/notify" + +# $env:RUSTFS_NOTIFY_WEBHOOK_ENABLE_PRIMARY = "on" +# $env:RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY = "http://[::]:3020/webhook" +# $env:RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_PRIMARY = "$current_dir/deploy/logs/notify" + +# $env:RUSTFS_NOTIFY_WEBHOOK_ENABLE_MASTER = "on" +# $env:RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_MASTER = "http://[::]:3020/webhook" +# $env:RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_MASTER = "$current_dir/deploy/logs/notify" + +# $env:RUSTFS_AUDIT_WEBHOOK_ENABLE = "on" +# $env:RUSTFS_AUDIT_WEBHOOK_ENDPOINT = "http://[::]:3020/webhook" +# $env:RUSTFS_AUDIT_WEBHOOK_QUEUE_DIR = "$current_dir/deploy/logs/audit" + +# $env:RUSTFS_AUDIT_WEBHOOK_ENABLE_PRIMARY = "on" +# $env:RUSTFS_AUDIT_WEBHOOK_ENDPOINT_PRIMARY = "http://[::]:3020/webhook" +# $env:RUSTFS_AUDIT_WEBHOOK_QUEUE_DIR_PRIMARY = "$current_dir/deploy/logs/audit" + +# $env:RUSTFS_AUDIT_WEBHOOK_ENABLE_MASTER = "on" +# $env:RUSTFS_AUDIT_WEBHOOK_ENDPOINT_MASTER = "http://[::]:3020/webhook" +# $env:RUSTFS_AUDIT_WEBHOOK_QUEUE_DIR_MASTER = "$current_dir/deploy/logs/audit" + +# $env:RUSTFS_POLICY_PLUGIN_URL = "http://localhost:8181/v1/data/rustfs/authz/allow" +# $env:RUSTFS_POLICY_PLUGIN_AUTH_TOKEN = "your-opa-token" + +$env:RUSTFS_NS_SCANNER_INTERVAL = "60" +# $env:RUSTFS_SKIP_BACKGROUND_TASK = "true" + +# Storage level compression +# $env:RUSTFS_COMPRESSION_ENABLED = "true" + +# HTTP Response Compression +# $env:RUSTFS_COMPRESS_ENABLE = "on" + +# Example 1: Compress text files and logs +# $env:RUSTFS_COMPRESS_ENABLE = "on" +# $env:RUSTFS_COMPRESS_EXTENSIONS = ".txt,.log,.csv" +# $env:RUSTFS_COMPRESS_MIME_TYPES = "text/*" +# $env:RUSTFS_COMPRESS_MIN_SIZE = "1000" + +# Example 2: Compress JSON and XML API responses +# $env:RUSTFS_COMPRESS_ENABLE = "on" +# $env:RUSTFS_COMPRESS_EXTENSIONS = ".json,.xml" +# $env:RUSTFS_COMPRESS_MIME_TYPES = "application/json,application/xml" +# $env:RUSTFS_COMPRESS_MIN_SIZE = "1000" + +# Example 3: Comprehensive web content compression +# $env:RUSTFS_COMPRESS_ENABLE = "on" +# $env:RUSTFS_COMPRESS_EXTENSIONS = ".html,.css,.js,.json,.xml,.txt,.svg" +# $env:RUSTFS_COMPRESS_MIME_TYPES = "text/*,application/json,application/xml,application/javascript,image/svg+xml" +# $env:RUSTFS_COMPRESS_MIN_SIZE = "1000" + +# Example 4: Compress only large text files +# $env:RUSTFS_COMPRESS_ENABLE = "on" +# $env:RUSTFS_COMPRESS_EXTENSIONS = ".txt,.log" +# $env:RUSTFS_COMPRESS_MIME_TYPES = "text/*" +# $env:RUSTFS_COMPRESS_MIN_SIZE = "10240" + +# $env:RUSTFS_REGION = "us-east-1" + +$env:RUSTFS_ENABLE_SCANNER = "false" +$env:RUSTFS_ENABLE_HEAL = "false" + +# Object cache configuration +$env:RUSTFS_OBJECT_CACHE_ENABLE = "true" + +# Profiling configuration +$env:RUSTFS_ENABLE_PROFILING = "false" + +# Heal configuration queue size +$env:RUSTFS_HEAL_QUEUE_SIZE = "10000" + +# rustfs trust system CA certificates +$env:RUSTFS_TRUST_SYSTEM_CA = "true" + +# Enable FTP server +$env:RUSTFS_FTPS_ENABLE = "false" + +# Increase timeout for high-latency network storage +# $env:RUSTFS_LOCK_ACQUIRE_TIMEOUT = "120" + +# Reduce timeout for low-latency local storage +$env:RUSTFS_LOCK_ACQUIRE_TIMEOUT = "30" -# Check command line arguments if ($args.Count -gt 0) { $env:RUSTFS_VOLUMES = $args[0] } -# Run the program -cargo run --bin rustfs +# Enable jemalloc for memory profiling +if (-not $env:MALLOC_CONF) { + $env:MALLOC_CONF = "prof:true,prof_active:true,lg_prof_sample:16,log:true,narenas:2,lg_chunk:21,background_thread:true,dirty_decay_ms:1000,muzzy_decay_ms:1000" +} + +# Start webhook server +# Start-Process cargo -ArgumentList "run --example webhook -p rustfs-notify" -NoNewWindow + +# Start main service +# To run with profiling enabled: +# cargo run --profile profiling --bin rustfs +# To run in release mode: +# cargo run --profile release --bin rustfs +# To run in debug mode: +cargo run --bin rustfs \ No newline at end of file