gosuda.org/zstd: Drop-In DataDog/zstd Compression with Zero CGO and Crash Isolation
gosuda.org/zstd is een CGO_ENABLED=0 Go-compressiebibliotheek die de officiële C Zstandard (libzstd 1.5.7) engine uitvoert binnen een geïsoleerd werksterproces via HQQ gedeeld geheugen. Het biedt een drop-in vervangings-API voor github.com/DataDog/zstd (inclusief one-shot Compress/Decompress, streaming io.Writer/io.Reader, vooraf verwerkte woordenboeken en herbruikbare zero-allocation contexten) terwijl de C-uitvoering volledig buiten de adresruimte van de Go-runtime wordt gehouden.
1go get gosuda.org/zstd@v0.3.10507
Waarom Decompressie Buiten Uw Go-heap Behoort
In productiebackends bevinden compressie en decompressie zich direct op de niet-vertrouwde invoergrens. Logverzamelaars, berichtbrokers, RPC-gateways en opslagengines decompresseren elke minuut gigabytes aan extern aangeleverde frames.
Veel Go-teams kiezen voor github.com/DataDog/zstd omdat dit direct bindt aan de upstream C libzstd: de C-implementatie levert een hoge doorvoer op één kern, strakke woordenboekcompressieverhoudingen op kleine records en C-arena's buiten de heap (ZSTD_CCtx en ZSTD_DCtx) die de scantijden van de Go-garbagecollection niet opblazen.
Het linken van libzstd in een Go-service via cgo brengt echter twee operationele pijnpunten met zich mee:
- Bouw- en Implementatiefrictie: Net als
go-sqlite3vereistDataDog/zstdCGO_ENABLED=1en een doelwit C-compilertoolchain. Het cross-compileren van services of het bouwen van statische binaire bestanden voorlinux/arm64ofwindows/arm64vereist externe C-sysroots. - Gedeelde Adresruimte op Niet-vertrouwde Invoer: Het decompresseren van gecorrumpeerde of adversariële payloads binnen dezelfde virtuele adresruimte als uw Go-server betekent dat elke bug in native C-code of aangepaste woordenboekverwerking het gehele Go-proces kan beëindigen.
gosuda.org/zstd behoudt de exacte upstream C libzstd 1.5.7 engine en de vertrouwde github.com/DataDog/zstd API, maar verplaatst de C-uitvoering naar een geverifieerd kindproces (zstd_server) dat is verbonden met uw Go-programma via gedeeld geheugen op cachelinenerniveau.
Drop-In Vervanging voor github.com/DataDog/zstd
Migreren van github.com/DataDog/zstd vereist enkel het wijzigen van het importpad naar "gosuda.org/zstd". Er is geen externe binaire installatie of opzetcode nodig: bij de eerste aanroep materialiseert gosuda.org/zstd een ingebed SLSA-geattesteerd zstd_server binair bestand voor uw platform, verifieert de SHA-256-digest via gosuda.org/hqq/secureexec en opent een sessiepool voor gedeeld geheugen.
1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "log"
8
9 "gosuda.org/zstd"
10)
11
12func main() {
13 // 1. One-shot Compress / Decompress (mirrors github.com/DataDog/zstd)
14 src := []byte("shared-memory zstd payload without cgo")
15 compressed, err := zstd.CompressLevel(nil, src, zstd.BestSpeed)
16 if err != nil {
17 log.Fatal(err)
18 }
19 plain, err := zstd.Decompress(nil, compressed)
20 if err != nil {
21 log.Fatal(err)
22 }
23 fmt.Printf("one-shot: %d bytes -> %d bytes (%s)\n", len(src), len(compressed), plain)
24
25 // 2. Streaming Writer and Reader with Dictionary Support
26 var buf bytes.Buffer
27 w := zstd.NewWriterLevel(&buf, zstd.DefaultCompression)
28 if _, err := w.Write(src); err != nil {
29 log.Fatal(err)
30 }
31 if err := w.Close(); err != nil {
32 log.Fatal(err)
33 }
34
35 r := zstd.NewReader(&buf)
36 defer r.Close()
37 streamed, err := io.ReadAll(r)
38 if err != nil {
39 log.Fatal(err)
40 }
41 fmt.Printf("streamed: %s\n", streamed)
42}
Ondersteunde Platformen en Ingebedde Binaire Bestanden
Alle zes doelplatformen worden geleverd met vooraf gebouwde, aan SHA-256 gekoppelde werksterbinaire bestanden die zijn ingebed via platformspecifieke bouw-tags (internal/embedded/embed_<goos>_<goarch>.go):
| Doel-OS | Doel-architectuur (GOARCH) | Ingebed Binair Bestand | Binaire Grootte | Bouw-toolchain & Herkomst |
|---|---|---|---|---|
| Linux | amd64, arm64 | zstd_server_linux_<arch> | ~770 - 880 KiB | GCC -O3, SLSA Build L3 Herkomst |
| macOS | arm64, amd64 | zstd_server_darwin_<arch> | ~660 - 795 KiB | AppleClang -O3, SLSA Build L3 Herkomst |
| Windows | amd64 | zstd_server_windows_amd64.exe | ~818 KiB | MSVC -O2, SLSA Build L3 Herkomst |
| Windows | arm64 | zstd_server_windows_arm64.exe | ~551 KiB | zig cc -target aarch64-windows-gnu -O3, SLSA L3 |
Binnen het Gegevenspad voor Gedeeld Geheugen
Om de doorvoer competitief te houden met in-process cgo, vermijdt gosuda.org/zstd socket-serialisatie en heap-allocaties op het kritieke pad.
1. Zero-Allocation Slot-codering (zstd_ctx.go en c/zstd_hooks.c)
Wanneer u een herbruikbare zstd.Ctx gebruikt (of wanneer de Compress- en Decompress-functies op pakketniveau een Ctx lenen van de interne sessiepool), stromen gegevens rechtstreeks door de gemapte StandardLink-slot:
1Go Caller (zstd.Ctx) Shared-Memory Slot C11 Worker (zstd_hooks.c)
2-------------------- ------------------ -------------------------
3src []byte -------------------------> Request Slot Payload -------------> ZSTD_compressCCtx(
4 cctx, resp_slot, src, level)
5dst []byte <------------------------- Response Slot Payload <------------- Writes compressed frame directly
6 into shared response slot
- De gegenereerde
hqq-bindgenclient kopieertsrcrechtstreeks in het in het geheugen gemapte aanvraagslot en draait hetSeqLock-sequentiewoord om. - De C-werkster leest
srcter plekke uit het aanvraagslot en geeft de payload-pointer van het antwoordslot direct door als de doelbuffer aanZSTD_compressCCtxofZSTD_decompressDCtx. - De Go-client kopieert de resulterende slice van het antwoordslot naar de door de aanroeper aangeleverde
dst-slice. In stabiele toestand voerenctx.CompressTo(dst, src, level)enctx.DecompressInto(dst, src)0 allocs/opuit.
Vooraf verwerkte woordenboeken (CDict en DDict) bevinden zich op de C-werkster achter op generatie gecontroleerde 64-bits handles (handle CDict, handle DDict in zstd.idl). Het eenmalig aanmaken van een woordenboek via ctx.NewCDict(rawDict, level) ontleedt de woordenboektabellen binnen de C-werkster en retourneert een lichtgewicht handle die kan worden hergebruikt over duizenden aanroepen zonder de woordenboekbytes opnieuw te versturen.
2. IPC-overhead Amortiseren met Bulk-framing (zstd_bulk.go)
Voor grote payloads (16 KiB en hoger) is de retourtijd van het gedeelde geheugen van ~200 ns te verwaarlozen in vergelijking met de CPU-tijd besteed binnen libzstd. Voor workloads die duizenden kleine berichten van 100 bytes tot 1 KiB tegelijk comprimeren, telt het betalen van zelfs 200 ns per item echter op.
gosuda.org/zstd biedt CompressBulk en DecompressBulk op BulkCtx (zstd_bulk.go) om retourreizen per item te elimineren:
1bctx, err := client.NewBulkCtx(1 << 20)
2if err != nil {
3 log.Fatal(err)
4}
5defer bctx.Close()
6
7items := [][]byte{
8 []byte(`{"event":"login","user":101}`),
9 []byte(`{"event":"purchase","user":101,"amount":4200}`),
10 []byte(`{"event":"logout","user":101}`),
11}
12compressedItems, err := bctx.CompressBulk(nil, items, zstd.DefaultCompression)
CompressBulk verpakt de gehele [][]byte batch in één aanvraagframe met lengtevoorvoegsel in gedeeld geheugen. De C-werkster doorloopt elk item in C (compress_bulk in c/zstd_hooks.c), schrijft alle gecomprimeerde frames aaneengesloten naar het antwoordslot en retourneert de gehele batch in één enkele retourreis via gedeeld geheugen.
3. Overleven van Beëindiging van de Werkster Zonder de Aanroeper te Laten Crashen
Omdat zstd_server in een apart besturingssysteemproces draait, brengt een fatale fout binnen de C-werkster uw Go-applicatie nooit ten val:
- Als
zstd_serverextern wordt beëindigd (bijvoorbeeld doorSIGKILLof een OOM-gebeurtenis), detecteert de HQQ-transportlaag de verbroken peer onmiddellijk en retourneert een getypte*rt.TransportErrorvan de actieveCompress- ofDecompress-aanroep. - Functies op pakketniveau (
zstd.Compress,zstd.Decompress,zstd.NewWriter,zstd.NewReader) detecteren automatisch dat de gedeelde standaardwerkster verdwenen is, starten bij de volgende aanroep een nieuw geverifieerdzstd_server-exemplaar en hervatten de normale werking.
Repository-indeling
De codebase scheidt de drop-in DataDog/zstd-facade van de low-level hqq-bindgen sessie- en bulkprimitieven:
1gosuda.org/zstd
2├── autolaunch.go / hashes.go # Drop-in Compress/Decompress API, auto-recovery pool, and SHA-256 hashes
3├── zstd_stream.go # Streaming Writer/Reader (NewWriterLevelDict, NewReaderDict)
4├── zstd_ctx.go # Zero-allocation Ctx, CDict, and DDict wrappers over hqq-bindgen
5├── zstd_bulk.go # Single-round-trip CompressBulk and DecompressBulk framing
6├── zstd.go # Generated hqq-bindgen Go client for the zstd IDL service
7├── zstd.idl # HQQ IDL schema defining Context, CDict, DDict, and RPC methods
8├── internal/embedded/ # Platform-tagged //go:embed zstd_server binaries (6 OS/Arch targets)
9└── c/ # C11 server implementation (zstd_server.c, zstd_hooks.c, main.c)
Aan de Slag
Installeer gosuda.org/zstd met go get en gebruik het met CGO_ENABLED=0:
1go get gosuda.org/zstd@v0.3.10507
- GitHub Repository: https://github.com/gosuda/hqq-zstd
- Architectuur & Benchmarks: docs/ARCHITECTURE.md
- HQQ Core Runtime: https://gosuda.org/hqq