gosuda.org/zstd: Drop-In-yhteensopiva DataDog/zstd-pakkaus ilman CGO-riippuvuutta ja kaatumisten eristäminen

gosuda.org/zstd on CGO_ENABLED=0 Go-pakkauskirjasto, joka suorittaa virallisen C-pohjaisen Zstandard-moottorin (libzstd 1.5.7) eristetyssä työprosessissa HQQ-jaetun muistin kautta. Se tarjoaa pudotettavan korvaavan API:n github.com/DataDog/zstd-kirjastolle (mukaan lukien kertakäyttöiset Compress/Decompress, suoratoistavat io.Writer/io.Reader, esikäsitellyt sanakirjat sekä uudelleenkäytettävät nollaallokaatiokontekstit) ja pitää C-suorituksen kokonaan Go-ajonaikaisen osoiteavaruuden ulkopuolella.
1go get gosuda.org/zstd@v0.3.10507
Miksi purku kuuluu Go-keon ulkopuolelle
Tuotantotaustajärjestelmissä pakkaus ja purku sijaitsevat suoraan luottamattoman syötteen rajapinnalla. Lokinkerääjät, viestivälittäjät, RPC-yhdyskäytävät ja tallennusmoottorit purkavat gigatavuja ulkoista dataa kehyksinä joka minuutti.
Monet Go-tiimit valitsevat github.com/DataDog/zstd-kirjaston, koska se sitoutuu suoraan alkuperäiseen C-kirjastoon libzstd: C-toteutus tarjoaa korkean yksiytimisen läpimenon, tiiviit sanakirjapakkaussuhteet pienille tietueille sekä Go-keon ulkopuoliset C-areenat (ZSTD_CCtx ja ZSTD_DCtx), jotka eivät kasvata Go-roskienkeruun tarkastusaikoja.
libzstd:n liittäminen Go-palveluun cgo:n kautta tuo kuitenkin kaksi operatiivista ongelmaa:
- Rakentamisen ja käyttöönoton haasteet: Samoin kuin
go-sqlite3,DataDog/zstdvaatiiCGO_ENABLED=1-asetuksen ja kohdealustan C-kääntäjätoolchainin. Palveluiden ristikääntäminen tai staattisten binäärien rakentaminen kohteillelinux/arm64taiwindows/arm64edellyttää ulkoisia C-sysroot-ympäristöjä. - Jaettu osoiteavaruus luottamattomilla syötteillä: Vioittuneiden tai vihamielisten hyötykuormien purku samassa virtuaalisessa osoiteavaruudessa Go-palvelimesi kanssa tarkoittaa, että mikä tahansa virhe natiivissa C-koodissa tai mukautetussa sanakirjakäsittelyssä voi lopettaa koko Go-prosessin.
gosuda.org/zstd säilyttää tarkan alkuperäisen C-pohjaisen libzstd 1.5.7-moottorin ja tutun github.com/DataDog/zstd-API:n, mutta siirtää C-suorituksen vahvistettuun lapsiprosessiin (zstd_server), joka on yhteydessä Go-ohjelmaasi välimuistirivin kokoisen jaetun muistin kautta.
Pudotettava korvaava toteutus github.com/DataDog/zstd-kirjastolle
Siirtyminen github.com/DataDog/zstd-kirjastosta edellyttää vain tuontipolun vaihtamista muotoon "gosuda.org/zstd". Ulkoista binääriasennusta tai alustuskoodia ei tarvita: ensimmäisellä kutsulla gosuda.org/zstd purkaa upotetun SLSA-todistetun zstd_server-binäärin alustallesi, varmistaa sen SHA-256-tiivisteen gosuda.org/hqq/secureexec-paketin avulla ja avaa jaetun muistin istuntoaltaan.
1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "log"
8
9 "gosuda.org/zstd"
10)
11
12func main() {
13 // 1. Kertakäyttöinen Compress / Decompress (peilaa github.com/DataDog/zstd-kirjaston)
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. Suoratoistava Writer ja Reader sanakirjatuella
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}
Tuetut alustat ja upotetut binäärit
Kaikki kuusi kohdealustaa toimitetaan valmiiksi käännettyinä, SHA-256-kiinnitettyinä työbinäärinä, jotka upotetaan alustakohtaisilla rakennustunnisteilla (internal/embedded/embed_<goos>_<goarch>.go):
| Kohdekäyttöjärjestelmä | Kohdearkkitehtuuri (GOARCH) | Upotettu binääri | Binäärin koko | Rakennustoolchain ja alkuperä |
|---|---|---|---|---|
| Linux | amd64, arm64 | zstd_server_linux_<arch> | ~770 - 880 KiB | GCC -O3, SLSA Build L3 -alkuperä |
| macOS | arm64, amd64 | zstd_server_darwin_<arch> | ~660 - 795 KiB | AppleClang -O3, SLSA Build L3 -alkuperä |
| Windows | amd64 | zstd_server_windows_amd64.exe | ~818 KiB | MSVC -O2, SLSA Build L3 -alkuperä |
| Windows | arm64 | zstd_server_windows_arm64.exe | ~551 KiB | zig cc -target aarch64-windows-gnu -O3, SLSA L3 |
Jaetun muistin datapolun sisällä
Pitääkseen läpimenon kilpailukykyisenä sisäprosessin cgo:n kanssa gosuda.org/zstd välttää socket-sarjallistamisen ja keon allokaatiot kuumalla polulla.
1. Nollaallokaatioinen slot-koodaus (zstd_ctx.go ja c/zstd_hooks.c)
Kun käytät uudelleenkäytettävää zstd.Ctx-kontekstia (tai kun pakettitason Compress- ja Decompress-funktiot lainaavat Ctx-kontekstin sisäiseltä istuntoaltaalta), data kulkee suoraan kartoitetun StandardLink-slotin kautta:
1Go-kutsuja (zstd.Ctx) Jaetun muistin slot C11-työprosessi (zstd_hooks.c)
2--------------------- ------------------ -------------------------
3src []byte -------------------------> Pyyntöslotin hyötykuorma -----------> ZSTD_compressCCtx(
4 cctx, resp_slot, src, level)
5dst []byte <------------------------- Vastausslotin hyötykuorma <----------- Kirjoittaa pakatun kehyksen suoraan
6 jaettuun vastausslottiin
- Generoitu
hqq-bindgen-asiakas kopioisrc-datan suoraan muistikartoitettuun pyyntöslottiin ja kääntääSeqLock-sekvenssisanan. - C-työprosessi lukee
src-datan paikallaan pyyntöslotista ja välittää vastausslotin hyötykuorman osoittimen suoraan kohdepuskurina funktiolleZSTD_compressCCtxtaiZSTD_decompressDCtx. - Go-asiakas kopioi tuloksena syntyneen siivun vastausslotista kutsujan antamaan
dst-siivuun. Vakaassa tilassactx.CompressTo(dst, src, level)jactx.DecompressInto(dst, src)suorittavat0 allocs/op.
Esikäsitellyt sanakirjat (CDict ja DDict) sijaitsevat C-työprosessissa sukupolvitarkistettujen 64-bittisten kahvojen (handle CDict, handle DDict tiedostossa zstd.idl) takana. Sanakirjan luominen kerran kutsulla ctx.NewCDict(rawDict, level) jäsentää sanakirjataulut C-työprosessissa ja palauttaa kevyen kahvan, jota voidaan käyttää uudelleen tuhansissa kutsuissa ilman sanakirjatavujen uudelleenlähettämistä.
2. IPC-yleiskustannusten tasaaminen joukkokehystyksellä (zstd_bulk.go)
Suurille hyötykuormille (16 KiB ja suuremmat) noin 200 ns:n jaetun muistin kierto on merkityksetön verrattuna libzstd:n sisällä kuluvaan CPU-aikaan. Työkuormissa, jotka pakkaavat tuhansia pieniä 100 tavun – 1 KiB:n viestejä kerralla, jo 200 ns per kohde kasvaa kuitenkin merkittäväksi.
gosuda.org/zstd tarjoaa CompressBulk- ja DecompressBulk-funktiot BulkCtx-tyypille (zstd_bulk.go) kierrosten poistamiseksi kohdekohtaisesti:
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 pakkaa koko [][]byte-erän yhdeksi pituusetuliitteiseksi pyyntökehykseksi jaettuun muistiin. C-työprosessi käy jokaisen kohteen läpi C-koodissa (compress_bulk tiedostossa c/zstd_hooks.c), kirjoittaa kaikki pakatut kehykset peräkkäin vastausslottiin ja palauttaa koko erän yhdellä jaetun muistin kierrolla.
3. Työprosessin päättymisen selviäminen ilman kutsujan kaatumista
Koska zstd_server toimii erillisessä käyttöjärjestelmäprosessissa, C-työprosessin fataali virhe ei koskaan kaada Go-sovellustasi:
- Jos
zstd_servertapetaan ulkoisesti (esimerkiksiSIGKILL-signaalilla tai OOM-tapahtumalla), HQQ-siirtokerros havaitsee katkenneen vastapään välittömästi ja palauttaa tyypitetyn*rt.TransportError-virheen käynnissä olevastaCompress- taiDecompress-kutsusta. - Pakettitason funktiot (
zstd.Compress,zstd.Decompress,zstd.NewWriter,zstd.NewReader) havaitsevat automaattisesti, että jaettu oletustyöprosessi on poissa, käynnistävät seuraavalla kutsulla uuden vahvistetunzstd_server-instanssin ja palauttavat normaalin toiminnan.
Repositorion rakenne
Koodikanta erottaa pudotettavan DataDog/zstd-julkisivun matalan tason hqq-bindgen-istunto- ja joukkoprimitiiveistä:
1gosuda.org/zstd
2├── autolaunch.go / hashes.go # Pudotettava Compress/Decompress-API, automaattisen palautumisen allas ja SHA-256-tiivisteet
3├── zstd_stream.go # Suoratoistava Writer/Reader (NewWriterLevelDict, NewReaderDict)
4├── zstd_ctx.go # Nollaallokaatioinen Ctx, CDict ja DDict hqq-bindgen-kääreinä
5├── zstd_bulk.go # Yhden kierroksen CompressBulk- ja DecompressBulk-kehystys
6├── zstd.go # Generoitu hqq-bindgen Go -asiakas zstd IDL -palvelulle
7├── zstd.idl # HQQ IDL -skeema, joka määrittelee Context-, CDict-, DDict- ja RPC-metodit
8├── internal/embedded/ # Alustakohtaisesti merkityt //go:embed zstd_server -binäärit (6 OS/arkkitehtuuri-kohdetta)
9└── c/ # C11-palvelintoteutus (zstd_server.c, zstd_hooks.c, main.c)
Aloittaminen
Asenna gosuda.org/zstd komennolla go get ja käytä sitä asetuksella CGO_ENABLED=0:
1go get gosuda.org/zstd@v0.3.10507
- GitHub-repositorio: https://github.com/gosuda/hqq-zstd
- Arkkitehtuuri ja vertailutestit: docs/ARCHITECTURE.md
- HQQ-ydinajonaika: https://gosuda.org/hqq