gosuda.org/zstd: Drop-In náhrada za DataDog/zstd s kompresiou bez CGO a izoláciou pádov

gosuda.org/zstd je kompresná knižnica v Go s CGO_ENABLED=0, ktorá spúšťa oficiálny C engine Zstandard (libzstd 1.5.7) v izolovanom pracovnom procese cez zdieľanú pamäť HQQ. Poskytuje API ako náhradu za github.com/DataDog/zstd (vrátane jednorazových funkcií Compress/Decompress, streamovacích io.Writer/io.Reader, vopred spracovaných slovníkov a opakovane použiteľných kontextov bez alokácií), pričom vykonávanie C kódu zostáva úplne mimo adresného priestoru runtime jazyka Go.
1go get gosuda.org/zstd@v0.3.10507
Prečo má dekompresia patriť mimo vašej haldy v Go
V produkčných backendoch sa kompresia a dekompresia nachádzajú priamo na hranici nedôveryhodného vstupu. Zberače logov, message brokery, RPC brány a úložné enginy dekomprimujú každú minútu gigabajty externe dodaných rámcov.
Mnohé tímy používajúce Go volia github.com/DataDog/zstd, pretože sa priamo viaže na pôvodnú C knižnicu libzstd: C implementácia poskytuje vysokú priepustnosť na jednom jadre, kompaktné kompresné pomery pri malých záznamoch a mimo haldy umiestnené C arény (ZSTD_CCtx a ZSTD_DCtx), ktoré nezvyšujú čas prehľadávania pamäte garbage collectorom v Go.
Prepojenie libzstd so službou v Go cez cgo však prináša dva prevádzkové problémy:
- Trenie pri zostavovaní a nasadení: Rovnako ako
go-sqlite3, ajDataDog/zstdvyžadujeCGO_ENABLED=1a cieľový toolchain C kompilátora. Krížová kompilácia služieb alebo tvorba statických binárok prelinux/arm64čiwindows/arm64si vyžaduje externé C sysroot prostredia. - Zdieľaný adresný priestor pri nedôveryhodných vstupoch: Dekompresia poškodených alebo nepriateľských dát v rovnakom virtuálnom adresnom priestore ako váš Go server znamená, že akákoľvek chyba v natívnom C kóde alebo vo vlastnom spracovaní slovníkov môže ukončiť celý proces Go.
gosuda.org/zstd zachováva presný upstream C engine libzstd 1.5.7 a známe API github.com/DataDog/zstd, ale presúva vykonávanie C kódu do overeného podriadeného procesu (zstd_server), ktorý je s vaším programom v Go prepojený cez zdieľanú pamäť zarovnanú na riadky cache.
Náhrada za github.com/DataDog/zstd bez úprav kódu
Migrácia z github.com/DataDog/zstd vyžaduje zmenu iba importnej cesty na "gosuda.org/zstd". Nie je potrebná inštalácia externých binárok ani inicializačný kód: pri prvom volaní gosuda.org/zstd pre vašu platformu sprístupní vnorenú binárku zstd_server s SLSA atestáciou, overí jej SHA-256 digest pomocou gosuda.org/hqq/secureexec a otvorí fond relácií zdieľanej pamäte.
1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "log"
8
9 "gosuda.org/zstd"
10)
11
12func main() {
13 // 1. Jednorazové Compress / Decompress (zodpovedá 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. Streamovací Writer a Reader s podporou slovníkov
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}
Podporované platformy a vnorené binárky
Všetkých šesť cieľových platforiem obsahuje predkompilované pracovné binárky zabezpečené SHA-256 hodnotou, vnorené pomocou platformovo špecifických build tagov (internal/embedded/embed_<goos>_<goarch>.go):
| Cieľový OS | Cieľová architektúra (GOARCH) | Vnorená binárka | Veľkosť binárky | Build toolchain a pôvod |
|---|---|---|---|---|
| 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 |
Vnútri dátovej cesty cez zdieľanú pamäť
Aby gosuda.org/zstd udržal priepustnosť konkurencieschopnú voči cgo v rámci procesu, vyhýba sa serializácii cez sockety a alokáciám na haldе v kritickej ceste.
1. Kódovanie slotov bez alokácií (zstd_ctx.go a c/zstd_hooks.c)
Pri použití opakovane použiteľného zstd.Ctx (alebo keď funkcie na úrovni balíka Compress a Decompress získajú Ctx z interného fondu relácií) dáta prúdia priamo cez mapovaný slot StandardLink:
1Go volajúci (zstd.Ctx) Slot zdieľanej pamäte C11 worker (zstd_hooks.c)
2-------------------- --------------------- -------------------------
3src []byte -------------------------> Užitočné dáta požiadavky ----------> ZSTD_compressCCtx(
4 cctx, resp_slot, src, level)
5dst []byte <------------------------- Užitočné dáta odpovede <------------ Zapisuje komprimovaný rámec priamo
6 do zdieľaného slotu odpovede
- Generovaný klient
hqq-bindgenskopírujesrcpriamo do namapovaného slotu požiadavky a prepne sekvenčné slovoSeqLock. - C worker číta
srcna mieste zo slotu požiadavky a ukazovateľ na užitočné dáta slotu odpovede odovzdáva priamo ako cieľový buffer doZSTD_compressCCtxaleboZSTD_decompressDCtx. - Klient v Go skopíruje výsledný slice zo slotu odpovede do
dstslice poskytnutého volajúcim. V ustálenom stavectx.CompressTo(dst, src, level)actx.DecompressInto(dst, src)vykonávajú0 allocs/op.
Vopred spracované slovníky (CDict a DDict) sú uložené na C workeri za 64-bitovými handle s kontrolou generácie (handle CDict, handle DDict v zstd.idl). Jednorazové vytvorenie slovníka cez ctx.NewCDict(rawDict, level) spracuje tabuľky slovníka vnútri C workera a vráti ľahký handle, ktorý sa dá znovu použiť v tisícoch volaní bez opätovného odosielania bajtov slovníka.
2. Amortizácia réžie IPC pomocou hromadného rámcovania (zstd_bulk.go)
Pri veľkých užitočných dátach (16 KiB a viac) je round trip cez zdieľanú pamäť s hodnotou približne 200 ns zanedbateľný v porovnaní s časom CPU strávenom v libzstd. Pri pracovných záťažiach, ktoré komprimujú tisíce malých správ s veľkosťou 100 bajtov až 1 KiB naraz, však aj 200 ns na položku sa sčíta.
gosuda.org/zstd poskytuje CompressBulk a DecompressBulk na BulkCtx (zstd_bulk.go), aby odstránil round trip pre každú položku:
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 zabalí celú dávku [][]byte do jedného rámca požiadavky s prefixovanými dĺžkami v zdieľanej pamäti. C worker prechádza každú položku v jazyku C (compress_bulk v c/zstd_hooks.c), zapisuje všetky komprimované rámce súvisle do slotu odpovede a vracia celú dávku v jedinom round tripe cez zdieľanú pamäť.
3. Prežitie ukončenia workera bez pádu volajúceho
Keďže zstd_server beží v samostatnom procese operačného systému, fatálne zlyhanie vnútri C workera nikdy nezhodí vašu aplikáciu v Go:
- Ak je
zstd_serverukončený zvonka (napríklad cezSIGKILLalebo udalosť OOM), transportná vrstva HQQ okamžite zistí porušeného partnera a vráti typovanú chybu*rt.TransportErrorz práve prebiehajúceho volaniaCompressaleboDecompress. - Funkcie na úrovni balíka (
zstd.Compress,zstd.Decompress,zstd.NewWriter,zstd.NewReader) automaticky zistia, že zdieľaný predvolený worker zmizol, pri ďalšom volaní spustia novú overenú inštanciuzstd_servera obnovia normálnu prevádzku.
Rozloženie repozitára
Kódová základňa oddeľuje fasádu DataDog/zstd na úrovni náhrady od nízkoúrovňových primitív relácií a hromadných operácií hqq-bindgen:
1gosuda.org/zstd
2├── autolaunch.go / hashes.go # API Compress/Decompress na úrovni náhrady, fond s automatickým obnovením a SHA-256 hodnoty
3├── zstd_stream.go # Streamovací Writer/Reader (NewWriterLevelDict, NewReaderDict)
4├── zstd_ctx.go # Ctx bez alokácií a obaly CDict a DDict nad hqq-bindgen
5├── zstd_bulk.go # Rámcovanie CompressBulk a DecompressBulk s jedným round tripom
6├── zstd.go # Generovaný klient hqq-bindgen v Go pre službu zstd IDL
7├── zstd.idl # Schéma HQQ IDL definujúca Context, CDict, DDict a RPC metódy
8├── internal/embedded/ # Vnorené binárky zstd_server s //go:embed označené platformovými tagmi (6 cieľov OS/Arch)
9└── c/ # Implementácia servera v C11 (zstd_server.c, zstd_hooks.c, main.c)
Začíname
Nainštalujte gosuda.org/zstd pomocou go get a používajte ho s CGO_ENABLED=0:
1go get gosuda.org/zstd@v0.3.10507
- GitHub repozitár: https://github.com/gosuda/hqq-zstd
- Architektúra a benchmarky: docs/ARCHITECTURE.md
- HQQ Core Runtime: https://gosuda.org/hqq