using System; using Svrnty.CQRS.Events.Abstractions.Subscriptions; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Svrnty.CQRS.Events.Abstractions.Subscriptions; /// /// Registry for tracking active consumers subscribed to event streams. /// /// /// /// The consumer registry tracks which consumers are actively listening to which subscriptions. /// This is different from which tracks subscription configurations. /// /// /// Usage: /// - A subscription defines WHAT to listen to (e.g., "user-events with filter X") /// - A consumer is WHO is listening (e.g., "analytics-service-instance-1") /// - Multiple consumers can listen to the same subscription (broadcast or consumer group) /// /// public interface IConsumerRegistry { /// /// Register a consumer for a subscription. /// /// The subscription ID. /// The consumer ID. /// Optional metadata about the consumer (e.g., hostname, version). /// Cancellation token. /// A task representing the async operation. /// /// Registers the consumer as actively listening to the subscription. /// If the consumer is already registered, updates the last heartbeat timestamp. /// Task RegisterConsumerAsync( string subscriptionId, string consumerId, Dictionary? metadata = null, CancellationToken cancellationToken = default); /// /// Unregister a consumer from a subscription. /// /// The subscription ID. /// The consumer ID. /// Cancellation token. /// True if the consumer was unregistered, false if not found. /// /// Removes the consumer from the active consumer list. /// Should be called when a consumer disconnects or stops listening. /// Task UnregisterConsumerAsync( string subscriptionId, string consumerId, CancellationToken cancellationToken = default); /// /// Get all active consumers for a subscription. /// /// The subscription ID. /// Cancellation token. /// List of active consumer IDs. /// /// Returns consumers that are currently registered and have recent heartbeats. /// Stale consumers (no heartbeat for timeout period) are automatically excluded. /// Task> GetConsumersAsync( string subscriptionId, CancellationToken cancellationToken = default); /// /// Get detailed information about all active consumers for a subscription. /// /// The subscription ID. /// Cancellation token. /// List of consumer information including metadata and timestamps. Task> GetConsumerInfoAsync( string subscriptionId, CancellationToken cancellationToken = default); /// /// Update the heartbeat timestamp for a consumer. /// /// The subscription ID. /// The consumer ID. /// Cancellation token. /// True if the heartbeat was updated, false if consumer not found. /// /// Consumers should send heartbeats periodically to indicate they're still active. /// Consumers without recent heartbeats are considered stale and automatically removed. /// Task HeartbeatAsync( string subscriptionId, string consumerId, CancellationToken cancellationToken = default); /// /// Check if a specific consumer is currently registered. /// /// The subscription ID. /// The consumer ID. /// Cancellation token. /// True if the consumer is active, false otherwise. Task IsConsumerActiveAsync( string subscriptionId, string consumerId, CancellationToken cancellationToken = default); /// /// Remove stale consumers that haven't sent heartbeats within the timeout period. /// /// Consider consumers stale if no heartbeat for this duration. /// Cancellation token. /// Number of stale consumers removed. /// /// This should be called periodically by a background service to clean up disconnected consumers. /// Task RemoveStaleConsumersAsync( TimeSpan timeout, CancellationToken cancellationToken = default); } /// /// Information about a registered consumer. /// public sealed record ConsumerInfo { /// /// The consumer ID. /// public required string ConsumerId { get; init; } /// /// The subscription ID this consumer is subscribed to. /// public required string SubscriptionId { get; init; } /// /// When the consumer was first registered. /// public required DateTimeOffset RegisteredAt { get; init; } /// /// When the consumer last sent a heartbeat. /// public required DateTimeOffset LastHeartbeat { get; init; } /// /// Optional metadata about the consumer (e.g., hostname, version, process ID). /// public IReadOnlyDictionary? Metadata { get; init; } }