| | | 1 | | namespace Elsa.Workflows.Runtime.IngressSources; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Base class for <see cref="IIngressSource"/> implementations whose actual pause/resume |
| | | 5 | | /// enforcement lives in another layer — request middleware, queue processor, scheduling |
| | | 6 | | /// dispatcher — and which only need to surface their state in the runtime's diagnostic |
| | | 7 | | /// registry. Subclasses provide a stable <see cref="Name"/> and may override |
| | | 8 | | /// <see cref="PauseTimeout"/>; everything else is derived from the shared |
| | | 9 | | /// <see cref="IQuiescenceSignal"/>. |
| | | 10 | | /// </summary> |
| | | 11 | | /// <remarks> |
| | | 12 | | /// <para> |
| | | 13 | | /// Use this base when your component already cooperates with <see cref="IQuiescenceSignal"/> |
| | | 14 | | /// at its hot path (e.g. an ASP.NET Core middleware that returns <c>503 Service Unavailable</c> |
| | | 15 | | /// while the runtime is not accepting new work, or a queue processor that consults the signal |
| | | 16 | | /// at the top of each invocation). The adapter is then a passive observer: <c>PauseAsync</c> |
| | | 17 | | /// / <c>ResumeAsync</c> are no-ops because there is nothing locally to start or stop, and |
| | | 18 | | /// <see cref="CurrentState"/> reflects the signal directly so the orchestrator's |
| | | 19 | | /// <c>DrainOutcome.Sources</c> and the admin status endpoint can show the source's state |
| | | 20 | | /// without each adapter re-implementing the same five-line snippet. |
| | | 21 | | /// </para> |
| | | 22 | | /// <para> |
| | | 23 | | /// Implement <see cref="IIngressSource"/> directly — not via this base — when the source owns |
| | | 24 | | /// concrete pause/resume behavior. For example, a message-queue consumer that needs to call |
| | | 25 | | /// <c>Pause()</c> on its underlying client, or an HTTP poller that must stop a background |
| | | 26 | | /// loop, has work to do at pause time and should encode that work in its own |
| | | 27 | | /// <c>PauseAsync</c> / <c>ResumeAsync</c> implementations. |
| | | 28 | | /// </para> |
| | | 29 | | /// </remarks> |
| | 23 | 30 | | public abstract class PassiveIngressSource(IQuiescenceSignal signal) : IIngressSource |
| | | 31 | | { |
| | 23 | 32 | | private readonly IQuiescenceSignal _signal = signal; |
| | | 33 | | |
| | | 34 | | /// <inheritdoc /> |
| | | 35 | | public abstract string Name { get; } |
| | | 36 | | |
| | | 37 | | /// <inheritdoc /> |
| | | 38 | | /// <remarks> |
| | | 39 | | /// Returns <see cref="TimeSpan.Zero"/> by default, which defers to the configured |
| | | 40 | | /// <c>GracefulShutdownOptions.IngressPauseTimeout</c>. Passive sources do no work at pause |
| | | 41 | | /// time, so any sub-second value is correct in practice; deferring to the option lets |
| | | 42 | | /// operators tune the value globally without per-source overrides. Subclasses may override |
| | | 43 | | /// to return a positive value if they have a reason to set their own per-source timeout. |
| | | 44 | | /// </remarks> |
| | 19 | 45 | | public virtual TimeSpan PauseTimeout => TimeSpan.Zero; |
| | | 46 | | |
| | | 47 | | /// <inheritdoc /> |
| | | 48 | | public IngressSourceState CurrentState => |
| | 2 | 49 | | _signal.IsAcceptingNewWork ? IngressSourceState.Running : IngressSourceState.Paused; |
| | | 50 | | |
| | | 51 | | /// <inheritdoc /> |
| | 19 | 52 | | public ValueTask PauseAsync(CancellationToken cancellationToken) => ValueTask.CompletedTask; |
| | | 53 | | |
| | | 54 | | /// <inheritdoc /> |
| | 0 | 55 | | public ValueTask ResumeAsync(CancellationToken cancellationToken) => ValueTask.CompletedTask; |
| | | 56 | | } |