# 040 — One rate/window implementation, shared by PromQL and alerts ## Summary "Rate over a window" is implemented twice with different semantics and no shared code: - PromQL's `rate()` (`windowed_rate_series`, `src/engine.rs`) scans the trailing window at every historical point over indexed bucket data (occurrences-per-second, backfilled). - Alerts' `Threshold`/`Ratio` conditions (`src/alerts.rs`) maintain an independent live sliding `VecDeque` window over raw log-line matches (current count only, resets on process restart, no backfill), deliberately bypassing the bucket/PromQL pipeline for cost reasons (module doc: a `metric[param]` comparison would require a full bucket rescan per line). These can and will disagree for equivalent-looking rate/ratio rules, since they read different data and compute the window differently. ## Scope - Extract a single windowed-rate function, e.g. in a new `src/rate.rs`: `fn rate_at(points: &[(Timestamp, f64)], now: Timestamp, window_ms: i64) -> f64`. - Have PromQL's `rate()` evaluation call this shared function (behavior should be unchanged for existing dashboard queries). - Have alerts call the same function against the bucket store instead of maintaining a separate `VecDeque` over raw matches. If alerts' proportional per-line cost guarantee can't be met by reading buckets directly, solve that inside the shared module (e.g. an incrementally maintained window index) rather than keeping two implementations. - As a consequence, alert rate/ratio conditions gain backfilled history (they no longer reset to empty on process restart) — call this out in release notes since it changes alert-firing behavior on restart. ## Acceptance criteria - Only one windowed-rate implementation exists in the codebase. - A dashboard `rate()` chart and an alert `Threshold`/`Ratio` condition over the same metric/window agree on the computed rate for the same historical data, verified with a seeded fixture. - Alert conditions no longer silently reset their window on process restart (or this is an explicitly flagged, deliberate behavior change). - Existing dashboard `rate()` queries produce unchanged output for existing test fixtures. ## Depends on None — independent of the param unification work ([037](037-unify-param-declarations.md)–[039](039-metric-param-query-resolves-declared-params.md)).