Viewing files at commit faa835d7e15eCommit detailsView latest

scripts/oakbench/cachectl.py 93 lines · 4.0 KB · python
1
"""Cache-state discipline for measured rows.
2
3
Cache state is a measurement, not a wish (ADR-0002).  A row may claim "cold"
4
only when an OS page-cache purge verifiably succeeded, and "warm" only when
5
the caller explicitly performed the warming.  Everything else is reported as
6
"unknown" with a reason — never a silently fabricated state.
7
"""
8
9
from __future__ import annotations
10
11
import os
12
import platform
13
import subprocess
14
from pathlib import Path
15
from typing import Any
16
17
PURGE_TIMEOUT_S = 30
18
DARWIN_PURGE = "/usr/sbin/purge"
19
LINUX_DROP_CACHES = Path("/proc/sys/vm/drop_caches")
20
21
22
def _purge_result(attempted: bool, ok: bool, method: str | None, error: str | None) -> dict[str, Any]:
23
    return {"attempted": attempted, "ok": ok, "method": method, "error": error}
24
25
26
def _run_purge_command(command: list[str]) -> str | None:
27
    """Run a purge command; return None on success, an error string on failure."""
28
    try:
29
        completed = subprocess.run(command, capture_output=True, text=True, timeout=PURGE_TIMEOUT_S)
30
    except Exception as error:
31
        return f"{type(error).__name__}: {error}"
32
    if completed.returncode != 0:
33
        detail = (completed.stderr or completed.stdout or "").strip()
34
        return f"exit {completed.returncode}" + (f": {detail}" if detail else "")
35
    return None
36
37
38
def _drop_linux_caches(path: Path = LINUX_DROP_CACHES) -> str | None:
39
    """Write to /proc/sys/vm/drop_caches; return None on success, error string on failure."""
40
    try:
41
        os.sync()
42
        path.write_text("3\n")
43
    except Exception as error:
44
        return f"{type(error).__name__}: {error}"
45
    return None
46
47
48
def purge_fs_caches() -> dict[str, Any]:
49
    """Best-effort OS page-cache purge.  Never raises; honesty over success."""
50
    system = platform.system()
51
    if system == "Darwin":
52
        # purge(8) needs root on current macOS. Try it directly, then a
53
        # non-interactive sudo (works only with a NOPASSWD sudoers entry for
54
        # /usr/sbin/purge); never prompt, never fake success.
55
        error = _run_purge_command([DARWIN_PURGE])
56
        if error is None:
57
            return _purge_result(True, True, "darwin_purge", None)
58
        sudo_error = _run_purge_command(["sudo", "-n", DARWIN_PURGE])
59
        if sudo_error is None:
60
            return _purge_result(True, True, "darwin_sudo_purge", None)
61
        return _purge_result(True, False, "darwin_purge", f"{error}; sudo -n: {sudo_error}")
62
    if system == "Linux":
63
        error = _drop_linux_caches()
64
        return _purge_result(True, error is None, "linux_drop_caches", error)
65
    return _purge_result(False, False, None, f"unsupported_platform: {system}")
66
67
68
def cache_fields(
69
    requested: str | None,
70
    purge_result: dict[str, Any] | None,
71
    *,
72
    warmed_by_scenario: bool = False,
73
) -> dict[str, Any]:
74
    """Honest ``cache_state``/``cache_state_reason`` row fields.
75
76
    ``warmed_by_scenario`` is the caller's explicit claim that it ran a warming
77
    pass; without that claim a "warm" request still reports "unknown".
78
    """
79
    if requested is None:
80
        return {"cache_state": "unknown", "cache_state_reason": "cache_state_unset"}
81
    if requested == "cold":
82
        if purge_result and purge_result.get("ok"):
83
            method = purge_result.get("method") or "unknown_method"
84
            return {"cache_state": "cold", "cache_state_reason": f"fs_caches_purged: {method}"}
85
        if purge_result and purge_result.get("attempted"):
86
            error = purge_result.get("error") or "unknown_error"
87
            return {"cache_state": "unknown", "cache_state_reason": f"purge_failed: {error}"}
88
        return {"cache_state": "unknown", "cache_state_reason": "purge_not_attempted"}
89
    if requested == "warm":
90
        if warmed_by_scenario:
91
            return {"cache_state": "warm", "cache_state_reason": "explicitly_warmed_by_scenario"}
92
        return {"cache_state": "unknown", "cache_state_reason": "warm_requested_without_warming_claim"}
93
    return {"cache_state": "unknown", "cache_state_reason": f"unrecognized_cache_state_request: {requested}"}