From c006f84461db3d75b8376ccff135fee86095b658 Mon Sep 17 00:00:00 2001 From: houseme Date: Thu, 27 Aug 2026 14:45:50 +0800 Subject: [PATCH] feat(info): report all rustfs features (#6722) * feat(info): report all rustfs features Co-Authored-By: heihutu * chore(deps): update s3s revision Co-Authored-By: heihutu * fix(obs): adapt dial9 telemetry API Co-Authored-By: heihutu --------- Co-authored-by: heihutu --- Cargo.lock | 229 +++++++++++++++------- Cargo.toml | 10 +- crates/audit/Cargo.toml | 1 - crates/ecstore/Cargo.toml | 1 - crates/obs/Cargo.toml | 2 +- crates/obs/src/telemetry/dial9/config.rs | 4 +- crates/obs/src/telemetry/dial9/enabled.rs | 86 +++++--- crates/obs/src/telemetry/dial9/mod.rs | 14 +- crates/obs/src/telemetry/dial9/state.rs | 13 +- crates/s3-client/Cargo.toml | 1 - rustfs/src/config/info.rs | 126 ++++++++++-- 11 files changed, 344 insertions(+), 143 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0da99f002..1877c1c77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1623,6 +1623,22 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "blazesym" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "847a0a95b041ad5aae1bdc44f2bd54743f76eb0065c4f86b139f81343c287eaf" +dependencies = [ + "cpp_demangle", + "crc32fast", + "flate2", + "gimli 0.33.0", + "libc", + "memmap2", + "rustc-demangle", + "tempfile", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -1806,6 +1822,12 @@ dependencies = [ "libbz2-rs-sys", ] +[[package]] +name = "c-enum" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd17eb909a8c6a894926bfcc3400a4bb0e732f5a57d37b1f14e8b29e329bace8" + [[package]] name = "camino" version = "1.2.5" @@ -2300,6 +2322,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpp_demangle" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0667304c32ea56cb4cd6d2d7c0cfe9a2f8041229db8c033af7f8d69492429def" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpubits" version = "0.1.1" @@ -3453,25 +3484,23 @@ dependencies = [ [[package]] name = "deadpool" -version = "0.12.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" +checksum = "3e98a7e119cd347f4201e1159b19831029e203e2d8b790547708e8157b4acf1e" dependencies = [ "deadpool-runtime", - "lazy_static", - "num_cpus", "tokio", ] [[package]] name = "deadpool-postgres" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d697d376cbfa018c23eb4caab1fd1883dd9c906a8c034e8d9a3cb06a7e0bef9" +checksum = "65a536565624b97fc19f758cd01b15d12908d3344425066efc8162236fbd3749" dependencies = [ "async-trait", "deadpool", - "getrandom 0.2.17", + "getrandom 0.4.3", "tokio", "tokio-postgres", "tracing", @@ -3479,9 +3508,9 @@ dependencies = [ [[package]] name = "deadpool-runtime" -version = "0.1.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" +checksum = "2657f61fb1dd8bf37a8d51093cc7cee4e77125b22f7753f49b289f831bec2bae" dependencies = [ "tokio", ] @@ -3685,35 +3714,61 @@ dependencies = [ ] [[package]] -name = "dial9-macro" -version = "0.3.7" +name = "dial9-core" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7e31f073f2e14e5a9d338c543a0601aeaf7c43fc428cd59ce417230d0db37d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "dial9-tokio-telemetry" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b511dfd54f5191f7eb86856fe19d8e3c8f71673bd256e1bfa1c8128fbbc0cdb" +checksum = "9e8cbbc8955394be626249a3b52ddd6bf664373661eaeae70d87eb12bf6f20b6" dependencies = [ "arc-swap", "bon", "bytes", "crossbeam-queue", - "dial9-macro", + "dial9-trace-format", + "flate2", + "futures-util", + "libc", + "metrique", + "metrique-timesource", + "tokio", + "tokio-util", + "tracing", + "ulid", +] + +[[package]] +name = "dial9-perf-self-profile" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f65948455c504bf08576c7b5cfe93bfcaa486d661dc4ee85c2a6309ab89629" +dependencies = [ + "blazesym", + "bon", + "bytes", + "crossbeam-utils", + "dial9-core", + "dial9-trace-format", + "libc", + "perf-event-data", + "perf-event-open-sys2", + "tracing", +] + +[[package]] +name = "dial9-tokio-telemetry" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1244091367c805b5d98a6a590f8ab992efda06e6d2560a6967ef898ffd30a2" +dependencies = [ + "bon", + "bytes", + "dial9-core", + "dial9-perf-self-profile", "dial9-trace-format", "flate2", "futures-util", "hostname", "libc", - "metrique", "metrique-timesource", - "metrique-writer", "pin-project-lite", "serde", "serde_json", @@ -3725,20 +3780,22 @@ dependencies = [ [[package]] name = "dial9-trace-format" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3636d6ec60d94840cc414dcd6a95c77b3ba0a7b86d43fd035b89662eeb5cfa7" +checksum = "86083b7240114b2d0da4a7e9d041571d80ca3b1f92ae0ec794f1be21709daa6a" dependencies = [ "dial9-trace-format-derive", "serde", + "typeid", ] [[package]] name = "dial9-trace-format-derive" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff7c2855b73d0de34bc31d6dc7afbf0f6ce230a668403ac2b57b21d1ffe3928" +checksum = "9309248f12e414d88bcc9505f78b0c9ed47f79c5b62db611493e19a612cdc9d6" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.119", @@ -4558,6 +4615,9 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c" dependencies = [ + "fnv", + "hashbrown 0.16.1", + "indexmap 2.14.0", "stable_deref_trait", ] @@ -4581,20 +4641,20 @@ dependencies = [ [[package]] name = "google-cloud-auth" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54aab44c16b8463ae11b165a87c3d484780231f157bb1ed65843d591beb5abd" +checksum = "ff461519b1a948200f163574be072753bcfb462a323f0eb426629d89872dd685" dependencies = [ "async-trait", "aws-lc-rs", - "base64 0.22.1", + "base64 0.23.1", "bytes", - "chrono", "google-cloud-gax", "hex", "hmac 0.13.0", "http 1.5.0", - "jsonwebtoken 10.4.0", + "jiff", + "jsonwebtoken", "reqwest", "rustc_version", "rustls", @@ -4610,9 +4670,9 @@ dependencies = [ [[package]] name = "google-cloud-gax" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9a46dd0fd026bbc4a5d84e6ab0c941cee6e3b057976a0bb107fdb5238ce598f" +checksum = "c5615cff28ee59cfe52fbb4c11b8b1e77f650296e2ea4f4c2b7757ac6b19e752" dependencies = [ "bytes", "futures", @@ -4625,13 +4685,14 @@ dependencies = [ "serde_json", "thiserror 2.0.20", "tokio", + "tokio-stream", ] [[package]] name = "google-cloud-gax-internal" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb04c54317ace06d489213f761797240b3046142a9b7ce6b9a82a9d134e193d1" +checksum = "d2766757d877a7a8ac23da9884cb0e3f10ed9b75a0ce59801ce6b19bf9d5819e" dependencies = [ "bytes", "futures", @@ -4668,9 +4729,9 @@ dependencies = [ [[package]] name = "google-cloud-iam-v1" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cdf5acc7ef946ee2db7a7f62bd436d8395a6543b4beef110cdc061fcf578bb" +checksum = "5f962b40234b1531e6ef73f7558871c96e117231e962086c98804323fb8d2c82" dependencies = [ "async-trait", "bytes", @@ -4686,9 +4747,9 @@ dependencies = [ [[package]] name = "google-cloud-longrunning" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6ce05df0aea2c08472983ce2bbbed9483cbb637b89ff69a7c4ef94371fe4f2" +checksum = "1c0363c5389ffda2b55cd8a86eef4b19a3481a48467c91dc5d77f626a9572766" dependencies = [ "async-trait", "bytes", @@ -4704,9 +4765,9 @@ dependencies = [ [[package]] name = "google-cloud-lro" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cca2b991d619525d72a170ca7f413cb520872702442da22ac9af650a8e786" +checksum = "47af3deef75c14a2983c430898d960c765bddbcc9f9188ca0563108e9227cfe7" dependencies = [ "google-cloud-gax", "google-cloud-gax-internal", @@ -4733,14 +4794,13 @@ dependencies = [ [[package]] name = "google-cloud-storage" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9227f65175fa91a6e41f246797917697efdadfe09dd8ea84ad8b737a71efbd28" +checksum = "973399251b245c63f1d02d0768772833dcf1372ee358f593fb9159de8fe9c7d4" dependencies = [ "async-trait", - "base64 0.22.1", + "base64 0.23.1", "bytes", - "chrono", "crc32c", "futures", "google-cloud-auth", @@ -4755,6 +4815,7 @@ dependencies = [ "hex", "http 1.5.0", "http-body 1.1.0", + "jiff", "md5", "percent-encoding", "prost 0.14.4", @@ -5784,22 +5845,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonwebtoken" -version = "10.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" -dependencies = [ - "aws-lc-rs", - "base64 0.22.1", - "getrandom 0.2.17", - "js-sys", - "serde", - "serde_json", - "signature 2.2.0", - "zeroize", -] - [[package]] name = "jsonwebtoken" version = "11.0.0" @@ -6357,7 +6402,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b55bfa39e6f5e44a37a59a794915ce36d04371471cf62ae365cd9703f58a5e0" dependencies = [ "itoa", - "jiff", "metrique-core", "metrique-macro", "metrique-service-metrics", @@ -6366,7 +6410,6 @@ dependencies = [ "metrique-writer-core", "metrique-writer-macro", "ryu", - "serde_json", "tokio", ] @@ -7611,6 +7654,27 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "perf-event-data" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575828d9d7d205188048eb1508560607a03d21eafdbba47b8cade1736c1c28e1" +dependencies = [ + "bitflags 2.13.1", + "c-enum", + "perf-event-open-sys2", +] + +[[package]] +name = "perf-event-open-sys2" +version = "5.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c25955321465255e437600b54296983fab1feac2cd0c38958adeb26dbae49e" +dependencies = [ + "libc", + "memoffset", +] + [[package]] name = "petgraph" version = "0.7.1" @@ -9443,7 +9507,6 @@ dependencies = [ name = "rustfs-audit" version = "1.0.0-rc.4" dependencies = [ - "async-trait", "const-str", "futures", "hashbrown 0.17.1", @@ -9537,7 +9600,7 @@ dependencies = [ "base64-simd", "chacha20poly1305", "hotpath", - "jsonwebtoken 11.0.0", + "jsonwebtoken", "pbkdf2 0.13.0", "rand 0.10.2", "rsa 0.10.0-rc.18", @@ -9582,7 +9645,6 @@ dependencies = [ "flatbuffers", "futures", "futures-util", - "glob", "google-cloud-auth", "google-cloud-storage", "hex-simd", @@ -9789,7 +9851,7 @@ dependencies = [ "hmac 0.13.0", "hotpath", "http 1.5.0", - "jsonwebtoken 11.0.0", + "jsonwebtoken", "moka", "openidconnect", "pollster", @@ -10220,7 +10282,7 @@ dependencies = [ "hotpath", "ipnetwork", "jiff", - "jsonwebtoken 11.0.0", + "jsonwebtoken", "moka", "pollster", "proptest", @@ -10441,7 +10503,6 @@ dependencies = [ "s3s", "serde", "serde_json", - "sha1 0.11.0", "sha2 0.11.0", "thiserror 2.0.20", "time", @@ -10981,7 +11042,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "s3s" version = "0.15.0" -source = "git+https://github.com/rustfs/s3s.git?rev=f4dedc905ec621fa85a4686df6304190b55375f6#f4dedc905ec621fa85a4686df6304190b55375f6" +source = "git+https://github.com/rustfs/s3s.git?rev=0f6f83d98b37fd9edcaa3be573db4aa8f568e088#0f6f83d98b37fd9edcaa3be573db4aa8f568e088" dependencies = [ "arc-swap", "arrayvec", @@ -12813,12 +12874,28 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "ulid" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" +dependencies = [ + "rand 0.9.5", + "web-time", +] + [[package]] name = "unarray" version = "0.1.4" @@ -12953,9 +13030,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index f54f71fd1..35fb5e512 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -259,8 +259,8 @@ enumset = "1.1.14" faster-hex = "0.10.0" flate2 = "1.1.9" glob = "0.3.4" -google-cloud-storage = "1.17.0" -google-cloud-auth = "1.15.0" +google-cloud-storage = "1.18.0" +google-cloud-auth = "1.16.0" hashbrown = { version = "0.17.1" } # Base32 for RFC 6238 TOTP shared secrets (RFC 4648 unpadded, the alphabet # every authenticator app expects). Already in the graph transitively. @@ -304,7 +304,7 @@ rustify = { version = "0.7", default-features = false } rustix = { version = "1.1.4" } rust-embed = { version = "8.12.0" } rustc-hash = { version = "2.1.3" } -s3s = { git = "https://github.com/rustfs/s3s.git", rev = "f4dedc905ec621fa85a4686df6304190b55375f6", version = "0.15.0", features = ["minio"] } +s3s = { git = "https://github.com/rustfs/s3s.git", rev = "0f6f83d98b37fd9edcaa3be573db4aa8f568e088", version = "0.15.0", features = ["minio"] } serial_test = "4.0.1" shadow-rs = { default-features = false, version = "2.0.0" } siphasher = "1.0.3" @@ -327,7 +327,7 @@ tracing-subscriber = { version = "0.3.23" } transform-stream = "0.3.1" url = "2.5.8" urlencoding = "2.1.3" -uuid = { version = "1.25.0" } +uuid = { version = "1.26.0" } vaultrs = { version = "0.8.0" } tar = "0.4.46" walkdir = "2.5.0" @@ -341,7 +341,7 @@ zstd = "0.13.3" # Observability and Metrics metrics = "0.24.6" metrics-util = "0.20" -dial9-tokio-telemetry = "0.3" +dial9-tokio-telemetry = "0.5.0" opentelemetry = { version = "0.32.0" } opentelemetry-appender-tracing = { version = "0.32.0" } opentelemetry-otlp = { version = "0.32.0" } diff --git a/crates/audit/Cargo.toml b/crates/audit/Cargo.toml index 8ec993a27..5f45dda92 100644 --- a/crates/audit/Cargo.toml +++ b/crates/audit/Cargo.toml @@ -68,7 +68,6 @@ tracing = { workspace = true, features = ["std", "attributes"] } [dev-dependencies] rustfs-targets = { workspace = true, features = ["test-support"] } -async-trait = { workspace = true } temp-env = { workspace = true } url = { workspace = true } diff --git a/crates/ecstore/Cargo.toml b/crates/ecstore/Cargo.toml index c2d605510..10100a435 100644 --- a/crates/ecstore/Cargo.toml +++ b/crates/ecstore/Cargo.toml @@ -146,7 +146,6 @@ bytes = { workspace = true, features = ["serde"] } byteorder = { workspace = true } chrono = { workspace = true, features = ["serde"] } jiff = { workspace = true, features = ["serde"] } -glob = { workspace = true } thiserror.workspace = true flatbuffers.workspace = true futures.workspace = true diff --git a/crates/obs/Cargo.toml b/crates/obs/Cargo.toml index 338128814..ae12858f1 100644 --- a/crates/obs/Cargo.toml +++ b/crates/obs/Cargo.toml @@ -74,7 +74,7 @@ hotpath-cpu = [ # Tokio runtime-level telemetry. Requires a `--cfg tokio_unstable` build; the # build script fails the compile when that flag is missing. Off by default so # ordinary builds neither pay for nor depend on Tokio's unstable API. -dial9 = ["dep:dial9-tokio-telemetry"] +dial9 = ["dep:dial9-tokio-telemetry", "dial9-tokio-telemetry/process-resource"] # # NOTE: there is deliberately no `dial9-taskdump` feature. dial9 only captures a # task dump for futures it wrapped itself, i.e. those spawned via diff --git a/crates/obs/src/telemetry/dial9/config.rs b/crates/obs/src/telemetry/dial9/config.rs index ee7889fc5..4784e1f74 100644 --- a/crates/obs/src/telemetry/dial9/config.rs +++ b/crates/obs/src/telemetry/dial9/config.rs @@ -76,7 +76,7 @@ pub struct Dial9Config { /// Directory where trace files are written pub output_dir: String, - /// Prefix for trace file names + /// Trace family name under the output directory pub file_prefix: String, /// Maximum size of each trace file in bytes @@ -158,7 +158,7 @@ impl Dial9Config { } } - /// Get the base path for trace files. + /// Get the trace family directory for rotating trace segments. pub fn base_path(&self) -> PathBuf { PathBuf::from(&self.output_dir).join(&self.file_prefix) } diff --git a/crates/obs/src/telemetry/dial9/enabled.rs b/crates/obs/src/telemetry/dial9/enabled.rs index 2efbd3009..912514ee5 100644 --- a/crates/obs/src/telemetry/dial9/enabled.rs +++ b/crates/obs/src/telemetry/dial9/enabled.rs @@ -23,15 +23,22 @@ use super::config::Dial9Config; use super::state::{dial9_runtime_state, measure_disk_usage_bytes}; use super::{EVENT_DIAL9_STATE, LOG_COMPONENT_OBS, LOG_SUBSYSTEM_DIAL9}; use crate::TelemetryError; -use dial9_tokio_telemetry::telemetry::{ProcessResourceUsageConfig, RotatingWriter, TracedRuntime}; +use dial9_tokio_telemetry::telemetry::{ + Dial9Handle, Dial9HandleTokioExt, DiskBuffer, ProcessResourceUsageConfig, RecorderPerfExt, TokioAttachOptions, recorder, +}; use std::time::Duration; use tracing::{info, warn}; -pub use dial9_tokio_telemetry::telemetry::TelemetryGuard; +pub type TelemetryGuard = Dial9Handle; + +type ShutdownRecorder = Box; /// How often the background refresher restates trace-file disk usage. const DISK_USAGE_REFRESH_INTERVAL: Duration = Duration::from_secs(60); +/// Maximum time spent flushing the recorder during graceful shutdown. +const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); + /// Name recorded in segment metadata so the trace viewer can label workers. const RUNTIME_NAME: &str = "rustfs-worker"; @@ -43,13 +50,14 @@ const RUNTIME_NAME: &str = "rustfs-worker"; /// are lost. pub struct Dial9SessionGuard { guard: TelemetryGuard, + shutdown: Option, config: Dial9Config, } impl Dial9SessionGuard { /// Whether the underlying telemetry session is recording. pub fn is_active(&self) -> bool { - self.guard.is_enabled() + self.guard.is_enabled() && self.guard.is_connected() && !self.guard.is_stopped() } } @@ -72,8 +80,10 @@ impl Drop for Dial9SessionGuard { state = "flushed", "dial9 state changed" ); - // `TelemetryGuard`'s own `Drop` flushes buffered events and seals the - // active segment; it runs immediately after this body. + + if let Some(shutdown) = self.shutdown.take() { + shutdown(); + } } } @@ -97,53 +107,58 @@ pub fn build_traced_runtime( TelemetryError::Io(format!("Failed to create dial9 output directory '{}': {e}", config.output_dir)) })?; - let writer = RotatingWriter::new(config.base_path(), config.max_file_size, config.total_disk_budget()).map_err(|e| { - dial9_runtime_state().record_runtime_error(&config); - TelemetryError::Io(format!("Failed to create dial9 RotatingWriter: {e}")) - })?; + let writer = DiskBuffer::builder() + .base_path(config.base_path()) + .max_file_size(config.max_file_size) + .max_total_size(config.total_disk_budget()) + .build() + .map_err(|e| { + dial9_runtime_state().record_runtime_error(&config); + TelemetryError::Io(format!("Failed to create dial9 DiskBuffer: {e}")) + })?; - // `with_trace_path` transitions the builder into the state that spawns the - // background worker, which drives the segment pipeline. - let traced = TracedRuntime::builder() - .with_trace_path(&config.output_dir) - .with_task_tracking(true) - .with_runtime_name(RUNTIME_NAME) - .with_process_resource_usage(ProcessResourceUsageConfig::default()); + let recorder = recorder(writer) + .with_process_resource_usage(ProcessResourceUsageConfig::default()) + .build(); + let guard = recorder.handle().clone(); + let shutdown: ShutdownRecorder = Box::new(move || recorder.graceful_shutdown(SHUTDOWN_TIMEOUT)); - // `build_and_start` rather than `build`: `build` returns a live guard that - // never records, writing segments that contain only a header. - // - // No `with_task_dumps` here. dial9 captures a task dump only for futures it + let attached = guard + .attach_tokio_runtime( + builder, + TokioAttachOptions::builder() + .runtime_name(RUNTIME_NAME) + .task_tracking_enabled(true) + .build(), + ) + .map(|runtime| (runtime, guard, shutdown)); + + // No task dumps here. dial9 captures a task dump only for futures it // wrapped itself, i.e. those spawned via `dial9_tokio_telemetry::spawn`; // `tokio::spawn` gets no wrapper. RustFS spawns with `tokio::spawn` - // throughout, so calling `with_task_dumps` records nothing. Measured on an + // throughout, so enabling task dumps records nothing. Measured on an // identical workload: 0 dumps via `tokio::spawn`, 14709 via `dial9::spawn`. // See rustfs/backlog#1157 (D9-16) and dial9-rs/dial9#477. // // No `with_s3_uploader` here: dial9's `worker-s3` feature carries a // vulnerable TLS stack. See the note in `crates/obs/Cargo.toml`. - finish_traced_runtime(traced.build_and_start(builder, writer), config) + finish_traced_runtime(attached, config) } /// Publish the outcome of a traced-runtime build and start the background /// disk-usage refresher. fn finish_traced_runtime( - started: std::io::Result<(tokio::runtime::Runtime, TelemetryGuard)>, + started: std::io::Result<(tokio::runtime::Runtime, TelemetryGuard, ShutdownRecorder)>, config: Dial9Config, ) -> Result<(tokio::runtime::Runtime, Dial9SessionGuard), TelemetryError> { - let (runtime, guard) = started.map_err(|e| { + let (runtime, guard, shutdown) = started.map_err(|e| { dial9_runtime_state().record_runtime_error(&config); - TelemetryError::Io(format!("Failed to build dial9 TracedRuntime: {e}")) + TelemetryError::Io(format!("Failed to attach dial9 runtime telemetry: {e}")) })?; - // `is_enabled` distinguishes a live guard from the inert one a lenient - // config produces after a build failure. It does NOT mean recording has - // started — a guard from `build` (rather than `build_and_start`) reports - // `true` while writing segments that contain only a header. Recording is - // guaranteed by the `build_and_start` call above, not by this check. if !guard.is_enabled() { dial9_runtime_state().record_runtime_error(&config); - return Err(TelemetryError::Io("dial9 TracedRuntime built with telemetry disabled".to_string())); + return Err(TelemetryError::Io("dial9 runtime telemetry attached with recording disabled".to_string())); } dial9_runtime_state().record_runtime_started(&config); @@ -160,7 +175,14 @@ fn finish_traced_runtime( "dial9 state changed" ); - Ok((runtime, Dial9SessionGuard { guard, config })) + Ok(( + runtime, + Dial9SessionGuard { + guard, + shutdown: Some(shutdown), + config, + }, + )) } /// Periodically restate trace-file disk usage so the metrics collector can read diff --git a/crates/obs/src/telemetry/dial9/mod.rs b/crates/obs/src/telemetry/dial9/mod.rs index 08c294c33..a186abf9c 100644 --- a/crates/obs/src/telemetry/dial9/mod.rs +++ b/crates/obs/src/telemetry/dial9/mod.rs @@ -49,13 +49,13 @@ //! //! # Known observability gap //! -//! `dial9`'s `RotatingWriter` stops accepting writes (its internal `Finished` -//! state) when the output directory disappears or a segment cannot be sealed, -//! and it exposes no way to observe that from outside. `TelemetryGuard::is_enabled` -//! reports how the session was *built*, not whether it is still writing. There -//! is therefore no `writer_healthy` metric: it could only ever be hard-coded to -//! `1`. Watch `rustfs_dial9_disk_usage_bytes` — a session that is recording but -//! whose disk usage stops growing has most likely hit this state. +//! `dial9`'s `DiskBuffer` stops accepting writes when the output directory +//! disappears or a segment cannot be sealed, and it exposes no way to observe +//! that from outside. `Dial9Handle::is_enabled` reports whether the recorder is +//! connected and unpaused, not whether the disk writer is still making progress. +//! There is therefore no `writer_healthy` metric: it could only ever be +//! hard-coded to `1`. Watch `rustfs_dial9_disk_usage_bytes` — a session that is +//! recording but whose disk usage stops growing has most likely hit this state. //! Reported upstream as dial9-rs/dial9#658. mod config; diff --git a/crates/obs/src/telemetry/dial9/state.rs b/crates/obs/src/telemetry/dial9/state.rs index 644fc3bce..f3a53d559 100644 --- a/crates/obs/src/telemetry/dial9/state.rs +++ b/crates/obs/src/telemetry/dial9/state.rs @@ -27,6 +27,9 @@ use std::sync::OnceLock; use std::sync::RwLock; use std::sync::atomic::{AtomicU64, Ordering}; +/// Segment filename stem used by `dial9` rotating disk buffers. +const DIAL9_SEGMENT_STEM: &str = "trace"; + /// Point-in-time view of dial9 runtime state. #[derive(Debug, Clone, Default)] pub(crate) struct Dial9RuntimeSnapshot { @@ -66,8 +69,8 @@ impl Dial9RuntimeState { pub(super) fn record_config(&self, config: &Dial9Config) { *self.trace_dir.write().expect("dial9 trace_dir lock should not be poisoned") = Some(TraceLocation { - output_dir: PathBuf::from(&config.output_dir), - file_prefix: config.file_prefix.clone(), + output_dir: config.base_path(), + file_prefix: DIAL9_SEGMENT_STEM.to_string(), }); if !config.enabled { self.active_sessions.store(0, Ordering::Relaxed); @@ -168,11 +171,11 @@ mod tests { #[test] fn measure_disk_usage_sums_only_matching_prefix() { let dir = tempdir().expect("create temp dir"); - std::fs::write(dir.path().join("rustfs-tokio.0.bin"), vec![0_u8; 128]).expect("write segment"); - std::fs::write(dir.path().join("rustfs-tokio.1.bin"), vec![0_u8; 64]).expect("write segment"); + std::fs::write(dir.path().join("trace.0.bin"), vec![0_u8; 128]).expect("write segment"); + std::fs::write(dir.path().join("trace.1.bin"), vec![0_u8; 64]).expect("write segment"); std::fs::write(dir.path().join("unrelated.log"), vec![0_u8; 4096]).expect("write unrelated"); - assert_eq!(measure_disk_usage_bytes(dir.path(), "rustfs-tokio"), 192); + assert_eq!(measure_disk_usage_bytes(dir.path(), DIAL9_SEGMENT_STEM), 192); } #[test] diff --git a/crates/s3-client/Cargo.toml b/crates/s3-client/Cargo.toml index af63ae896..280cf3794 100644 --- a/crates/s3-client/Cargo.toml +++ b/crates/s3-client/Cargo.toml @@ -54,7 +54,6 @@ rustls-pki-types.workspace = true s3s = { workspace = true, features = ["minio"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["raw_value"] } -sha1 = { workspace = true } sha2 = { workspace = true } thiserror.workspace = true time = { workspace = true, features = ["parsing", "formatting", "macros", "serde"] } diff --git a/rustfs/src/config/info.rs b/rustfs/src/config/info.rs index 1c8649116..9468bc1dc 100644 --- a/rustfs/src/config/info.rs +++ b/rustfs/src/config/info.rs @@ -591,12 +591,19 @@ struct FeatureSpec { default_enabled: bool, } -fn feature_specs() -> [FeatureSpec; 7] { - [ +fn feature_specs() -> &'static [FeatureSpec] { + &[ + FeatureSpec { + name: "default", + enabled: cfg!(feature = "default"), + description: "Default feature set", + dependencies: "ftps + webdav", + default_enabled: true, + }, FeatureSpec { name: "metrics-gpu", enabled: cfg!(feature = "metrics-gpu"), - description: "Metrics GPU support", + description: "GPU metrics support", dependencies: "rustfs-obs/gpu", default_enabled: false, }, @@ -610,7 +617,7 @@ fn feature_specs() -> [FeatureSpec; 7] { FeatureSpec { name: "swift", enabled: cfg!(feature = "swift"), - description: "Swift storage backend", + description: "OpenStack Swift protocol support", dependencies: "rustfs-protocols/swift", default_enabled: false, }, @@ -621,6 +628,13 @@ fn feature_specs() -> [FeatureSpec; 7] { dependencies: "rustfs-protocols/webdav", default_enabled: true, }, + FeatureSpec { + name: "sftp", + enabled: cfg!(feature = "sftp"), + description: "SFTP protocol support", + dependencies: "rustfs-protocols/sftp", + default_enabled: false, + }, FeatureSpec { name: "license", enabled: cfg!(feature = "license"), @@ -635,11 +649,81 @@ fn feature_specs() -> [FeatureSpec; 7] { dependencies: "(none)", default_enabled: false, }, + FeatureSpec { + name: "tracing-chunk-debug", + enabled: cfg!(feature = "tracing-chunk-debug"), + description: "Per-chunk data-plane tracing", + dependencies: "(none)", + default_enabled: false, + }, FeatureSpec { name: "full", enabled: cfg!(feature = "full"), - description: "All features enabled", - dependencies: "metrics-gpu + ftps + swift + webdav", + description: "Full protocol and observability bundle", + dependencies: "metrics-gpu + ftps + swift + webdav + sftp + pyroscope", + default_enabled: false, + }, + FeatureSpec { + name: "e2e-test-hooks", + enabled: cfg!(feature = "e2e-test-hooks"), + description: "End-to-end test hooks", + dependencies: "(none)", + default_enabled: false, + }, + FeatureSpec { + name: "connect-e2e-short-credentials", + enabled: cfg!(feature = "connect-e2e-short-credentials"), + description: "Short-lived Connect credentials for debug E2E builds", + dependencies: "(none)", + default_enabled: false, + }, + FeatureSpec { + name: "offline-enrollment-e2e-root", + enabled: cfg!(feature = "offline-enrollment-e2e-root"), + description: "Dedicated offline enrollment E2E root", + dependencies: "(none)", + default_enabled: false, + }, + FeatureSpec { + name: "rio-v2", + enabled: cfg!(feature = "rio-v2"), + description: "RIO v2 storage path support", + dependencies: "rustfs-ecstore/rio-v2", + default_enabled: false, + }, + FeatureSpec { + name: "pyroscope", + enabled: cfg!(feature = "pyroscope"), + description: "Pyroscope profiling support", + dependencies: "rustfs-obs/pyroscope", + default_enabled: false, + }, + FeatureSpec { + name: "dial9", + enabled: cfg!(feature = "dial9"), + description: "Tokio runtime telemetry", + dependencies: "rustfs-obs/dial9", + default_enabled: false, + }, + FeatureSpec { + name: "hotpath", + enabled: cfg!(feature = "hotpath"), + description: "Hotpath instrumentation", + dependencies: "hotpath + RustFS crate hotpath features", + default_enabled: false, + }, + FeatureSpec { + name: "hotpath-alloc", + enabled: cfg!(feature = "hotpath-alloc"), + description: "Hotpath allocation diagnostics", + dependencies: "hotpath + hotpath/hotpath-alloc + RustFS crate hotpath-alloc features", + default_enabled: false, + }, + FeatureSpec { + name: "hotpath-cpu", + enabled: cfg!(feature = "hotpath-cpu"), + description: "Hotpath CPU attribution", + dependencies: "hotpath + hotpath/hotpath-cpu + RustFS crate hotpath-cpu features", default_enabled: false, }, ] @@ -655,7 +739,7 @@ struct DepsInfoJson { fn collect_deps_info_json() -> DepsInfoJson { let features: Vec = feature_specs() - .into_iter() + .iter() .map(|feature| FeatureInfoJson { name: feature.name, enabled: feature.enabled, @@ -901,7 +985,7 @@ fn format_deps_info() -> String { output.push_str("### Feature Status\n\n"); output.push_str("| Feature | Status | Description |\n"); output.push_str("|---------|--------|-------------|\n"); - for feature in &features { + for feature in features { let status = if feature.enabled { "✓" } else { "✗" }; output.push_str(&format!("| {} | {} | {} |\n", feature.name, status, feature.description)); } @@ -916,7 +1000,7 @@ fn format_deps_info() -> String { output.push_str("\n### Feature Dependencies\n\n"); output.push_str("| Feature | Dependencies |\n"); output.push_str("|---------|-------------|\n"); - for feature in &features { + for feature in features { output.push_str(&format!("| {} | {} |\n", feature.name, feature.dependencies)); } @@ -1001,10 +1085,22 @@ mod tests { let info = collect_deps_info_json(); let feature_names: Vec<_> = info.features.iter().map(|feature| feature.name).collect(); - assert_eq!(info.total_count, 7); - assert_eq!(info.features.len(), 7); + assert_eq!(info.total_count, 19); + assert_eq!(info.features.len(), 19); + assert!(feature_names.contains(&"default")); assert!(feature_names.contains(&"metrics-gpu")); + assert!(feature_names.contains(&"sftp")); assert!(feature_names.contains(&"io-scheduler-debug")); + assert!(feature_names.contains(&"tracing-chunk-debug")); + assert!(feature_names.contains(&"e2e-test-hooks")); + assert!(feature_names.contains(&"connect-e2e-short-credentials")); + assert!(feature_names.contains(&"offline-enrollment-e2e-root")); + assert!(feature_names.contains(&"rio-v2")); + assert!(feature_names.contains(&"pyroscope")); + assert!(feature_names.contains(&"dial9")); + assert!(feature_names.contains(&"hotpath")); + assert!(feature_names.contains(&"hotpath-alloc")); + assert!(feature_names.contains(&"hotpath-cpu")); assert!(!feature_names.contains(&"manual-test-runners")); assert!(!feature_names.contains(&"metrics")); assert!(!feature_names.contains(&"direct-io")); @@ -1016,10 +1112,16 @@ mod tests { assert!(output.contains("| metrics-gpu |")); assert!(output.contains("| io-scheduler-debug |")); + assert!(output.contains("| tracing-chunk-debug |")); + assert!(output.contains("| sftp |")); + assert!(output.contains("| rio-v2 |")); + assert!(output.contains("| dial9 |")); + assert!(output.contains("| hotpath-cpu |")); + assert!(output.contains("| default | enabled by default |")); assert!(!output.contains("| manual-test-runners |")); assert!(output.contains("| ftps | enabled by default |")); assert!(output.contains("| webdav | enabled by default |")); - assert!(output.contains("| full | metrics-gpu + ftps + swift + webdav |")); + assert!(output.contains("| full | metrics-gpu + ftps + swift + webdav + sftp + pyroscope |")); assert!(!output.contains("| direct-io |")); }