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).
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_versionmust match the configured worker protocol version,session_idmust match the owning gateway session,- the envelope must contain one typed
bodyvalue.
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