mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-11 23:56:53 +00:00
8039a4ceae
fix(ecstore): make body-cache hook re-registrable + add e2e regressions ODC-21 (backlog#1126): the GET body-cache hook lived in a first-wins OnceLock. When AppContext is rebuilt (config reload, test re-init) a fresh ObjectDataCacheAdapter is constructed and re-registered, but the OnceLock kept ecstore's GET probe pointed at adapter #1 while every usecase-layer fill and invalidation targeted adapter #2 — silently degrading the feature to a 0% hit rate with no error, log, or metric, and stranding entries in the unreachable cache until their TTL. Replace the slot with RwLock<Option<Arc<dyn GetObjectBodyCacheHook>>> so re-registration atomically swaps to the newest adapter, and log at WARN when a swap replaces a *different* instance (Arc::ptr_eq). RwLock over ArcSwapOption because arc-swap's RefCnt is impl<T> (Sized, thin *mut T) and cannot hold an Arc<dyn Trait> without a sized newtype wrapper; the probe reads the slot once per full-object GET but only clones an Arc, negligible next to the metadata quorum fan-out already done before the probe. Add a test-only clear_get_object_body_cache_hook so tests register/unregister deterministically. With the hook now re-registrable, add true end-to-end regressions that drive get_object_reader (not the full_object_plaintext_len predicate) against a real erasure-coded, genuinely-compressed object via the blackbox make_local_set_disks harness, with a stand-in hook playing the app-layer cache (the injection point production uses; the adapter itself lives above ecstore). These close the gap the predicate-only tests left — a caller that opens a new shortcut serving the cached body directly, the original form of both P0s: - backlog#1108: a raw_data_movement_read must yield the STORED (compressed) bytes, never the cached plaintext. - backlog#1109: a compressed cache hit must publish the DECOMPRESSED length as object_info.size (the UploadPartCopy invariant), with the streamed length matching. - backlog#1146: a restore read (restore_request.days) must serve STORED bytes, not the cache. Mutation-verified each e2e test bites: dropping the raw_data_movement_read gate serves plaintext (fails #1108); removing the hit-site size republication publishes 2972 vs 660000 (fails #1109); dropping the restore gate serves plaintext (fails #1146). Co-authored-by: heihutu <heihutu@gmail.com>
71 lines
2.3 KiB
Rust
71 lines
2.3 KiB
Rust
// 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.
|
|
|
|
//! Stub implementation used when the `dial9` feature is off.
|
|
//!
|
|
//! Mirrors the surface of [`super::enabled`] so that callers never need a
|
|
//! `#[cfg]`. Building a traced runtime always fails here, which drives callers
|
|
//! down their standard-runtime fallback path.
|
|
|
|
use crate::TelemetryError;
|
|
|
|
/// Placeholder for `dial9_tokio_telemetry::telemetry::TelemetryGuard`.
|
|
///
|
|
/// Never constructed: [`build_traced_runtime`] always fails without the
|
|
/// `dial9` feature.
|
|
#[derive(Debug)]
|
|
pub struct TelemetryGuard(());
|
|
|
|
/// Placeholder session guard. Never constructed without the `dial9` feature.
|
|
#[derive(Debug)]
|
|
pub struct Dial9SessionGuard(());
|
|
|
|
impl Dial9SessionGuard {
|
|
/// Always false: telemetry support is not compiled in.
|
|
pub fn is_active(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
/// Mirrors the `Drop` the enabled guard uses to flush and seal the trace
|
|
/// segment, so a caller can drop the guard before `process::exit` under either
|
|
/// feature without `clippy::drop_non_drop` firing on this variant.
|
|
impl Drop for Dial9SessionGuard {
|
|
fn drop(&mut self) {}
|
|
}
|
|
|
|
/// Always fails: telemetry support is not compiled in.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Always returns [`TelemetryError::Io`] describing the missing feature.
|
|
pub fn build_traced_runtime(
|
|
_builder: tokio::runtime::Builder,
|
|
) -> Result<(tokio::runtime::Runtime, Dial9SessionGuard), TelemetryError> {
|
|
Err(TelemetryError::Io(
|
|
"dial9 telemetry support is not compiled in; rebuild with --features dial9".to_string(),
|
|
))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn build_traced_runtime_always_fails_without_the_feature() {
|
|
let result = build_traced_runtime(tokio::runtime::Builder::new_current_thread());
|
|
assert!(result.is_err());
|
|
}
|
|
}
|