diff --git a/crates/config/src/constants/runtime.rs b/crates/config/src/constants/runtime.rs index d63ad22d2..0a7601f04 100644 --- a/crates/config/src/constants/runtime.rs +++ b/crates/config/src/constants/runtime.rs @@ -118,6 +118,25 @@ pub const ENV_OBJECT_SEEK_SUPPORT_THRESHOLD: &str = "RUSTFS_OBJECT_SEEK_SUPPORT_ pub const DEFAULT_OBJECT_SEEK_SUPPORT_THRESHOLD: usize = 10 * 1024 * 1024; // Object data cache configuration + +/// Enables the in-memory object body cache, which serves whole-object GET +/// responses without an erasure read. +/// +/// # Timing side channel +/// +/// A cache hit skips the erasure read, bitrot verification and decode, so it is +/// reliably faster than a miss. Any principal authorized to read an object can +/// therefore infer, by timing a single GET, whether *someone* read that object +/// within the entry's lifetime (see `RUSTFS_OBJECT_DATA_CACHE_TTL_SECS` and +/// `..._TIME_TO_IDLE_SECS`). +/// +/// This never crosses an authorization boundary — the probe requires read +/// access to the exact bucket and object, checked before the cache is consulted +/// — but it does disclose co-tenants' recent access patterns on objects the +/// observer may already read. Leave the cache disabled where access-pattern +/// confidentiality matters, such as a bucket shared read-only between competing +/// tenants. Timing noise is not a viable mitigation: it would cost exactly the +/// latency the cache exists to save. pub const ENV_OBJECT_DATA_CACHE_ENABLE: &str = "RUSTFS_OBJECT_DATA_CACHE_ENABLE"; pub const ENV_OBJECT_DATA_CACHE_MODE: &str = "RUSTFS_OBJECT_DATA_CACHE_MODE"; pub const ENV_OBJECT_DATA_CACHE_MAX_BYTES: &str = "RUSTFS_OBJECT_DATA_CACHE_MAX_BYTES"; diff --git a/crates/object-data-cache/src/lib.rs b/crates/object-data-cache/src/lib.rs index 62888c18c..13dcadec1 100644 --- a/crates/object-data-cache/src/lib.rs +++ b/crates/object-data-cache/src/lib.rs @@ -12,11 +12,34 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Engine-only object body cache contracts for RustFS. +//! Engine-only object body cache for RustFS. //! -//! This crate intentionally contains only a minimal skeleton for the initial -//! rollout phase. App-layer semantics and storage-specific read paths stay in -//! the `rustfs` crate. +//! App-layer semantics and storage-specific read paths stay in the `rustfs` +//! crate; this crate owns the key, the eviction backend and the metrics. +//! +//! # Correctness boundary +//! +//! An entry is keyed by object identity — bucket, object, version, etag, size +//! and body variant — and every lookup runs after the caller has resolved fresh +//! metadata from a read quorum. Serving a hit is therefore sound because the +//! key matched metadata that was just read, not because the entry was recently +//! invalidated. Invalidation is process-local and is **hygiene**: it frees +//! capacity promptly, but the key is what keeps a stale body from being served. +//! +//! # Timing side channel +//! +//! A cache hit skips the erasure read, bitrot verification and decode, so it is +//! reliably faster than a miss. Any principal authorized to read an object can +//! therefore infer, by timing a single GET, whether *someone* read that object +//! within the entry's lifetime (`ttl` / `time_to_idle`). +//! +//! This never crosses an authorization boundary: the probe requires read access +//! to the exact bucket and object, which is checked before the cache is +//! consulted. It does leak co-tenants' recent access patterns on objects the +//! observer may already read. Deployments where access-pattern confidentiality +//! matters — a bucket shared read-only between competing tenants — should leave +//! the cache disabled for those buckets. Adding timing noise is not a viable +//! mitigation: it would cost exactly the latency the cache exists to save. pub mod backend; pub mod cache;