dsfb_database/residual/cache_io.rs
1//! Cache / buffer collapse residuals.
2//!
3//! Two channels per source:
4//! * **hit-ratio drop** — `expected_hit_ratio − observed_hit_ratio`,
5//! positive when the cache is failing.
6//! * **I/O wait amplification** — `observed_io_wait_seconds /
7//! baseline_io_wait_seconds − 1.0`, positive when I/O is slower than
8//! the rolling baseline.
9//!
10//! In PostgreSQL 16+ both are first-class via `pg_stat_io.hits` and
11//! `pg_stat_io.read_time` (fact #44). In SQL Server they are reachable via
12//! `sys.dm_os_buffer_descriptors` plus Query Store I/O wait stats. In Oracle
13//! they are in `V$SEGMENT_STATISTICS` plus `V$SYSTEM_EVENT`.
14
15use super::{ResidualClass, ResidualSample, ResidualStream};
16
17pub fn push_hit_ratio(
18 stream: &mut ResidualStream,
19 t: f64,
20 cache_id: &str,
21 expected: f64,
22 observed: f64,
23) {
24 let drop = expected - observed;
25 stream.push(ResidualSample::new(t, ResidualClass::CacheIo, drop).with_channel(cache_id));
26}
27
28pub fn push_io_amplification(
29 stream: &mut ResidualStream,
30 t: f64,
31 file_id: &str,
32 observed_seconds: f64,
33 baseline_seconds: f64,
34) {
35 let amp = if baseline_seconds > 0.0 {
36 observed_seconds / baseline_seconds - 1.0
37 } else {
38 0.0
39 };
40 stream.push(
41 ResidualSample::new(t, ResidualClass::CacheIo, amp).with_channel(format!("{file_id}#io")),
42 );
43}