gosuda.org/zstd: Drop-In DataDog/zstd kompresia s nulovým CGO a izoláciou zlyhaní
gosuda.org/zstd je kompresná knižnica pre jazyk Go s nastavením CGO_ENABLED=0, ktorá spúšťa oficiálny C engine Zstandard (libzstd 1.5.7) vo vnútri izolovaného procesu pracovníka prostredníctvom zdieľanej pamäte HQQ. Poskytuje priamo nahraditeľné API pre github.com/DataDog/zstd (vrátane jednorazových funkcií Compress/Decompress, streamovacieho io.Writer/io.Reader, predpripravených slovníkov a opakovane použiteľných kontextov s nulovou alokáciou), pričom udržiava vykonávanie v jazyku C úplne mimo adresného priestoru runtime prostredia jazyka Go.
1go get gosuda.org/zstd@v0.3.10507
Prečo dekompresia patrí mimo váš Go heap
V produkčných backendoch sa kompresia a dekompresia nachádzajú priamo na hranici nedôveryhodných vstupov. Zberače logov, správcovia správ, brány RPC a úložné systémy dekomprimujú gigabajty externe dodaných rámcov každú minútu.
Mnoho tímov pracujúcich v jazyku Go si vyberá github.com/DataDog/zstd, pretože sa priamo viaže na upstream C knižnicu libzstd: implementácia v jazyku C poskytuje vysokú priepustnosť na jedno jadro, tesné kompresné pomery slovníkov pri malých záznamoch a C arény mimo heapu (ZSTD_CCtx a ZSTD_DCtx), ktoré nezvyšujú časy skenovania garbage collection v jazyku Go.
Prepojenie libzstd so službou v jazyku Go prostredníctvom cgo však prináša dva prevádzkové problémy:
- Komplexnosť zostavovania a nasadzovania: Rovnako ako
go-sqlite3, ajDataDog/zstdvyžadujeCGO_ENABLED=1a cieľový reťazec nástrojov kompilátora C. Krížová kompilácia služieb alebo zostavovanie statických binárnych súborov prelinux/arm64alebowindows/arm64vyžaduje externé C sysrooty. - Zdieľaný adresný priestor pri nedôveryhodných vstupoch: Dekompresia poškodených alebo zámerne škodlivých dát v rovnakom virtuálnom adresnom priestore ako váš Go server znamená, že akákoľvek chyba v natívnom kóde C alebo pri vlastnej správe slovníkov môže ukončiť celý proces jazyka 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 kódu C do overeného podprocesu (zstd_server), ktorý je pripojený k vášmu programu v jazyku Go prostredníctvom zdieľanej pamäte na úrovni vyrovnávacej pamäte cache.
Náhrada typu „drop-in“ pre github.com/DataDog/zstd
Migrácia z github.com/DataDog/zstd vyžaduje zmenu iba cesty importu na "gosuda.org/zstd". Nie je potrebná žiadna inštalácia externých binárnych súborov ani nastavovací kód: pri prvom volaní gosuda.org/zstd vytvorí vnorený binárny súbor zstd_server overený pomocou SLSA pre vašu platformu, overí jeho súčet SHA-256 pomocou gosuda.org/hqq/secureexec a otvorí bazén relácií so zdieľanou pamäťou.
1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "log"
8
9 "gosuda.org/zstd"
10)
11
12func main() {
13 // 1. Jednorazová kompresia / dekompresia (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í zapisovač a čítačka s podporou slovníka
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árne súbory
Všetkých šesť cieľových platforiem sa dodáva s predpripravenými binárnymi súbormi pracovníkov so zabezpečeným hashovaním SHA-256, ktoré sú vnorené prostredníctvom značiek zostavenia špecifických pre danú platformu (internal/embedded/embed_<goos>_<goarch>.go):
| Cieľový OS | Cieľová architektúra (GOARCH) | Vnorený binárny súbor | Veľkosť binárneho súboru | Reťazec nástrojov na zostavenie 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 |
Architektúra dátovej cesty v zdieľanej pamäti
Aby sa zachovala konkurencieschopná priepustnosť v porovnaní s in-process cgo, gosuda.org/zstd sa vyhýba serializácii soketov a alokáciám haldy na kritickej ceste.
1. Kódovanie slotov s nulovou alokáciou (zstd_ctx.go a c/zstd_hooks.c)
Keď používate opakovane použiteľný objekt zstd.Ctx (alebo keď funkcie balíka Compress a Decompress si vypožičiavajú kontext Ctx z vnútorného bazéna relácií), dáta prúdia priamo cez namapovaný slot StandardLink:
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
- Vygenerovaný klient
hqq-bindgenskopírujesrcpriamo do pamäťovo namapovaného slotu požiadavky a prepne sekvenčné slovoSeqLock. - Pracovník v jazyku C prečíta
srcpriamo zo slotu požiadavky a odovzdá ukazovateľ na užitočné zaťaženie slotu odpovede priamo ako cieľovú vyrovnávaciu pamäť do funkcieZSTD_compressCCtxaleboZSTD_decompressDCtx. - Klient v jazyku Go skopíruje výsledný slice zo slotu odpovede do slice
dstposkytnutého volajúcim. V ustálenom stave vykonávajúctx.CompressTo(dst, src, level)actx.DecompressInto(dst, src)operácie s0 allocs/op.
Predpripravené slovníky (CDict a DDict) sa nachádzajú na strane pracovníka v jazyku C za 64-bitovými rukoväťami kontrolovanými generáciou (handle CDict, handle DDict v zstd.idl). Vytvorenie slovníka raz pomocou ctx.NewCDict(rawDict, level) analyzuje tabuľky slovníka vo vnútri pracovníka v jazyku C a vráti ľahkú rukoväť, ktorú možno opakovane použiť pri tisícoch volaní bez nutnosti opätovného odosielania bajtov slovníka.
2. Amortizácia režijnej réžie IPC pomocou dávkového rámcovania (zstd_bulk.go)
Pre veľké objemy dát (16 KiB a viac) je spiatočná cesta v zdieľanej pamäti trvajúca približne 200 ns zanedbateľná v porovnaní s časom CPU stráveným vnútri libzstd. V prípade pracovných záťaží, ktoré komprimujú tisíce malých 100-bajtových až 1-KiB správ naraz, sa však poplatok čo i len 200 ns za položku nahromadí.
gosuda.org/zstd poskytuje funkcie CompressBulk a DecompressBulk na objekte BulkCtx (zstd_bulk.go) na elimináciu spiatočných ciest 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)
Funkcia CompressBulk zbalí celú dávku typu [][]byte do jedného rámca požiadavky s predponou dĺžky v zdieľanej pamäti. Pracovník v jazyku C prejde v cykle každú položku v jazyku C (compress_bulk v c/zstd_hooks.c), zapíše všetky komprimované rámce bez medzier za sebou do slotu odpovede a vráti celú dávku v jedinej spiatočnej ceste v zdieľanej pamäti.
3. Prežitie ukončenia pracovníka bez zlyhania volajúceho
Pretože zstd_server beží v samostatnom procese operačného systému, fatálna zlyhanie vo vnútri pracovníka v jazyku C nikdy nepoloží vašu aplikáciu v jazyku Go:
- Ak je proces
zstd_serverexterne ukončený (napríklad príkazomSIGKILLalebo udalosťou OOM), vrstva prenosu HQQ okamžite detekuje prerušené partnerstvo a vráti typovanú chybu*rt.TransportErrorz prebiehajúceho volaniaCompressaleboDecompress. - Funkcie na úrovni balíka (
zstd.Compress,zstd.Decompress,zstd.NewWriter,zstd.NewReader) automaticky zistia, že zdieľaný predvolený pracovník zmizol, pri nasledujúcom volaní spustia novú overenú inštanciuzstd_servera obnovia normálnu prevádzku.
Štruktúra úložiska
Kódová základňa oddeľuje rozhranie DataDog/zstd od nízkoúrovňových relácií a hromadných primitív hqq-bindgen:
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)
Začíname
Nainštalujte si gosuda.org/zstd pomocou príkazu go get a použite ho s nastavením CGO_ENABLED=0:
1go get gosuda.org/zstd@v0.3.10507
- Úložisko GitHub: https://github.com/gosuda/hqq-zstd
- Architektúra a porovnávacie testy: docs/ARCHITECTURE.md
- HQQ Core Runtime: https://gosuda.org/hqq