| | | 1 | | namespace AsyncResponse; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Faults a waiter's <see cref="IAsyncResponseWaiter{T}.ResponseTask"/> when the channel can no |
| | | 5 | | /// longer say whether the response was delivered. Two causes: disposal abandoned an in-flight |
| | | 6 | | /// delivery without learning its outcome (the drain of a dispatch that had already claimed a |
| | | 7 | | /// message — typically an <c>Until</c> predicate still running user code — did not finish within |
| | | 8 | | /// the channel's <c>DisposalDrainTimeout</c>), or a fire-and-forget channel was <em>overloaded</em> |
| | | 9 | | /// (responses for the correlation id arrived faster than the wait could process them and the |
| | | 10 | | /// bounded per-wait buffer filled; the next one could not be admitted). Either way the response |
| | | 11 | | /// may or may not have been consumed from the channel. |
| | | 12 | | /// <para> |
| | | 13 | | /// This is deliberately <em>not</em> a cancellation. A canceled response task tells the caller |
| | | 14 | | /// "nothing was delivered", which invites re-attaching to the correlation id — and if the wedged |
| | | 15 | | /// delivery had consumed the message, that re-attached wait can never be answered. Treat the |
| | | 16 | | /// awaiting step as indeterminate and restart it fresh (steps are idempotent); |
| | | 17 | | /// <c>DurableFlowContext</c> does so automatically through its faulted-wait path. |
| | | 18 | | /// </para> |
| | | 19 | | /// </summary> |
| | | 20 | | public sealed class AsyncResponseIndeterminateDeliveryException : Exception |
| | | 21 | | { |
| | | 22 | | /// <summary>Runs the AsyncResponseIndeterminateDeliveryException operation.</summary> |
| | | 23 | | public AsyncResponseIndeterminateDeliveryException(string? correlationId, TimeSpan drainTimeout) |
| | 15 | 24 | | : base($"Disposal abandoned an in-flight response delivery for correlationId '{correlationId}' " + |
| | 15 | 25 | | $"after draining for {drainTimeout.TotalSeconds:0.###}s. The response may already have been " + |
| | 15 | 26 | | "consumed from the channel; treat delivery as indeterminate and restart the awaiting " + |
| | 15 | 27 | | "(idempotent) step instead of re-attaching to this correlation id.") |
| | | 28 | | { |
| | 15 | 29 | | CorrelationId = correlationId; |
| | 15 | 30 | | } |
| | | 31 | | |
| | | 32 | | /// <summary> |
| | | 33 | | /// The overload form: <paramref name="bufferedMessages"/> responses were already queued behind |
| | | 34 | | /// the wait's serial processing when the next one arrived and could not be admitted. Nothing |
| | | 35 | | /// is discarded silently — the wait is faulted so the caller restarts the (idempotent) step — |
| | | 36 | | /// but a terminal response may be among the queued or the refused ones. |
| | | 37 | | /// </summary> |
| | | 38 | | public AsyncResponseIndeterminateDeliveryException(string? correlationId, int bufferedMessages) |
| | 4 | 39 | | : base($"Responses for correlationId '{correlationId}' arrived faster than the wait could process them: " + |
| | 4 | 40 | | $"{bufferedMessages} were already queued behind its serial processing when the next one could not be " + |
| | 4 | 41 | | "admitted. A terminal response may be among them; treat delivery as indeterminate and restart the " + |
| | 4 | 42 | | "awaiting (idempotent) step instead of re-attaching to this correlation id. Speed up the completion " + |
| | 4 | 43 | | "predicate, publish fewer progress messages, or use a retained (database) channel whose backlog stays ser |
| | | 44 | | { |
| | 4 | 45 | | CorrelationId = correlationId; |
| | 4 | 46 | | BufferedMessages = bufferedMessages; |
| | 4 | 47 | | } |
| | | 48 | | |
| | | 49 | | /// <summary>The correlation id whose delivery outcome is unknown.</summary> |
| | 15 | 50 | | public string? CorrelationId { get; } |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// For the overload form, how many responses were queued behind the wait's serial processing |
| | | 54 | | /// when the next one could not be admitted; <c>0</c> for the disposal-drain form. |
| | | 55 | | /// </summary> |
| | 4 | 56 | | public int BufferedMessages { get; } |
| | | 57 | | } |