GoSuda

gosuda.org/zstd: Drop-In DataDog/zstd Compression med Zero CGO og Crash Isolation

By Lemon Mint
views ...

gosuda.org/zstd er et CGO_ENABLED=0 Go-komprimeringsbibliotek som kjører den offisielle C Zstandard-motoren (libzstd 1.5.7) inne i en isolert arbeiderprosess over delt HQQ-minne. Det tilbyter et erstatningsklart API for github.com/DataDog/zstd (inkludert engangs Compress/Decompress, strømmende io.Writer/io.Reader, forhåndsfordøyde ordbøker og gjenbrukbare kontekster uten allokering) samtidig som C-utførelsen holdes fullstendig utenfor Go-kjøretidens adresserom.

1go get gosuda.org/zstd@v0.3.10507

Hvorfor dekompresjon hører hjemme utenfor din Go-heap

I produksjonsbackender befinner kompresjon og dekompresjon seg direkte på grensen for ikke-validerte inndata. Loggsamlere, meldingsformidlere, RPC-gatewayer og lagringsmotorer dekomprimerer gigabyte med eksternt leverte rammer hvert minutt.

Mange Go-team velger github.com/DataDog/zstd fordi det kobler seg direkte til den oppstrøms C-biblioteket libzstd: C-implementeringen leverer høy gjennomvoeringsevne på enkeltkjerner, tette ordbokkompresjonsforhold på små poster, og C-arenaer utenfor heapen (ZSTD_CCtx og ZSTD_DCtx) som ikke blåser opp søketidene for Go-søppelinnsamling.

Å lenke libzstd inn i en Go-tjeneste via cgo medfører imidlertid to operasjonelle utfordringer:

  1. Bygge- og distribusjonsfriksjon: Akkurat som go-sqlite3, krever DataDog/zstd CGO_ENABLED=1 og en målstyrt C-kompilatorverktøykjede. Krysskompilering av tjenester eller bygging av statiske binærfiler for linux/arm64 eller windows/arm64 krever eksterne C-systemrøtter.
  2. Delt adresserom på ikke-validerte inndata: Dekompresjon av korrupte eller fiendtlige nyttelaster inne i det samme virtuelle adresserommet som din Go-server betyr at en hvilken som helst feil i innfødt C-kode eller tilpasset ordbokshåndtering kan avslutte hele Go-prosessen.

gosuda.org/zstd beholder den nøyaktige oppstrøms C libzstd 1.5.7-motoren og det kjente github.com/DataDog/zstd-API-et, men flytter C-utførelsen til en verifisert underprosess (zstd_server) som er koblet til ditt Go-program over hurtigbufringslinje for delt minne.

Erstatningsklar for github.com/DataDog/zstd

Migrering fra github.com/DataDog/zstd krever endring av bare importstien til "gosuda.org/zstd". Ingen ekstern binærinstallasjon eller oppsettskode er nødvendig: ved det første kallet materialiserer gosuda.org/zstd en innebygd SLSA-attestert zstd_server-binærfil for din plattform, verifiserer dens SHA-256-sammendrag via gosuda.org/hqq/secureexec, og åpner en øktpool for delt minne.

 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}

Støttede plattformer og innebygde binærfiler

Alle seks målplattformene leveres med forhåndsbygde, SHA-256-pinnete arbeiderbinærfiler som er innebygd via plattformspesifikke byggetagger (internal/embedded/embed_<goos>_<goarch>.go):

Mål-OSMålarkitektur (GOARCH)Innebygd binærfilBinærstørrelseByggeverktøykjede og opprinnelse
Linuxamd64, arm64zstd_server_linux_<arch>~770 - 880 KiBGCC -O3, SLSA Build L3 Provenance
macOSarm64, amd64zstd_server_darwin_<arch>~660 - 795 KiBAppleClang -O3, SLSA Build L3 Provenance
Windowsamd64zstd_server_windows_amd64.exe~818 KiBMSVC -O2, SLSA Build L3 Provenance
Windowsarm64zstd_server_windows_arm64.exe~551 KiBzig cc -target aarch64-windows-gnu -O3, SLSA L3

Inne i databanen for delt minne

For å holde gjennomstrømningen konkurransedyktig med cgo i prosessen, unngår gosuda.org/zstd sokkelserialisering og heap-allokeringer på den kritiske stien.

1. Sprog-koding uten allokering (zstd_ctx.go og c/zstd_hooks.c)

Når du bruker en gjenbrukbar zstd.Ctx (eller når pakkens Compress- og Decompress-funksjoner låner en Ctx fra den interne øktpoolen), flyter data direkte gjennom den tilordnede StandardLink-plassen:

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
  1. Den genererte hqq-bindgen-klienten kopierer src rett inn i den minnetilordnede forespørselsplassen og snur SeqLock-sekvensordet.
  2. C-arbeideren leser src på plass fra forespørselsplassen og sender responsplassens nyttelastpeker direkte som målbuffer til ZSTD_compressCCtx eller ZSTD_decompressDCtx.
  3. Go-klienten kopierer den resulterende skiven fra responsplassen inn i den dst-skiven som ble levert av anropskuden. I stabil tilstand utfører ctx.CompressTo(dst, src, level) og ctx.DecompressInto(dst, src) 0 allocs/op.

Forhåndsfordøyde ordbøker (CDict og DDict) ligger på C-arbeideren bak generasjonssjekkede 64-bits håndtak (handle CDict, handle DDict i zstd.idl). Å opprette en ordbok én gang via ctx.NewCDict(rawDict, level) parser ordboktabellene inne i C-arbeideren og returnerer et lettvekts håndtak som kan gjenbrukes på tvers av tusenvis av kall uten å sende ordbokbyte på nytt.

2. Amortisering av IPC-overhead med masseinnramming (zstd_bulk.go)

For store nyttelaster (16 KiB og oppover) er rundturstiden for delt minne på ~200 ns ubetydelig sammenlignet med CPU-tiden som brukes inne i libzstd. For arbeidsmengder som komprimerer tusenvis av små 100-bytes til 1-KiB meldinger samtidig, hoper det seg imidlertid opp selv ved å betale 200 ns per element.

gosuda.org/zstd tilbyr CompressBulk og DecompressBulk på BulkCtx (zstd_bulk.go) for å eliminere tur-retur-tider per element:

 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 pakker hele [][]byte-pakken inn i en enkelt lengdeprefikset forespørselsramme i delt minne. C-arbeideren løkker over hvert element i C (compress_bulk i c/zstd_hooks.c), skriver alle komprimerte rammer sammenhengende inn i responsplassen, og returnerer hele batchen i én enkelt tur-retur med delt minne.

3. Overlevelse av arbeiderterminering uten at anroperen krasjer

Fordi zstd_server kjører i en separat OS-prosessen, vil en fatal feil inne i C-arbeideren aldri ta ned Go-applikasjonen din:

  • Hvis zstd_server blir drept eksternt (for eksempel av SIGKILL eller en OOM-hendelse), oppdager HQQ-transportlaget den ødelagte likemannen umiddelbart og returnerer en typet *rt.TransportError fra det pågående Compress- eller Decompress-kallet.
  • Pakkenivåfunksjoner (zstd.Compress, zstd.Decompress, zstd.NewWriter, zstd.NewReader) oppdager automatisk at den delte standardarbeideren er borte, starter en ny verifisert zstd_server-instans ved neste kall, og gjenopptar normal drift.

Repositoriestruktur

Kodebasen skiljer den erstatningsklare DataDog/zstd-fasaden fra de lavnivå hqq-bindgen-økts- og massemasivene:

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)

Komme i gang

Installer gosuda.org/zstd med go get og bruk den med CGO_ENABLED=0:

1go get gosuda.org/zstd@v0.3.10507