docs(cache): plan the cargo dependency cache, and why not cargo-chef

Thread entry furyrs#12 measured where a 133s Rust job actually goes: ~120s is
compiling the ~490 external dependencies, which a committed Cargo.lock keeps
bit-identical between runs. The workspace crates cost 7s. #13 settled the
policy -- reuse dependency artifacts, never workspace ones, and keep v* tags
cold.

The plan departs from #12's cargo-chef shape for a concrete reason: the
orchestrator receives build-command as an opaque string (furyrs runs six cargo
invocations across three feature sets) and `cargo chef cook` must be handed the
same flags as the real build. Rather than guess them, invert the order -- build
normally, then prune the workspace members' artifacts out of target/ and
publish the remainder. No knowledge of the command, one entry holding every
feature graph, no new tool in the template.

That makes the safety property checkable rather than argued: after the prune,
an assertion looks for any surviving workspace fingerprint and the job refuses
to publish if it finds one. A missing entry costs 120s; a poisoned one costs a
wrong verdict, which is the worst failure a build system has because it is
silent.

Store is one immutable file per key with an atomic rename, so there is no
shared mutable directory and no advisory locking -- the objection furyrs raised
about VMware Shared Folders under concurrency. Retention of the store is in the
same plan: adding an unbounded directory without it would repeat TODO 2.9 at a
larger scale.
This commit is contained in:
2026-07-22 20:32:38 +02:00
parent 9db74aed3f
commit bd49c4c455
2 changed files with 1415 additions and 1 deletions
+40 -1
View File
@@ -35,7 +35,7 @@ _Last updated: 2026-05-11 (post Sprint 15: §7.5 DONE — Rinomina Setup-* → I
| [x] | Gitea Workflow Integration | 5 | 0 | tutti completati — example.yml usa composite action |
| [x] | §1 Sicurezza & hardening | 3 | 0 | 1.1, 1.4, 1.6 done; 1.2/1.3/1.5/1.7/1.8 deferred (home lab) |
| [x] | §2 Affidabilità & resilienza | 7 | 0 | tutto done (2.1-2.7) |
| [x] | §3 Performance & throughput | 4 | 0 | 3.1/3.2/3.3/3.6 done; 3.4/3.5 deferred — **§3 CHIUSO** |
| [ ] | §3 Performance & throughput | 4 | 1 | 3.1/3.2/3.3/3.6 done; 3.4/3.5 deferred; **3.8 cache cargo pianificata** |
| [x] | §4 Osservabilità & manutenzione | 3 | 0 | 4.1/4.3/4.4 done (4.2 Prometheus, 4.5 Dashboard rimossi) |
| [x] | §5 Qualità codice & test | 5 | 0 | 5.1/5.2/5.3/5.4/5.5 done |
| [x] | §6 Scalabilità & estensioni | 5 | 0 | 6.1-6.2 done, 6.3 deferred (home lab), 6.4-6.5 done, 6.6 done |
@@ -938,6 +938,45 @@ cioè nelle invocazioni manuali/burn-in.
`Start-Job`/`pwsh`/`$IsWindows`. Lo shim resta solo per il runner Windows, dove
`CI_VENV_PYTHON` è sempre settato. Il bug non è più raggiungibile sull'host Linux.
### 3.8 [P2] [ ] Cache delle dipendenze cargo per i job Rust — PIANIFICATO (2026-07-22)
File: [plans/cargo-dependency-cache.md](plans/cargo-dependency-cache.md).
Misurato in furyrs#12: dei ~133 s di un job Rust, ~120 s sono la compilazione
delle ~490 dipendenze esterne, che `Cargo.lock` committato tiene **bit-identiche
fra un run e l'altro**. I crate del workspace costano 7 s. Il registry — che
avevamo indicato per primo — vale 9,3 s (6,8%) e costerebbe +800 MB su template
e backup: **scartato esplicitamente**, non è un rinvio.
Decisione presa con furyrs in #13: riusare solo gli artefatti delle dipendenze,
**mai** quelli del workspace; i tag `v*` restano a freddo.
Il piano si discosta dalla forma `cargo-chef` proposta in #12 per un motivo
concreto: l'orchestratore riceve `build-command` come stringa opaca (furyrs ne
lancia sei, con tre set di feature diversi) e `cargo chef cook` va invocato con
gli stessi flag del build vero. Invece di indovinarli, si fa il contrario —
build normale, poi **potatura** degli artefatti dei membri del workspace da
`target/`, e si pubblica il resto. Nessuna conoscenza del comando, una sola
entry che contiene tutti i grafi di feature, nessun tool nuovo nel template.
La proprietà di sicurezza che furyrs ha chiesto diventa così **verificabile**:
dopo la potatura, un'asserzione controlla che non sopravviva nessun fingerprint
di un membro del workspace. Se ne trova uno, il job **non pubblica**. Fail
closed — una entry mancante costa 120 s, una avvelenata costa un verdetto
sbagliato, che è il guasto peggiore che un sistema di build possa avere.
Store: un file immutabile per chiave sotto `/var/lib/ci/cache/cargo`, chiave
`sha256(Cargo.lock) + rustc -V + host triple`, pubblicazione con `rename(2)`
atomico. Niente directory mutabile condivisa, niente lock — che è il punto
sollevato da furyrs sul locking avvisorio delle Shared Folders con 3 job
concorrenti.
**Nota di collegamento con §2.9**: il piano include la retention dello store
*nello stesso ciclo*. Aggiungere una directory illimitata (una entry per
cambio di `Cargo.lock`, centinaia di MB l'una) senza estendere `retention run`
ripeterebbe l'errore di §2.9 su scala maggiore.
**Prossimo passo**: Task 0 del piano — misurare prima di costruire, come in #12.
### 8.1 [P3] [ ] Mirror GitHub Actions su Gitea locale (rimuovi dipendenza github.com)
File: [.gitea/workflows/lint.yml](.gitea/workflows/lint.yml), Gitea (org `actions`).
File diff suppressed because it is too large Load Diff