feat(info): report all rustfs features (#6722)

* feat(info): report all rustfs features

Co-Authored-By: heihutu <heihutu@gmail.com>

* chore(deps): update s3s revision

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(obs): adapt dial9 telemetry API

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-08-27 14:45:50 +08:00
committed by GitHub
parent 9a1a15ca58
commit c006f84461
11 changed files with 344 additions and 143 deletions
Generated
+153 -76
View File
@@ -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",
+5 -5
View File
@@ -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" }
-1
View File
@@ -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 }
-1
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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)
}
+54 -32
View File
@@ -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<dyn FnOnce() + Send + 'static>;
/// 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<ShutdownRecorder>,
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
+7 -7
View File
@@ -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;
+8 -5
View File
@@ -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]
-1
View File
@@ -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"] }
+114 -12
View File
@@ -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<FeatureInfoJson> = 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 |"));
}