gosuda.org/zstd: Drop-In DataDog/zstd-komprimering med noll CGO och kraschisoleringsmekanism
gosuda.org/zstd är ett CGO_ENABLED=0 Go-komprimeringsbibliotek som kör den officiella C Zstandard-motorn (libzstd 1.5.7) inuti en isolerad arbetsprocess via HQQ delat minne. Det tillhandahåller ett direkt utbytbart API (drop-in replacement API) för github.com/DataDog/zstd (inklusive engångsfunktioner för Compress/Decompress, strömmande io.Writer/io.Reader, föranalyserade ordböcker och återanvändbara kontexter utan allokering) samtidigt som C-exekveringen hålls helt utanför Go-körklientens adressrymd.
1go get gosuda.org/zstd@v0.3.10507
Varför dekomprimering hör hemma utanför din Go-heap
I produktionsbackend ligger komprimering och dekomprimering direkt på gränsen för otillförlitlig indata. Logginsamlare, meddelandeköer, RPC-gateways och lagringsmotorer dekomprimerar gigabyte av externt tillhandahållna ramar varje minut.
Många Go-team väljer github.com/DataDog/zstd eftersom det binder direkt till den uppströms C-baserade libzstd: C-implementeringen levererar hög enkärnig genomströmning, tighta ordbokskomprimeringsförhållanden på små poster, och C-arenor utanför heapen (ZSTD_CCtx och ZSTD_DCtx) som inte ökar Go-skräpsamlingens skanningstider.
Att länka libzstd till en Go-tjänst via cgo medför dock två operationella problempunkter:
- Bygg- och driftsättningsfriktion: Precis som
go-sqlite3kräverDataDog/zstdCGO_ENABLED=1och en målanpassad C-kompilatorverktygskedja. Korskompilering av tjänster eller byggande av statiska binärer förlinux/arm64ellerwindows/arm64kräver externa C-systemrotkataloger. - Delad adressrymd för otillförlitlig indata: Att dekomprimera korrumperade eller fientliga nyttoaster inom samma virtuella adressrymd som din Go-server innebär att eventuella buggar i den inhemska C-koden eller anpassad ordbokshantering kan avsluta hela Go-processen.
gosuda.org/zstd behåller exakt samma uppströms C-motor libzstd 1.5.7 och det bekanta API:et för github.com/DataDog/zstd, men flyttar C-exekveringen till en verifierad barnprocess (zstd_server) som är ansluten till ditt Go-program via delat minne på cachelinjenivå.
Direkt utbytbar ersättning för github.com/DataDog/zstd
Migrering från github.com/DataDog/zstd kräver endast en ändring av importvägen till "gosuda.org/zstd". Ingen extern binärinstallation eller inställningskod behövs: vid det första anropet materialiserar gosuda.org/zstd en inbäddad SLSA-attesterad zstd_server-binär för din plattform, verifierar dess SHA-256-digest via gosuda.org/hqq/secureexec, och öppnar en sessionspool för delat minne.
1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "log"
8
9 "gosuda.org/zstd"
10)
11
12func main() {
13 // 1. Engångskomprimering / -dekomprimering (speglar 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. Strömmande Writer och Reader med stöd för ordbok
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}
Plattformar som stöds och inbäddade binärer
Alla sex målplattformar levereras med förbyggda, SHA-256-pinbade arbetarbinärer som är inbäddade via plattformsspecifika bygg-taggar (internal/embedded/embed_<goos>_<goarch>.go):
| Mål-OS | Målarkitektur (GOARCH) | Inbäddad binär | Binärstorlek | Byggverktygskedja & ursprung |
|---|---|---|---|---|
| Linux | amd64, arm64 | zstd_server_linux_<arch> | ~770 - 880 KiB | GCC -O3, SLSA Build L3 Provenance |
| macOS | arm64, amd64 | zstd_server_darwin_<arch> | ~660 - 795 KiB | AppleClang -O3, SLSA Build L3 Provenance |
| Windows | amd64 | zstd_server_windows_amd64.exe | ~818 KiB | MSVC -O2, SLSA Build L3 Provenance |
| Windows | arm64 | zstd_server_windows_arm64.exe | ~551 KiB | zig cc -target aarch64-windows-gnu -O3, SLSA L3 |
Inne i datavägen för delat minne
För att hålla genomströmningen konkurrenskraftig med processintern cgo undviker gosuda.org/zstd socket-serialisering och heap-allokeringar på den heta vägen.
1. Platskodning utan allokering (zstd_ctx.go och c/zstd_hooks.c)
När du använder en återanvändbar zstd.Ctx (eller när paketnivåfunktionerna Compress och Decompress lånar en Ctx från den interna sessionspoolen) flödar data direkt genom den mappade StandardLink-platsen:
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
- Den genererade
hqq-bindgen-klienten kopierarsrcdirekt till den minnesmappade förfrågningsplatsen och vänder påSeqLock-sekvensordet. - C-arbetaren läser
srcpå plats från förfrågningsplatsen och skickar svarsplatsens nyttoastspekare direkt som destinationsbuffert tillZSTD_compressCCtxellerZSTD_decompressDCtx. - Go-klienten kopierar den resulterande skivan från svarsplatsen till den skiva som tillhandahålls av anroparen (
dst). I stabilt tillstånd utförctx.CompressTo(dst, src, level)ochctx.DecompressInto(dst, src)0 allocs/op.
Föranalyserade ordböcker (CDict och DDict) lever på C-arbetaren bakom generationskontrollerade 64-bitars handtag (handle CDict, handle DDict i zstd.idl). Att skapa en ordbok en gång via ctx.NewCDict(rawDict, level) tolkar ordbokstabellerna inuti C-arbetaren och returnerar ett lättviktshandtag som kan återanvändas över tusentals anrop utan att ordboksbyten återsändas.
2. Amortering av IPC-omkostnader med massinramning (zstd_bulk.go)
För stora nyttoaster (16 KiB och uppåt) är rundturstiden på ~200 ns för delat minne försumbar jämfört med den CPU-tid som spenderas inuti libzstd. För arbetsbelastningar som komprimerar tusentals små meddelanden på 100 byte till 1 KiB på en gång, adderas dock även 200 ns per objekt upp.
gosuda.org/zstd tillhandahåller CompressBulk och DecompressBulk på BulkCtx (zstd_bulk.go) för att eliminera rundresor per objekt:
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 packar hela [][]byte-satsen i en enda längdprefixad förfrågningsram i delat minne. C-arbetaren loopar över varje objekt i C (compress_bulk i c/zstd_hooks.c), skriver alla komprimerade ramar sammanhängande till svarsplatsen och returnerar hela satsen i en enda rundresa med delat minne.
3. Överleva avslut av arbetare utan att krascha anroparen
Eftersom zstd_server körs i en separat OS-process leder ett ödesdigert fel inuti C-arbetaren aldrig till att din Go-applikation kraschar:
- Om
zstd_serverdödas externt (till exempel avSIGKILLeller en OOM-händelse) upptäcker HQQ-transportlagret den trasiga motparten omedelbart och returnerar ett typat*rt.TransportErrorfrån det pågåendeCompress- ellerDecompress-anropet. - Funktioner på paketnivå (
zstd.Compress,zstd.Decompress,zstd.NewWriter,zstd.NewReader) upptäcker automatiskt att den delade standardarbetaren är borta, startar en ny verifieradzstd_server-instans vid nästa anrop och återupptar normal drift.
Repositories layout
Kodbasen separerar DataDog/zstd-fasaden för direktbyte från de lågnivåbaserade hqq-bindgen-sessions- och massprimitiverna:
1gosuda.org/zstd
2├── autolaunch.go / hashes.go # Direkt utbytbart API för Compress/Decompress, automatisk återställningspool och SHA-256-hashar
3├── zstd_stream.go # Strömmande Writer/Reader (NewWriterLevelDict, NewReaderDict)
4├── zstd_ctx.go # Ctx utan allokering, CDict- och DDict-omslag över hqq-bindgen
5├── zstd_bulk.go # Inramning för CompressBulk och DecompressBulk med en enda rundresa
6├── zstd.go # Genererad hqq-bindgen Go-klient för zstd IDL-tjänsten
7├── zstd.idl # HQQ IDL-schema som definierar Context, CDict, DDict och RPC-metoder
8├── internal/embedded/ # Plattformstaggade //go:embed zstd_server-binärer (6 OS/arkitekturmål)
9└── c/ # C11-serverimplementering (zstd_server.c, zstd_hooks.c, main.c)
Komma igång
Installera gosuda.org/zstd med go get och använd det med CGO_ENABLED=0:
1go get gosuda.org/zstd@v0.3.10507
- GitHub-repository: https://github.com/gosuda/hqq-zstd
- Arkitektur & prestandatester: docs/ARCHITECTURE.md
- HQQ Core Runtime: https://gosuda.org/hqq