Files
mxaccessgw/docs/WorkerFrameProtocol.md
T
Joseph Doherty c8b3a2281a feat(ipc): negotiate worker frame max, add gRPC headroom, bound DrainEvents (IPC-02/03/04 gateway half)
Proto foundation + gateway-side of the size/backpressure pass:

- IPC-02: add GatewayHello.max_frame_bytes (regen Generated/); gateway sends
  its negotiated worker-frame max in the handshake so the worker can adopt it
  instead of a hard-coded default. Worker read-half lands separately.
- IPC-03: give the pipe frame max envelope-overhead headroom above the public
  gRPC cap (WorkerFrameProtocolOptions.EnvelopeOverheadReserveBytes = 64 KiB;
  default Worker.MaxMessageBytes bumped to 16 MiB + reserve), cross-validate the
  headroom at startup, and pre-check command envelope size in WorkerClient so an
  oversized command fails only that correlation (ResourceExhausted) instead of
  faulting the whole session.
- IPC-04: reject DrainEvents max_events above a public ceiling in the request
  validator (worker per-reply cap lands with the worker half).

Docs: GatewayConfiguration, WorkerFrameProtocol, gateway.md.
Tests: headroom validation, DrainEvents bound, oversized-command per-command
failure (pipe-harness, verified on windev).
2026-07-09 08:49:59 -04:00

2.7 KiB

Worker Frame Protocol

The gateway uses the worker frame protocol to move WorkerEnvelope protobuf messages over a bidirectional named pipe. The frame layer is deliberately small: it handles message boundaries, size limits, protobuf parsing, and envelope validation before higher-level worker client code routes commands, replies, events, and faults.

Frame Format

Each frame starts with a four-byte little-endian unsigned payload length, followed by the serialized WorkerEnvelope payload:

uint32 little-endian payload_length
payload_length bytes protobuf WorkerEnvelope

The reader rejects zero-length payloads and payloads larger than the configured maximum before allocating the payload buffer. The default maximum is the 16 MiB public gRPC cap plus a 64 KiB envelope-overhead reserve (16842752 bytes) so a maximally-sized accepted gRPC payload always fits one worker frame once wrapped in a WorkerEnvelope.

The gateway is the source of truth for this maximum: it conveys the negotiated value in the handshake as GatewayHello.max_frame_bytes, and the worker adopts it as its WorkerFrameProtocolOptions.MaxMessageBytes instead of a hard-coded default. A max_frame_bytes of 0 (an older gateway that never set the field) means "use the worker's built-in default". This keeps both ends framing to the same limit rather than depending on matched compile-time constants.

Envelope Validation

WorkerFrameReader and WorkerFrameWriter validate each envelope against the owning session before returning or writing it:

  • protocol_version must match the configured worker protocol version,
  • session_id must match the owning gateway session,
  • the envelope must contain one typed body value.

Protocol violations throw WorkerFrameProtocolException with a WorkerFrameProtocolErrorCode so callers can distinguish malformed frames, oversized frames, protocol version mismatches, and session mismatches.

Verification

The frame protocol lives in ZB.MOM.WW.MxGateway.Worker.Ipc (WorkerFrameReader, WorkerFrameWriter, WorkerFrameProtocolOptions) and is covered by src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerFrameProtocolTests.cs. The worker is an x86 process, so build and test it with -p:Platform=x86.

Run the focused tests after changing the frame protocol:

dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter WorkerFrameProtocolTests

Run the x86 worker build because the frame protocol is part of ZB.MOM.WW.MxGateway.Worker:

dotnet build src/ZB.MOM.WW.MxGateway.Worker/ZB.MOM.WW.MxGateway.Worker.csproj -p:Platform=x86