200 lines
8.4 KiB
C#
200 lines
8.4 KiB
C#
using System;
|
|
using Svrnty.CQRS.Events.Abstractions.EventStore;
|
|
using Svrnty.CQRS.Events.Abstractions.Models;
|
|
using Svrnty.CQRS.Events.Abstractions.Storage;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Svrnty.CQRS.Events.Abstractions.Subscriptions;
|
|
|
|
/// <summary>
|
|
/// Client interface for subscribing to event streams and consuming events.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This is the primary interface consumers use to receive events from subscriptions.
|
|
/// Supports async enumeration (IAsyncEnumerable) for streaming consumption.
|
|
/// </para>
|
|
/// <para>
|
|
/// <strong>Usage Pattern:</strong>
|
|
/// <code>
|
|
/// await foreach (var @event in client.SubscribeAsync("my-subscription", "consumer-1", ct))
|
|
/// {
|
|
/// // Process event
|
|
/// await ProcessEventAsync(@event);
|
|
///
|
|
/// // Event is automatically acknowledged after successful processing
|
|
/// // (unless manual acknowledgment mode is enabled)
|
|
/// }
|
|
/// </code>
|
|
/// </para>
|
|
/// </remarks>
|
|
public interface IEventSubscriptionClient
|
|
{
|
|
/// <summary>
|
|
/// Subscribe to a subscription and receive events as an async stream.
|
|
/// </summary>
|
|
/// <param name="subscriptionId">The subscription ID to consume from.</param>
|
|
/// <param name="consumerId">Unique identifier for this consumer instance.</param>
|
|
/// <param name="cancellationToken">Cancellation token to stop consuming.</param>
|
|
/// <returns>Async enumerable stream of events.</returns>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Events are automatically acknowledged after being yielded, unless manual acknowledgment is enabled.
|
|
/// The consumer is automatically registered when enumeration starts and unregistered when it stops.
|
|
/// </para>
|
|
/// <para>
|
|
/// <strong>Subscription Modes:</strong>
|
|
/// - Broadcast: Each consumer gets all events
|
|
/// - Exclusive: Only one consumer gets each event (load balanced)
|
|
/// - ConsumerGroup: Load balanced across group members
|
|
/// - ReadReceipt: Requires explicit MarkAsRead call
|
|
/// </para>
|
|
/// </remarks>
|
|
IAsyncEnumerable<ICorrelatedEvent> SubscribeAsync(
|
|
string subscriptionId,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Subscribe with consumer metadata (hostname, version, etc.).
|
|
/// </summary>
|
|
/// <param name="subscriptionId">The subscription ID to consume from.</param>
|
|
/// <param name="consumerId">Unique identifier for this consumer instance.</param>
|
|
/// <param name="metadata">Optional metadata about this consumer.</param>
|
|
/// <param name="cancellationToken">Cancellation token to stop consuming.</param>
|
|
/// <returns>Async enumerable stream of events.</returns>
|
|
IAsyncEnumerable<ICorrelatedEvent> SubscribeAsync(
|
|
string subscriptionId,
|
|
string consumerId,
|
|
Dictionary<string, string> metadata,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Manually acknowledge an event (only needed if manual acknowledgment mode is enabled).
|
|
/// </summary>
|
|
/// <param name="subscriptionId">The subscription ID.</param>
|
|
/// <param name="eventId">The event ID to acknowledge.</param>
|
|
/// <param name="consumerId">The consumer ID acknowledging the event.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>True if acknowledged, false if event not found or already acknowledged.</returns>
|
|
Task<bool> AcknowledgeAsync(
|
|
string subscriptionId,
|
|
string eventId,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Negative acknowledge an event (NACK), marking it for redelivery or dead letter.
|
|
/// </summary>
|
|
/// <param name="subscriptionId">The subscription ID.</param>
|
|
/// <param name="eventId">The event ID to NACK.</param>
|
|
/// <param name="consumerId">The consumer ID nacking the event.</param>
|
|
/// <param name="requeue">If true, requeue for retry. If false, move to dead letter queue.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>True if nacked, false if event not found.</returns>
|
|
Task<bool> NackAsync(
|
|
string subscriptionId,
|
|
string eventId,
|
|
string consumerId,
|
|
bool requeue = true,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Get subscription details.
|
|
/// </summary>
|
|
/// <param name="subscriptionId">The subscription ID.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The subscription configuration, or null if not found.</returns>
|
|
Task<ISubscription?> GetSubscriptionAsync(
|
|
string subscriptionId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Get all active consumers for a subscription.
|
|
/// </summary>
|
|
/// <param name="subscriptionId">The subscription ID.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>List of active consumer information.</returns>
|
|
Task<List<ConsumerInfo>> GetActiveConsumersAsync(
|
|
string subscriptionId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Unsubscribe a consumer from a subscription.
|
|
/// </summary>
|
|
/// <param name="subscriptionId">The subscription ID.</param>
|
|
/// <param name="consumerId">The consumer ID to unregister.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>True if unregistered, false if not found.</returns>
|
|
Task<bool> UnsubscribeAsync(
|
|
string subscriptionId,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
// ========================================================================
|
|
// Phase 3: Read Receipt API (Consumer Progress Tracking)
|
|
// ========================================================================
|
|
|
|
/// <summary>
|
|
/// Records a read receipt for an event, tracking consumer progress.
|
|
/// </summary>
|
|
/// <param name="streamName">The stream name.</param>
|
|
/// <param name="consumerId">The consumer identifier.</param>
|
|
/// <param name="eventId">The event ID being acknowledged.</param>
|
|
/// <param name="offset">The event's offset/position in the stream.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Read receipts differ from acknowledgments:
|
|
/// - Acknowledgments are for subscription delivery tracking
|
|
/// - Read receipts are for consumer progress/offset tracking
|
|
/// </para>
|
|
/// <para>
|
|
/// Use this to track which events a consumer has successfully processed,
|
|
/// allowing resume from last position and monitoring consumer lag.
|
|
/// </para>
|
|
/// </remarks>
|
|
Task RecordReadReceiptAsync(
|
|
string streamName,
|
|
string consumerId,
|
|
string eventId,
|
|
long offset,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Gets the last acknowledged offset for a consumer on a stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The stream name.</param>
|
|
/// <param name="consumerId">The consumer identifier.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The last acknowledged offset, or null if no receipts exist.</returns>
|
|
Task<long?> GetLastReadOffsetAsync(
|
|
string streamName,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Gets consumer progress statistics for a stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The stream name.</param>
|
|
/// <param name="consumerId">The consumer identifier.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>Consumer progress information, or null if no receipts exist.</returns>
|
|
Task<ConsumerProgress?> GetConsumerProgressAsync(
|
|
string streamName,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Gets all consumers tracking a specific stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The stream name.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>List of consumer IDs tracking this stream.</returns>
|
|
Task<IReadOnlyList<string>> GetStreamConsumersAsync(
|
|
string streamName,
|
|
CancellationToken cancellationToken = default);
|
|
}
|