Kompression
Object Storage unterstützt At-Rest-Kompression — Bodies werden vor dem Put komprimiert, beim Get dekomprimiert. Drei Modi; wähle den CPU-gegen-Größe-Kompromiss, der zum Payload passt.
import { ObjectStorageDurableStateStore, S3ObjectStorageBackend,} from 'actor-ts';
const store = new ObjectStorageDurableStateStore({ backend: new S3ObjectStorageBackend({ /* ... */ }), compression: { algorithm: 'zstd', level: 6, // optional; zstd 1–22 (Default 3) },});Jetzt wird jeder persistierte State vor dem Upload zstd-komprimiert. Reads dekomprimieren transparent.
Algorithmen
Abschnitt betitelt „Algorithmen“| Algorithmus | Kompressionsverhältnis | CPU-Kosten | Wann |
|---|---|---|---|
zstd | Am besten | Niedrig–moderat (Decode sehr schnell) | Große State-Blobs — bestes Verhältnis bei konkurrenzfähiger Geschwindigkeit. |
gzip | Gut (typisch 50-70 %) | Moderat | Universeller Default — node:zlib, läuft auf jedem Runtime. |
none | Keine | Null | Bereits komprimiert oder winzige Payloads (siehe Hinweis unten). |
Der Store-Default ist gzip — es braucht keine native
Runtime-Unterstützung und keine Zusatz-Dependency. Für
text-lastigen State (JSON) erreichen gzip und zstd beide ~70 %
Reduktion; zstd kommt schneller dahin und dekomprimiert flotter.
Für bereits komprimierte Daten (verschlüsselte Bytes,
Image-Daten) bringt Kompression fast keinen Wert und verschwendet
CPU — nimm none.
Es gibt kein
brotliund keindeflate— nurnone,gzipundzstd.
Konfiguration
Abschnitt betitelt „Konfiguration“type CompressionAlgo = 'none' | 'gzip' | 'zstd';
interface CompressionConfig { algorithm: CompressionAlgo; level?: number; // algorithmus-spezifisch; geclamped; undefined = Default}Level sind algorithmus-spezifisch. Out-of-range-Werte werden
geclamped; ohne level gilt der Implementierungs-Default:
- gzip: 0 (keine Kompression) – 9 (am besten). Default 6.
- zstd: 1 (am schnellsten) – 22 (am besten). Default 3.
- none: ignoriert.
Für die meisten Workloads sind die Defaults in Ordnung — dreh nur hoch, wenn Storage-Kosten dominieren.
Runtime-Unterstützung
Abschnitt betitelt „Runtime-Unterstützung“zstd-Support unterscheidet sich je Richtung:
- Compress (Write) — nur nativ: Bun (
Bun.zstdCompressSync) oder Node ≥ 22.15 (zlib.zstdCompressSync). Es gibt keinen Pure-JS-Fallback fürs Schreiben; die optionalefzstd-Peer-Dependency kann nur dekomprimieren.zstdauf einem Runtime ohne native Unterstützung schlägt früh fehl — eager bei der Plugin-Registrierung, nicht kryptisch beim ersten Persist. - Decompress (Read) — erst nativ, dann die optionale
fzstd-Peer-Dependency, sodass ein Runtime ohne natives zstd anderswo geschriebene zstd-Bodies trotzdem lesen kann.
gzip nutzt node:zlib und läuft überall (Bun, Node, Deno) ohne
Zusatz-Dependency.
Wie es funktioniert
Abschnitt betitelt „Wie es funktioniert“Beim Put: Wert serialisieren → JSON-Bytes → compress(bytes) → [verschlüsseln] → mit ATS1-Manifest rahmen → backend.put(framed)
Beim Get: backend.get → gerahmte Bytes → ATS1-Manifest lesen → [entschlüsseln] → gemäß Algorithmus-Flag dekomprimieren → JSON → deserialisierenJeder Body trägt einen kleinen ATS1-Manifest-Header; ein
2-Bit-Feld im Flags-Byte hält den Kompressions-Algorithmus fest
(none / gzip / zstd). Die Dekompression wird ausschließlich
von diesem Flag gesteuert — nicht von einem HTTP-Header. (Auf
S3 setzt das Framework zusätzlich Content-Encoding als Marker,
aber das ist nie die Quelle der Wahrheit fürs Decoding.)
Weil jeder Body seinen Algorithmus selbst beschreibt, funktioniert
das Mischen von none / gzip / zstd im selben Bucket — jedes
Objekt dekodiert gemäß seinem eigenen Manifest.
Level wechseln
Abschnitt betitelt „Level wechseln“Das Level ist eine reine Encoder-Einstellung. Es wird nicht auf dem Wire gespeichert (das Manifest hält den Algorithmus fest, nicht das Level), und der Decompressor braucht es nie — gzip- und zstd-Frames sind selbst-beschreibend.
Daher braucht ein Level-Wechsel keine Migration: Bodies, die mit dem alten Level geschrieben wurden, dekodieren weiter, neue Bodies nutzen das neue Level, und beide mischen sich problemlos. Du kannst das Level auch jederzeit per Actor anheben oder senken, ohne bestehende Daten anzufassen.
Kompression + Serialisierung
Abschnitt betitelt „Kompression + Serialisierung“serialisieren → JSON-Bytes → compress → [verschlüsseln] → speichernKompression läuft nach der Serialisierung und vor der
Verschlüsselung — Ciphertext zu komprimieren ist sinnlos
(hochentrop), und die feste Reihenfolge vermeidet zudem
CRIME-artige Seitenkanäle. Für maximale Größenreduktion bei großem
text-lastigem State ist zstd auf höherem Level meist der beste
einzelne Hebel.
Wann was verwenden
Abschnitt betitelt „Wann was verwenden“| Szenario | Algorithmus |
|---|---|
| Großer text-lastiger State (großes JSON, lange Beschreibungen) | zstd |
| Gemischt Text + Zahlen | zstd oder gzip |
| Maximale Portabilität, natives zstd nicht garantiert | gzip |
| Bereits verschlüsselte / komprimierte Bytes | none (kein Nutzen) |
| Storage-Kosten-begrenzt, Write-once-Read-many | zstd (Level 15-19) |
| CPU-begrenzter Write-Pfad | gzip Level 1 oder zstd Level 1 |
CPU-Kosten (grobe Richtwerte)
Abschnitt betitelt „CPU-Kosten (grobe Richtwerte)“Single-Thread, ~100 KB Blob — nur Größenordnung; echte Zahlen hängen von Payload und Runtime ab:
- gzip Level 6 — ~1-3 ms Encode, ~0,5 ms Decode.
- gzip Level 1 — ~0,5 ms Encode, schnelles Decode.
- zstd Level 3 (Default) — Encode vergleichbar mit gzip, besseres Verhältnis, sehr schnelles Decode.
- zstd Level 19 — spürbar langsamer beim Encode, Decode weiter schnell.
- zstd Level 20-22 (ultra) — deutlich langsamer beim Encode für marginalen Gewinn; reserviere es für Write-once-Read-many-Bulk-Daten.
zstds Asymmetrie (günstiges Decode auf jedem Level) macht es zu einem starken Default für lese-lastigen State. Für typische State-Store-Workloads (kleine Objekte, moderate Schreibrate) sind die CPU-Kosten unsichtbar.
Per-Actor-Override
Abschnitt betitelt „Per-Actor-Override“class SmallStateActor extends DurableStateActor<...> { protected compression() { return { algorithm: 'none' as const }; }}
class LogStateActor extends DurableStateActor<...> { protected compression() { return { algorithm: 'zstd' as const, level: 19 }; }}Überschreibe den Store-Level-Default pro Actor. Nützlich, wenn:
- Die meisten Actors kleinen State haben (keine Kompression nötig).
- Einige großen text-lastigen State haben (hohe Kompression).
Siehe Per-Actor-Policies.
Wie geht’s weiter
Abschnitt betitelt „Wie geht’s weiter“- Object Storage im Überblick — das größere Bild.
- Verschlüsselung — das komplementäre At-Rest-Feature.
- Per-Actor-Policies — Kompression pro Actor konfigurieren.