gosuda.org/zstd: Drop-In съвместимост с DataDog/zstd, компресия със Zero CGO и изолация на сривовете

gosuda.org/zstd е Go библиотека за компресия с CGO_ENABLED=0, която изпълнява официалния C енджин на Zstandard (libzstd 1.5.7) в изолиран работен процес чрез споделена памет HQQ. Тя предоставя API за директна замяна на github.com/DataDog/zstd (включително еднократни Compress/Decompress, поточни io.Writer/io.Reader, предварително обработени речници и повторно използваеми контексти с нулева алокация), като същевременно изпълнението на C кода остава напълно извън адресното пространство на Go рънтайма.
1go get gosuda.org/zstd@v0.3.10507
Защо декомпресията трябва да бъде извън Go heap паметта
В продуктивни бекенд системи компресията и декомпресията се намират directly на границата с недоверени входни данни. Колектори на логове, брокери на съобщения, RPC шлюзове и системи за съхранение декомпресират гигабайти външно подадени фреймове всяка минута.
Много Go екипи избират github.com/DataDog/zstd, защото се свързва директно с upstream C libzstd: C имплементацията осигурява висока производителност на едно ядро, добри коефициенти на компресия с речници за малки записи и off-heap C арени (ZSTD_CCtx и ZSTD_DCtx), които не увеличават времето за сканиране от garbage collector-а на Go.
Свързването на libzstd с Go услуга чрез cgo обаче носи два оперативни проблема:
- Трудности при изграждане и внедряване: Подобно на
go-sqlite3,DataDog/zstdизискваCGO_ENABLED=1и целеви C компилаторен toolchain. Кроскомпилирането на услуги или изграждането на статични бинарни файлове заlinux/arm64илиwindows/arm64изисква външни C sysroot среди. - Споделено адресно пространство при недоверени входни данни: Декомпресирането на повредени или злонамерени полезни товари в същото виртуално адресно пространство като вашия Go сървър означава, че всеки бъг в native C кода или в персонализираното обработване на речници може да прекрати целия Go процес.
gosuda.org/zstd запазва точния upstream C енджин libzstd 1.5.7 и познатия API на github.com/DataDog/zstd, но премества изпълнението на C кода в проверен дъщерен процес (zstd_server), свързан с вашата Go програма чрез споделена памет, подравнена по cache line.
Директна замяна на github.com/DataDog/zstd
Миграцията от github.com/DataDog/zstd изисква промяна само на импорт пътя към "gosuda.org/zstd". Не е необходима инсталация на външен бинарен файл или код за настройка: при първото извикване gosuda.org/zstd материализира вграден zstd_server бинарен файл с SLSA атестация за вашата платформа, проверява SHA-256 дайджеста му чрез gosuda.org/hqq/secureexec и отваря пул от сесии в споделена памет.
1package main
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "log"
8
9 "gosuda.org/zstd"
10)
11
12func main() {
13 // 1. Еднократни Compress / Decompress (повтаря 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. Поточни Writer и Reader с поддръжка на речници
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}
Поддържани платформи и вградени бинарни файлове
Всички шест целеви платформи се доставят с предварително компилирани работни бинарни файлове, фиксирани чрез SHA-256 и вградени чрез платформено-специфични build tags (internal/embedded/embed_<goos>_<goarch>.go):
| Целева ОС | Целева архитектура (GOARCH) | Вграден бинарен файл | Размер на файла | Build toolchain и произход |
|---|---|---|---|---|
| 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 |
Вътрешна структура на пътя за данни в споделена памет
За да остане пропускателната способност конкурентна спрямо in-process cgo, gosuda.org/zstd избягва сериализация през сокети и heap алокации в горещия път.
1. Кодиране в слот без алокация (zstd_ctx.go и c/zstd_hooks.c)
Когато използвате повторно използваем zstd.Ctx (или когато функциите на пакетно ниво Compress и Decompress заемат Ctx от вътрешния пул от сесии), данните текат директно през картографирания 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
- Генерираният
hqq-bindgenклиент копираsrcdirectly в картографирания request слот и превключва sequence word-а наSeqLock. - C работникът чете
srcна място от request слота и подава указателя към полезния товар на response слота directly като целеви буфер наZSTD_compressCCtxилиZSTD_decompressDCtx. - Go клиентът копира получения slice от response слота в подадения от извикващия slice
dst. В стационарно състояниеctx.CompressTo(dst, src, level)иctx.DecompressInto(dst, src)извършват0 allocs/op.
Предварително обработените речници (CDict и DDict) живеят в C работника зад генерационно проверявани 64-битови handles (handle CDict, handle DDict в zstd.idl). Създаването на речник веднъж чрез ctx.NewCDict(rawDict, level) парсва таблиците на речника в C работника и връща лек handle, който може да се използва повторно при хиляди извиквания, без повторно изпращане на байтовете на речника.
2. Амортизиране на IPC разходите чрез bulk framing (zstd_bulk.go)
При големи полезни товари (16 KiB и повече) ~200 ns цикъл през споделената памет е пренебрежим спрямо процесорното време, прекарано в libzstd. При работни натоварвания, които компресират хиляди малки съобщения от 100 байта до 1 KiB наведнъж, обаче заплащането на дори 200 ns за всеки елемент се натрупва.
gosuda.org/zstd предоставя CompressBulk и DecompressBulk върху BulkCtx (zstd_bulk.go), за да елиминира обмените по елемент:
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 опакова целия пакет [][]byte в една заявка с префикс за дължина в споделената памет. C работникът обхожда всеки елемент на C ниво (compress_bulk в c/zstd_hooks.c), записва всички компресирани фреймове последователно в response слота и връща целия пакет при един обмен през споделената памет.
3. Оцеляване при прекратяване на работника без срив на извикващия
Тъй като zstd_server работи в отделен OS процес, фатален провал в C работника никога не сваля вашето Go приложение:
- Ако
zstd_serverбъде убит externally (например чрезSIGKILLили OOM събитие), транспортният слой на HQQ незабавно открива нарушения peer и връща типизиран*rt.TransportErrorот текущото извикване наCompressилиDecompress. - Функциите на пакетно ниво (
zstd.Compress,zstd.Decompress,zstd.NewWriter,zstd.NewReader) автоматично откриват, че споделеният по подразбиране работник е изчезнал, стартират нова проверена инстанция наzstd_serverпри следващото извикване и възобновяват нормалната работа.
Структура на хранилището
Кодовата база отделя facade-а DataDog/zstd за директна замяна от низкоуровневите примитиви за сесии и bulk операции на hqq-bindgen:
1gosuda.org/zstd
2├── autolaunch.go / hashes.go # API за Compress/Decompress за директна замяна, пул с автоматично възстановяване и SHA-256 хешове
3├── zstd_stream.go # Поточни Writer/Reader (NewWriterLevelDict, NewReaderDict)
4├── zstd_ctx.go # Ctx без алокация, CDict и DDict обвивки върху hqq-bindgen
5├── zstd_bulk.go # Framing за CompressBulk и DecompressBulk с един обмен
6├── zstd.go # Генериран hqq-bindgen Go клиент за IDL услугата zstd
7├── zstd.idl # HQQ IDL схема, дефинираща Context, CDict, DDict и RPC методи
8├── internal/embedded/ # Платформено-тагнати //go:embed бинарни файлове zstd_server (6 OS/Arch цели)
9└── c/ # Имплементация на C11 сървъра (zstd_server.c, zstd_hooks.c, main.c)
Начало на работа
Инсталирайте gosuda.org/zstd с go get и го използвайте с CGO_ENABLED=0:
1go get gosuda.org/zstd@v0.3.10507
- GitHub Repository: https://github.com/gosuda/hqq-zstd
- Architecture & Benchmarks: docs/ARCHITECTURE.md
- HQQ Core Runtime: https://gosuda.org/hqq