Repository navigation
3.4 Structured Zmq IpcMessage Transport
Feature-gated (zmq) PUB/SUB transport that carries one compact canonical JSON WireEnvelope of IpcMessage per ZeroMQ message. Added on main @ 08d2f28 (#55; CHANGELOG Unreleased / LIM-1278).
This is a separate protocol surface from binary readout ZmqIpcBackend and from the REST server. Sources: src/zmq_message/mod.rs, src/zmq_message/transport.rs, src/zmq_message/hybrid.rs.
| Surface | Feature | Framing | Types |
|---|---|---|---|
| Structured ZeroMQ (this page) | zmq |
one canonical JSON WireEnvelope of IpcMessage
|
ZmqMessagePublisher, ZmqMessageSubscriber, ZmqHybridFlowBackend
|
| Binary readout | zmq |
[i64 tick][N × f32 LE] |
ZmqIpcBackend only |
| REST | server |
HTTP JSON around IpcBackend
|
corpus_ipc_server |
ZmqIpcBackend does not implement HybridFlowBackend. ZmqHybridFlowBackend does not implement IpcBackend.
| Type | Role |
|---|---|
ZmqTransportConfig |
endpoint, send/receive HWM, linger, reconnect interval/max — applied before bind/connect |
ZmqMessagePublisher |
binds PUB; validates with Validate, then encode_canonical_ipc_message, then send |
ZmqMessageSubscriber |
connects SUB (empty subscription prefix); try_recv → decode_ipc_message_json + Validate
|
ZmqHybridFlowBackend |
HybridFlowBackend adapter over that pair |
ZmqMessageError |
structured-transport errors (InvalidConfig, WouldBlock, HighWaterMark, Closed, framing, envelope, …) |
FramingFault |
Empty or Multipart
|
STRUCTURED_MAX_FRAME_BYTES |
512 MiB (ZMQ_MAXMSGSIZE); publisher rejects oversized encoded frames before send |
Root re-exports (feature zmq): see src/lib.rs.
| Field | Default |
|---|---|
send_hwm / receive_hwm
|
1000 (0 = unlimited, matching libzmq) |
linger_ms |
0 (discard unsent on close) |
reconnect_ivl_ms |
100 |
reconnect_ivl_max_ms |
5000 (must be ≥ interval; libzmq ignores a smaller cap) |
Publisher binds; subscriber connects. PUB/SUB is best-effort and at-most-once (no acknowledgements): slow-joiner loss, HWM drops, and linger 0 teardown all discard frames. A successful send means the local socket accepted the frame, not that a peer observed it. Control/config variants use the same contract.
Inbound frames above STRUCTURED_MAX_FRAME_BYTES are rejected by libzmq before allocate. Multipart input is drained up to eight extra frames: a message that fits in that drain returns a framing error and leaves the subscriber open; frames still remaining after the drain close the subscriber.
Sockets stay mutex-serialized Send + Sync with no unsafe impl Sync. ZmqMessagePublisher::bind and ZmqMessageSubscriber::connect use the crate-wide shared context from src/zmq_context.rs (#62), which stays alive until process exit, so closing one socket never tears the context down under another thread.
open(outbound, inbound) binds PUB + connects SUB. Identical outbound and inbound endpoints are rejected before either socket opens.
| Trait method |
IpcMessage variant |
Direction |
|---|---|---|
send_spikes |
Spikes |
out |
send_embeddings |
Embeddings |
out |
try_receive_gradients |
GradientUpdate |
in |
try_receive_traces |
EligibilityTraces |
in |
- The other supported inbound variant (gradient while receiving traces, or vice versa) is held one message per variant for its own method.
- A second held sibling returns
BackendError::CommunicationErrorand consumes the new frame (held frame stays). - Any unrelated inbound variant returns
BackendError::Protocol(ProtocolError::UnexpectedVariant); the frame is consumed, not dropped quietly. - Payload validation failures inside an envelope surface as
BackendError::InvalidInput. - A closed publisher reports
Closed/ mapped communication error before payload validation on send. - Non-blocking empty receive →
Ok(None)on the hybrid methods (WouldBlockon the raw subscriber).
flowchart LR
App["Caller"] -->|"send_spikes / send_embeddings"| Hybrid["ZmqHybridFlowBackend"]
Hybrid -->|"IpcMessage::Spikes / Embeddings"| Pub["ZmqMessagePublisher.bind"]
Pub -->|"Validate + encode_canonical_ipc_message"| WireOut["WireEnvelope JSON frame"]
WireOut -->|"PUB"| PeerSub["Peer SUB"]
PeerPub["Peer PUB"] -->|"SUB"| Sub["ZmqMessageSubscriber.connect"]
Sub -->|"decode_ipc_message_json + Validate"| Hybrid
Hybrid -->|"try_receive_gradients / traces"| App
Hold["held_gradient / held_trace slots"] -.-> Hybrid
Unit/live coverage under src/zmq_message/ (tests.rs, transport_tests.rs, framing_tests.rs, hybrid_live.rs) — feature zmq.
- Home
- 1. Overview
- 1.1. Getting Started
- 1.2. Architecture Overview
- 1.3. Crate Exports and Module Map
- 2. Core Abstractions
- 2.1. IpcBackend Trait
- 2.2. HybridFlowBackend Trait
- 2.3. BackendFactory and BackendType
- 2.4. Error Handling
- 3. Backend Implementations
- 3.1. RustBackend
- 3.2. ZmqIpcBackend
- 3.3. ZMQ Readout Parsing and Golden Frames
- 3.4. Structured ZeroMQ IpcMessage Transport
- 4. IPC Protocol and Data Models
- 4.1. IpcMessage Protocol
- 4.2. Batch Data Types
- 4.3. NeuromodulatorSnapshot and Neuromodulator Packet
- 4.4. StimulusBatch and Runtime Ingress
- 4.5. Deserialization Hooks and Model Compat Aliases
- 5. Validation and Wire Compatibility
- 5.1. Validation Framework and ProtocolLimits
- 5.2. ValidationError and ValidationKind
- 5.3. WireCompatibility and Envelope Decoding
- 5.4. Canonical Wire-v1 Encoding
- 6. corpus_ipc_server HTTP Server
- 6.1. REST API Endpoints
- 6.2. Server Configuration and Deployment
- 7. Build, Dependencies, and Quality Gates
- 7.1. Cargo Features and Dependency Isolation
- 7.2. Testing Strategy
- 7.2.1. Wire-v1 Golden Test Vectors
- 7.2.2. Property-Based Wire Tests
- 7.3. CI, Coverage, and Release Qualification
- 8. Project History and Licensing
- 8.1. Naming History and Completed Renames
- 8.2. Licensing
- 9. Glossary