#!/usr/bin/env python3 """Veralteten Seiten-Cache des Wirts für alle Dateien dieses Containers verwerfen (vor Docker-Start). Hintergrund (12.10.2026, Task 5.2, HA-Probe): Nach `zfs receive` (Proxmox-Replikation) liefert der Wirt für Dateien, die auf ihm früher per mmap geöffnet waren, weiter den ALTEN Inhalt aus dem Seiten-Cache (pve44: `zfs diff` leer, `written` 0, Datei-md5 trotzdem ungleich Snapshot; ZFS 2.4.4, Kernel 7.0.14). Zieht der Container per HA auf so einen Knoten, liest dockerd veraltete bbolt-Datenbanken (Panic „invalid freelist page“, Netzwerke/Volumes „not found“); Prometheus-TSDB wäre ebenso betroffen. POSIX_FADV_DONTNEED wirft saubere, nicht gemappte Seiten aus dem Cache; danach liest der Kernel wieder von ZFS. Läuft vor containerd/docker, scheitert nie (Exit 0), protokolliert geänderte *.db-Prüfsummen. """ import hashlib import os import stat import sys SKIP = {"/proc", "/sys", "/dev", "/run", "/tmp"} WATCH = ("/var/lib/docker/", "/var/lib/containerd/") def md5(path: str) -> str: h = hashlib.md5() with open(path, "rb") as fh: for chunk in iter(lambda: fh.read(1 << 20), b""): h.update(chunk) return h.hexdigest() def main() -> int: root_dev = os.stat("/").st_dev files = dropped = errors = 0 changed = [] for dirpath, dirnames, filenames in os.walk("/"): dirnames[:] = [d for d in dirnames if os.path.join(dirpath, d) not in SKIP] try: if os.lstat(dirpath).st_dev != root_dev: dirnames[:] = [] continue except OSError: continue for name in filenames: path = os.path.join(dirpath, name) try: st = os.lstat(path) if not stat.S_ISREG(st.st_mode) or st.st_dev != root_dev or st.st_size == 0: continue files += 1 watch = path.endswith(".db") and path.startswith(WATCH) before = md5(path) if watch else None fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) try: os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED) dropped += 1 finally: os.close(fd) if watch: after = md5(path) if after != before: changed.append(f"{path}: {before[:8]} -> {after[:8]}") except OSError: errors += 1 for line in changed: print(f"veralteter Cache verworfen: {line}") print(f"{files} Dateien, Cache verworfen für {dropped}, Fehler {errors}, geänderte Datenbanken {len(changed)}") return 0 if __name__ == "__main__": try: sys.exit(main()) except Exception as exc: # nie den Docker-Start blockieren print(f"Fehler: {exc}", file=sys.stderr) sys.exit(0)