gosuda.org/zstd: Kompresi Drop-In DataDog/zstd tanpa CGO dan dengan Isolasi Crash

gosuda.org/zstd adalah pustaka kompresi Go dengan CGO_ENABLED=0 yang menjalankan mesin Zstandard C resmi (libzstd 1.5.7) di dalam proses pekerja yang terisolasi melalui memori bersama HQQ. Pustaka ini menyediakan API pengganti langsung (drop-in) untuk github.com/DataDog/zstd (termasuk Compress/Decompress sekali panggil, io.Writer/io.Reader streaming, kamus yang telah diproses sebelumnya, dan konteks tanpa alokasi yang dapat digunakan ulang), sambil menjaga agar eksekusi C sepenuhnya berada di luar ruang alamat runtime Go.
1go get gosuda.org/zstd@v0.3.10507
Mengapa Dekompresi Harus Berada di Luar Heap Go Anda
Dalam backend produksi, kompresi dan dekompresi berada langsung pada batas input yang tidak tepercaya. Pengumpul log, broker pesan, gateway RPC, dan mesin penyimpanan mendekompresi gigabyte frame yang berasal dari luar setiap menit.
Banyak tim Go memilih github.com/DataDog/zstd karena pustaka ini terikat langsung ke libzstd C hulu: implementasi C memberikan throughput single-core yang tinggi, rasio kompresi kamus yang ketat pada rekaman kecil, serta arena C di luar heap (ZSTD_CCtx dan ZSTD_DCtx) yang tidak memperbesar waktu pemindaian garbage collection Go.
Namun, menautkan libzstd ke layanan Go melalui cgo menimbulkan dua kendala operasional:
- Gesekan Build dan Deployment: Seperti
go-sqlite3,DataDog/zstdmemerlukanCGO_ENABLED=1dan toolchain kompiler C target. Kompilasi silang layanan atau pembuatan biner statis untuklinux/arm64atauwindows/arm64memerlukan sysroot C eksternal. - Ruang Alamat Bersama pada Input yang Tidak Tepercaya: Mendekompresi payload yang rusak atau bersifat adversarial di dalam ruang alamat virtual yang sama dengan server Go Anda berarti setiap bug dalam kode C native atau penanganan kamus kustom dapat menghentikan seluruh proses Go.
gosuda.org/zstd mempertahankan mesin libzstd 1.5.7 C hulu yang persis sama serta API github.com/DataDog/zstd yang familiar, tetapi memindahkan eksekusi C ke dalam proses anak terverifikasi (zstd_server) yang terhubung ke program Go Anda melalui memori bersama yang disejajarkan dengan cache line.
Pengganti Langsung untuk github.com/DataDog/zstd
Bermigrasi dari github.com/DataDog/zstd hanya memerlukan perubahan jalur impor menjadi "gosuda.org/zstd". Tidak diperlukan instalasi biner eksternal ataupun kode pengaturan: pada panggilan pertama, gosuda.org/zstd mengeluarkan biner zstd_server yang tertanam dan telah diattestasi SLSA untuk platform Anda, memverifikasi digest SHA-256-nya melalui gosuda.org/hqq/secureexec, dan membuka kumpulan sesi memori bersama.
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}
Platform yang Didukung dan Biner Tertanam
Keenam platform target dikirim dengan biner worker prebuilt yang dipin SHA-256 dan tertanam melalui build tag khusus platform (internal/embedded/embed_<goos>_<goarch>.go):
| Target OS | Target Arsitektur (GOARCH) | Biner Tertanam | Ukuran Biner | Toolchain Build & Provenance |
|---|---|---|---|---|
| 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 |
Di Dalam Jalur Data Memori Bersama
Untuk menjaga throughput tetap kompetitif dengan cgo dalam proses, gosuda.org/zstd menghindari serialisasi soket dan alokasi heap pada jalur panas (hot path).
1. Penyandian Slot Tanpa Alokasi (zstd_ctx.go dan c/zstd_hooks.c)
Ketika Anda menggunakan zstd.Ctx yang dapat digunakan ulang (atau ketika fungsi Compress dan Decompress tingkat paket meminjam Ctx dari kumpulan sesi internal), data mengalir langsung melalui slot StandardLink yang dipetakan:
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
- Klien
hqq-bindgenyang dihasilkan menyalinsrclangsung ke slot permintaan yang dipetakan ke memori dan membalik kata urutanSeqLock. - Worker C membaca
srcdi tempat dari slot permintaan dan meneruskan pointer payload slot respons secara langsung sebagai buffer tujuan keZSTD_compressCCtxatauZSTD_decompressDCtx. - Klien Go menyalin slice hasil dari slot respons ke slice
dstyang disediakan pemanggil. Dalam kondisi steady state,ctx.CompressTo(dst, src, level)danctx.DecompressInto(dst, src)menghasilkan0 allocs/op.
Kamus yang telah diproses sebelumnya (CDict dan DDict) berada di worker C di balik handle 64-bit yang diperiksa generasinya (handle CDict, handle DDict dalam zstd.idl). Membuat kamus sekali melalui ctx.NewCDict(rawDict, level) akan mengurai tabel kamus di dalam worker C dan mengembalikan handle ringan yang dapat digunakan ulang di ribuan panggilan tanpa mengirim ulang byte kamus.
2. Mengamortisasi Overhead IPC dengan Framing Massal (zstd_bulk.go)
Untuk payload besar (16 KiB ke atas), round trip memori bersama sekitar 200 ns dapat diabaikan dibandingkan waktu CPU yang dihabiskan di dalam libzstd. Namun, untuk beban kerja yang mengompresi ribuan pesan kecil berukuran 100 byte hingga 1 KiB sekaligus, membayar 200 ns per item akan menumpuk.
gosuda.org/zstd menyediakan CompressBulk dan DecompressBulk pada BulkCtx (zstd_bulk.go) untuk menghilangkan round trip per item:
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 mengemas seluruh batch [][]byte ke dalam satu frame permintaan berprefiks panjang di memori bersama. Worker C mengulang setiap item di C (compress_bulk dalam c/zstd_hooks.c), menulis semua frame terkompresi secara berurutan ke slot respons, dan mengembalikan seluruh batch dalam satu round trip memori bersama.
3. Bertahan dari Terminasi Worker Tanpa Membuat Pemanggil Crash
Karena zstd_server berjalan dalam proses OS yang terpisah, kegagalan fatal di dalam worker C tidak pernah menjatuhkan aplikasi Go Anda:
- Jika
zstd_serverdihentikan dari luar (misalnya, olehSIGKILLatau peristiwa OOM), lapisan transport HQQ segera mendeteksi peer yang rusak dan mengembalikan*rt.TransportErrorbertipe dari panggilanCompressatauDecompressyang sedang berjalan. - Fungsi tingkat paket (
zstd.Compress,zstd.Decompress,zstd.NewWriter,zstd.NewReader) secara otomatis mendeteksi bahwa worker bawaan bersama telah hilang, memunculkan instanszstd_serverterverifikasi yang baru pada panggilan berikutnya, dan melanjutkan operasi normal.
Tata Letak Repositori
Basis kode memisahkan fasad DataDog/zstd pengganti langsung dari primitif sesi dan bulk hqq-bindgen tingkat rendah:
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)
Memulai
Pasang gosuda.org/zstd dengan go get dan gunakan dengan CGO_ENABLED=0:
1go get gosuda.org/zstd@v0.3.10507
- Repositori GitHub: https://github.com/gosuda/hqq-zstd
- Arsitektur & Benchmark: docs/ARCHITECTURE.md
- HQQ Core Runtime: https://gosuda.org/hqq