
Sechzig Container auf einem Server
Eine Bare-Metal-Box führt Dutzende bis Hunderte von Hoody-Containern aus. KSM und BTRFS-Dedup machen die Marginalkosten nahezu null.
Der erste Job in deiner Pipeline tart node_modules und pipet sie in eine Hoody-URL. Zwanzig nachgelagerte Jobs holen dieselbe URL per curl und entpacken. Mit ?n=20 wartet der Producer, bis sich alle zwanzig Worker verbunden haben, und streamt dann einmal — gefannt out an alle. Kein S3-Bucket, keine Cache-Action, keine Egress-Rechnung.
Es gibt keine Client-Library, keinen Daemon, kein SDK. Der Producer streamt seinen Tarball in PUT /pipe/cache?n=20. Jeder Worker streamt ihn aus GET /pipe/cache?n=20 wieder raus. Die Pipe hält die Bytes nur, während sie in Flight sind — niemals auf der Disk.
tar packt node_modules; zstd komprimiert on the fly; curl PUTtet die Bytes direkt in den Pipe-Pfad. Kein Tempfile, kein upload-artifact-Step, keine Bucket-Credentials.
Jeder Test-Worker GETtet dieselbe URL, dekomprimiert und entpackt in sein Working Directory. Langsame Worker üben Backpressure auf den Producer aus, blockieren aber nicht die schnelleren.
?n=20Ein Query-Parameter. Producer und Consumer einigen sich auf dasselbe n. Die Pipe hält den Upload, bis exakt so viele Receiver verbunden sind, und öffnet dann die Schleusen.
CI-Caching war früher eine Steuer: Storage, den du morgen überschreibst, Egress jedes Mal, wenn ein Worker zieht, Engineering-Zeit für die Macken der Cache-Action. Die Pipe streicht alle drei Posten auf einmal.
Es gibt keinen S3-Bucket, weil es keinen Storage gibt. Die Pipe vergisst die Bytes in der Sekunde, in der der Transfer endet — also gibt es nichts, wofür man pro GB-Monat oder pro GB-Out abrechnen könnte. Der Cache ist kein Kostenposten mehr.
Keine YAML-codierten Keys, kein save-cache/restore-cache-Split, kein Debugging, warum ein Cache-Hit nicht auf dem richtigen Runner passiert ist. Nur curl. Dieselben zwei Zeilen laufen auf GitHub Actions, BuildKite, Jenkins, deinem Laptop oder einem Cron-Container.
Branches nutzen einfach unterschiedliche Pfade. /pipe/cache/main, /pipe/cache/feat-x, /pipe/cache/PR-742. Nichts zu invalidieren. Nichts zu evicten. Wenn der Branch stirbt, wird sein Pfad nicht mehr abgefragt — und das ist der gesamte Lifecycle.
Bei einem realen Workload — node_modules um die 800 MB, zwanzig parallele Test-Worker, hundert CI-Runs pro Tag — ist der Großteil der Rechnung Egress, nicht Storage.
20× Egress
Jeder der zwanzig Worker zieht den Cache aus S3. Zwanzig Downloads eines 800-MB-Tarballs sind 16 GB Egress pro CI-Run. Der Bucket selbst ist der einfache Teil — der Egress ist es, was sich aufaddiert.
1× Transfer
Der Producer streamt die 800 MB einmal. Die Pipe fanntoutet die Bytes in Flight an alle zwanzig Receiver. Ein Transfer durch die Leitung, kein Per-Receiver-Multiplikator, keine Storage-Rechnung.
Zahlen sind illustrativ für einen typischen Node-Monorepo-Cache. Tatsächliche Einsparungen hängen von Tarball-Größe, Worker-Fan-out und dem Egress-Preis ab, den dein Provider aus der Cache-Region berechnet. Die Form — linear vs. konstant in der Worker-Anzahl — bleibt invariant.
Die Cache-Schicht ist HTTP. War sie immer. Wir haben es nur nicht gemerkt.
Caches waren nie Storage. Es ging darum, dieselben Bytes ohne Rebuild an N Worker zu kriegen. HTTP macht das längst — sobald du eine URL an eine bekannte Anzahl von Receivern fan-outten lässt. Der Bucket war ein Workaround für das Fan-out, das wir nicht hatten.
Ein PUT, n GETs, identische Bytes. Backpressure ist pro Receiver, damit der langsame Worker die schnellen nicht ausbremst.
PUT /pipe/cache?n=20Die Pipe hält die Bytes für den Transfer und vergisst sie, wenn er endet. Nichts zu evicten, nichts zu lifecyclen, nichts zu backupen.
TTL ≤ 5 min · dann evictedJeder Branch wählt seinen eigenen Pfad. Kein gemeinsamer Keyspace, keine Kollisionen. Der Pfad ist der Cache-Key und die URL zugleich.
/pipe/cache/[branch]Die meisten CI-Caches lösen dasselbe Problem: denselben Tarball an N Worker bringen. Sie tun es über Storage und Egress. Die Pipe tut es über die Leitung.
Zwei curl-Befehle. Eine URL. Zwanzig Worker versorgt.