fix(feed): backup rebuild non-fatale — copy2→copyfile, il .bak non abortisce piu' il feed live
Il feed BTC/ETH del book era congelato 7 giorni (2026-07-08 -> 07-15): rebuild_history.py usava shutil.copy2 per il .prebuild.bak; copy2=copyfile+copystat e copystat (os.utime/chmod con valori espliciti) richiede la PROPRIETA' del file, non basta il group-write. I .bak sono root:adriano, il cron gira come adriano -> EPERM ogni notte, abortendo PRIMA della scrittura del feed (data/raw/*.parquet fermo a Jul 8). Il book ha ri-girato ogni ora su barra vecchia (TP01 1d non ri-valutato); nessuna perdita (segnale fresco == tenuto) ma cieco 7g. Fix: backup best-effort = shutil.copyfile (solo contenuto, no copystat) in try/except OSError. Un .bak difensivo non deve mai poter bloccare la scrittura del feed live. Verificato: rebuild rigirato -> feed a 2026-07-15, audit cross-venue BTC 1.9/ETH 2.0 bps, file ora adriano:adriano (auto-sana), book dry-run legge ultima barra 2026-07-15 -> HOLD. Diario: docs/diary/2026-07-15-feed-freeze-rebuild-copystat.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -186,7 +186,15 @@ def build(asset: str, write: bool, smoke: bool) -> None:
|
||||
for tf, d in built.items():
|
||||
path = _parquet_path(asset, tf)
|
||||
if path.exists():
|
||||
shutil.copy2(path, BACKUP / f"{asset.lower()}_{tf}.parquet.prebuild.bak")
|
||||
# Backup best-effort: copyfile (solo contenuto, NO copystat).
|
||||
# copy2 copiava anche i metadati -> os.utime/chmod su un .bak di proprieta'
|
||||
# altrui (es. root) fallisce con EPERM per un membro-gruppo e ABORTIVA il
|
||||
# rebuild del feed live (incidente 2026-07-15: feed fermo 7g). Un backup e'
|
||||
# difensivo: un suo errore non deve MAI bloccare la scrittura del feed.
|
||||
try:
|
||||
shutil.copyfile(path, BACKUP / f"{asset.lower()}_{tf}.parquet.prebuild.bak")
|
||||
except OSError as e:
|
||||
print(f" WARN backup {path.name} saltato (non fatale): {e}")
|
||||
tmp = path.with_suffix(".parquet.tmp")
|
||||
d.to_parquet(tmp, index=False)
|
||||
tmp.replace(path)
|
||||
|
||||
Reference in New Issue
Block a user