Merge fix/c3-ipc-boundaries: IPC-33 drain budget bounded under the gRPC send cap + validator upper coupling, IPC-39 pre-check sequence allowance, IPC-34 WorkerCancel on caller cancellation + CallerCancelled code, IPC-35 handshake WorkerFault surfaces category
This commit is contained in:
@@ -27,7 +27,8 @@ public sealed class WorkerPipeSession
|
||||
// Headroom subtracted from the negotiated frame maximum when budgeting a DrainEvents reply. It
|
||||
// covers the WorkerEnvelope/WorkerCommandReply/MxCommandReply wrapper the drained events are
|
||||
// packed into — the same envelope-overhead reserve rationale docs/WorkerFrameProtocol.md
|
||||
// records for the frame max itself.
|
||||
// records for the frame max itself. ResolveDrainReplyByteBudget subtracts it twice: once for the
|
||||
// pipe frame, once so the reply also clears the public gRPC send cap it is forwarded through.
|
||||
private const int DrainReplyFrameHeadroomBytes = 64 * 1024;
|
||||
|
||||
private readonly WorkerFrameProtocolOptions _options;
|
||||
@@ -1021,20 +1022,33 @@ public sealed class WorkerPipeSession
|
||||
|
||||
/// <summary>
|
||||
/// Byte budget for the events packed into one DrainEvents reply: the negotiated frame
|
||||
/// maximum less a fixed reserve for the envelope/reply wrapper, but never below half the
|
||||
/// negotiated maximum. The lower bound must be a floor, not a step: a bare
|
||||
/// <c>subtract-then-guard-positive</c> collapses the budget to a handful of bytes just above
|
||||
/// the reserve (e.g. at the validator-permitted floor MaxMessageBytes = 1024 + 64 KiB the
|
||||
/// subtraction leaves 1024, too small to move even one byte-heavy event, so every drain
|
||||
/// truncates and the drain-until-empty caller never terminates). Taking the max with
|
||||
/// half the negotiated maximum keeps the budget monotonic across the reserve boundary while
|
||||
/// still leaving the full reserve for the wrapper whenever the frame max is large enough
|
||||
/// that the reserve is the smaller subtraction — which is every configuration above 128 KiB.
|
||||
/// maximum less <em>two</em> reserves, but never below half the negotiated maximum. The
|
||||
/// first reserve covers the envelope/reply wrapper the events are packed into so the reply
|
||||
/// fits one pipe frame. The second reserve exists because that same
|
||||
/// <c>MxCommandReply</c> does not stop at the pipe: the gateway returns it verbatim through
|
||||
/// <c>Invoke</c>, where the public gRPC send cap applies, and the gateway's validator
|
||||
/// couples the two limits so the pipe maximum never exceeds the gRPC cap by more than two
|
||||
/// reserves (IPC-33). Subtracting both therefore keeps the packed events at or below the
|
||||
/// gRPC cap and leaves the wrapper — correlation id, protocol status, kind, the
|
||||
/// <c>drain_events</c> length prefix and, in the truncated case, the
|
||||
/// <c>DiagnosticMessage</c> — a full reserve of margin under it. Budgeting against the pipe
|
||||
/// maximum alone let a legal configuration build a reply the pipe carried and the gRPC layer
|
||||
/// then refused to send, with the events already dequeued and therefore lost.
|
||||
/// <para>
|
||||
/// The lower bound must be a floor, not a step: a bare <c>subtract-then-guard-positive</c>
|
||||
/// collapses the budget to a handful of bytes (or below zero) once the reserves approach the
|
||||
/// frame maximum (e.g. at the validator-permitted floor MaxMessageBytes = 1024 + 64 KiB),
|
||||
/// too small to move even one byte-heavy event, so every drain truncates and the
|
||||
/// drain-until-empty caller never terminates. Taking the max with half the negotiated
|
||||
/// maximum keeps the budget monotonic across the reserve boundary while still leaving both
|
||||
/// reserves for the wrapper whenever the frame max is large enough that the subtraction is
|
||||
/// the smaller of the two — which is every configuration above 256 KiB.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
private int ResolveDrainReplyByteBudget()
|
||||
{
|
||||
return Math.Max(
|
||||
_options.MaxMessageBytes - DrainReplyFrameHeadroomBytes,
|
||||
_options.MaxMessageBytes - (2 * DrainReplyFrameHeadroomBytes),
|
||||
_options.MaxMessageBytes / 2);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user