GoSuda

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

By Lemon Mint views ...

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:

  1. Gesekan Build dan Deployment: Seperti go-sqlite3, DataDog/zstd memerlukan CGO_ENABLED=1 dan toolchain kompiler C target. Kompilasi silang layanan atau pembuatan biner statis untuk linux/arm64 atau windows/arm64 memerlukan sysroot C eksternal.
  2. 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 OSTarget Arsitektur (GOARCH)Biner TertanamUkuran BinerToolchain Build & Provenance
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

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
  1. Klien hqq-bindgen yang dihasilkan menyalin src langsung ke slot permintaan yang dipetakan ke memori dan membalik kata urutan SeqLock.
  2. Worker C membaca src di tempat dari slot permintaan dan meneruskan pointer payload slot respons secara langsung sebagai buffer tujuan ke ZSTD_compressCCtx atau ZSTD_decompressDCtx.
  3. Klien Go menyalin slice hasil dari slot respons ke slice dst yang disediakan pemanggil. Dalam kondisi steady state, ctx.CompressTo(dst, src, level) dan ctx.DecompressInto(dst, src) menghasilkan 0 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_server dihentikan dari luar (misalnya, oleh SIGKILL atau peristiwa OOM), lapisan transport HQQ segera mendeteksi peer yang rusak dan mengembalikan *rt.TransportError bertipe dari panggilan Compress atau Decompress yang sedang berjalan.
  • Fungsi tingkat paket (zstd.Compress, zstd.Decompress, zstd.NewWriter, zstd.NewReader) secara otomatis mendeteksi bahwa worker bawaan bersama telah hilang, memunculkan instans zstd_server terverifikasi 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