nnq-zstd — Zstd compression transport plugin for NNQ
Registers zstd+tcp:// as an NNQ transport: TCP underneath, with
transparent per-message Zstd compression, sender-side dictionary
training, and in-band dictionary shipping. No handshake, no
negotiation — both peers must use zstd+tcp://.
See RFC.md for the normative wire protocol.
Quick start
require "nnq"
require "nnq/zstd" # registers the transport
push = NNQ::PUSH0.new
push.connect("zstd+tcp://127.0.0.1:5555", level: -3) # -3 fast, 3 balanced
push.send("payload") # compressed on the wire
pull = NNQ::PULL0.new
pull.bind("zstd+tcp://*:5555") # receiver: no level, no dict config
pull.receive # => "payload"
Any tcp:// URL in an NNQ/nnq-cli API works with the zstd+ prefix —
nnq CLI users get this for free via -z / -Z / --compress=LEVEL,
which rewrite tcp:// to zstd+tcp://.
How it works
require "nnq/zstd"installsNNQ::Transport::ZstdTcpunder thezstd+tcpscheme inNNQ::Engine.transports.bind/connectdial plain TCP; after the SP handshake,ConnectionLifecycle#ready!callsZstdTcp.wrap_connection(conn, engine), which decorates the connection with aZstdConnection(SimpleDelegator) that runs every outbound body through the codec and decompresses every inbound wire message.- One
NNQ::Zstd::Codecper engine, cached in aWeakKeyMap— all connections on one socket share codec state, which is what makes dict training meaningful across fan-in / fan-out. - Each wire message carries a 4-byte discriminator preamble:
00 00 00 00— plaintext (stripped on recv),- Zstd frame magic — a full Zstd frame (compressed payload),
- Zstd dict magic — a dictionary to install (silently swallowed on recv).
- The sender trains a single dict from its first ~1000 small messages
(or 100 KiB cumulative sample bytes), then compresses subsequent small
messages with it and ships the dict once per peer. User-supplied dicts
(
dict: bytes) replace training. - Decompression is bounded by
socket.options.max_message_size— the same cap that guards plaintext recv.--recv-maxsz 0(nil on the socket) disables it for compressed payloads too. Frames whose header omitsFrame_Content_Sizeare rejected. ZstdConnection#last_wire_size_incaches the compressed byte count of the last decoded payload frame so nnq's recv loop can surface it to:message_receivedverbose monitor events ((1000B wire=21B)in nnq-cli's-vvvtrace).
Out of scope
- Non-TCP transports.
ipc://andinproc://are plaintext-only; there is nozstd+ipc://. A transport-layer plugin would need an analogous wrap hook at the framing layer. - Negotiation / auto-detection. Both peers must use
zstd+tcp://. - Dict persistence across process restarts. Training is per-session.
- Receiver-side training — receivers only install shipped dicts.
License
ISC. See LICENSE.