Skip to content

Control Plane (UDP)

Control-plane operations are UDP-only and market-data-only.
Order routing has no control-plane channel in this version.

Use this plane for non-hot-path actions:

  • Runtime subscription changes
  • On-demand snapshot requests

Market and order messages still flow on SHM rings.

Scope

  • Plane scope: one control-plane endpoint per stack (master or nightly)
  • Data scope: one venue per request (set in request header)
  • ID scope: inst_id only (resolve canonical keys through metadata before sending control requests)

Transport contract

  • Protocol: UDP unicast
  • Reliability: best-effort, client retries required
  • Encoding: little-endian packed binary
  • Maximum datagram size: 1200 bytes (never rely on IP fragmentation)

Endpoint configuration is in feedd config:

[control_plane]
bind_host = "127.0.0.1"
bind_port = 5510

bind_host/bind_port identify the UDP listener for control-plane requests for that stack.

Control-plane payloads are packed exactly as documented below. Do not assume native struct alignment; parse by offset and payload_len.

Wire format

Request header (32 bytes)

offset  size  type   field
────────────────────────────────────────────
0       2     u16    version         protocol version (currently 1)
2       1     u8     op              1=SUBSCRIBE, 2=UNSUBSCRIBE, 3=REQUEST_SNAPSHOT
3       1     u8     stack           1=master, 2=nightly
4       1     u8     venue           venue ID from metadata registry
5       1     u8     flags           request flags (currently 0)
6       2     u16    payload_len     bytes after header
8       8     u64    client_id       stable sender identity
16      8     u64    request_id      idempotency key per client_id
24      8     u64    send_ts_ns      client timestamp (optional; 0 allowed)
────────────────────────────────────────────

Response header (32 bytes)

offset  size  type   field
────────────────────────────────────────────
0       2     u16    version         protocol version (currently 1)
2       1     u8     op              echoes request op
3       1     u8     stack           echoes request stack
4       1     u8     venue           echoes request venue
5       1     u8     status          0=OK, non-zero=error code
6       2     u16    payload_len     bytes after header
8       8     u64    client_id       echoes request client_id
16      8     u64    request_id      echoes request request_id
24      8     u64    recv_ts_ns      feedd receive timestamp
────────────────────────────────────────────

Status / error codes

Code Name Meaning
0 OK Request accepted
1 BAD_VERSION Unsupported control-plane version
2 UNKNOWN_OP Invalid op code
3 BAD_PAYLOAD Malformed payload or invalid size
4 UNKNOWN_INSTRUMENT inst_id not present/active in metadata
5 VENUE_UNAVAILABLE Venue disconnected or unavailable
6 RATE_LIMITED Snapshot/control rate limit exceeded
7 TOO_MANY_ITEMS Instrument list exceeds server limit
8 INTERNAL Internal error

Operations

subscribe

Add one or more instruments for one venue.

Payload:

offset  size  type           field
────────────────────────────────────────
0       2     u16            n_inst
2       ...   u64[n_inst]    inst_ids

Rules:

  • Atomic apply: any invalid inst_id rejects the full request (UNKNOWN_INSTRUMENT)
  • Idempotent: re-subscribing an already subscribed instrument is accepted (no-op for that instrument)
  • n_inst must be greater than 0

Success response payload:

offset  size  type   field
────────────────────────────────────────
0       2     u16    applied_count
2       8     u64    apply_seq_hint

apply_seq_hint is an advisory md-ring sequence watermark. First updates for newly subscribed instruments are published at seq >= apply_seq_hint.

unsubscribe

Remove one or more instruments for one venue.

Payload:

offset  size  type           field
────────────────────────────────────────
0       2     u16            n_inst
2       ...   u64[n_inst]    inst_ids

Rules:

  • Idempotent: unsubscribing an instrument not currently active is accepted (no-op for that instrument)
  • n_inst must be greater than 0

Success response payload:

offset  size  type   field
────────────────────────────────────────
0       2     u16    applied_count
2       8     u64    drain_seq_hint

drain_seq_hint is an advisory watermark. After consumer processes md messages up to this seq, no new updates for removed instruments should arrive (except already-published frames before the cutover).

request_snapshot

Request a fresh snapshot for one instrument.

Payload:

offset  size  type   field
────────────────────────────────────────
0       8     u64    inst_id
8       1     u8     snap_type      1=L2_BOOK, 2=L4_ORDERS
9       2     u16    depth          0=full depth, else top N
11      4     u32    timeout_ms     advisory; 0 uses server default

Success response payload:

offset  size  type   field
────────────────────────────────────────
0       8     u64    accepted_seq

accepted_seq is the md sequence watermark when the request was accepted.

Completion path:

  1. Client receives OK response for request_snapshot
  2. feedd publishes SNAPSHOT_REF on /sorcery-{stack}-md
  3. Client reads snapshot bytes from /sorcery-{stack}-snapshot and verifies checksum

Correlation rule:

  • SNAPSHOT_REF does not carry request_id
  • Correlate by (venue, inst_id, snap_type, depth) and snap_seq >= accepted_seq
  • Multiple outstanding requests for the same tuple may be coalesced by feedd

Operational limits

These limits are part of the control-plane contract:

  • payload_len MUST be <= 1100 bytes (BAD_PAYLOAD otherwise).
  • subscribe / unsubscribe n_inst MUST be in [1, 128] (TOO_MANY_ITEMS if exceeded, BAD_PAYLOAD if zero).
  • request_snapshot.timeout_ms:
  • 0 means server default 1500ms
  • accepted range is [10, 10000] (BAD_PAYLOAD outside range)
  • For identical snapshot tuples (venue, inst_id, snap_type, depth), servers MAY coalesce concurrent requests.

Idempotency and retries

Deduplication key: (client_id, request_id).

Server behavior:

  • Same key + same payload: return cached prior response
  • Same key + different payload: reject with BAD_PAYLOAD

Client policy:

  1. Use monotone request_id per client_id
  2. Retry with bounded exponential backoff + jitter
  3. Recommended backoff envelope: 10ms initial, x2, cap 250ms, max 8 attempts
  4. Treat timeout as unknown outcome and retry with the same (client_id, request_id) pair
  5. Escalate after bounded retry budget exhaustion

Failure handling

Clients must handle:

  • Lost requests
  • Lost responses
  • Duplicate responses
  • Out-of-order responses

Recovery rules:

  1. On request_snapshot timeout or error, keep affected book INVALID
  2. Continue draining md ring while retrying snapshot request
  3. Resume normal book use only after applying a valid SNAPSHOT_REF