258 lines
11 KiB
C#
258 lines
11 KiB
C#
using System;
|
|
using Svrnty.CQRS.Events.Abstractions.EventStore;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Svrnty.CQRS.Events.Abstractions.Models;
|
|
|
|
namespace Svrnty.CQRS.Events.Abstractions.EventStore;
|
|
|
|
/// <summary>
|
|
/// Storage abstraction for event streams with message queue semantics.
|
|
/// Supports both ephemeral (queue) and persistent (log) stream types.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <strong>Ephemeral Streams (Phase 1):</strong>
|
|
/// Events are enqueued and dequeued like a message queue. Events are deleted after acknowledgment.
|
|
/// Supports multiple consumers with visibility tracking.
|
|
/// </para>
|
|
/// <para>
|
|
/// <strong>Persistent Streams (Phase 2+):</strong>
|
|
/// Events are appended to an append-only log. Events are never deleted (except by retention policy).
|
|
/// Consumers track their position (offset) in the stream.
|
|
/// </para>
|
|
/// </remarks>
|
|
public interface IEventStreamStore
|
|
{
|
|
// ========================================================================
|
|
// EPHEMERAL STREAM OPERATIONS (Message Queue Semantics)
|
|
// ========================================================================
|
|
|
|
/// <summary>
|
|
/// Enqueue an event to an ephemeral stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="event">The event to enqueue.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>A task representing the async operation.</returns>
|
|
/// <remarks>
|
|
/// For ephemeral streams, this adds the event to a queue.
|
|
/// The event will be delivered to consumers and then deleted after acknowledgment.
|
|
/// </remarks>
|
|
Task EnqueueAsync(
|
|
string streamName,
|
|
ICorrelatedEvent @event,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Enqueue multiple events to an ephemeral stream in a batch.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="events">The events to enqueue.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>A task representing the async operation.</returns>
|
|
Task EnqueueBatchAsync(
|
|
string streamName,
|
|
IEnumerable<ICorrelatedEvent> events,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Dequeue the next available event from an ephemeral stream for a specific consumer.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="consumerId">The consumer ID requesting the event.</param>
|
|
/// <param name="visibilityTimeout">How long the event should be invisible to other consumers while processing.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The next event, or null if the queue is empty.</returns>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The event becomes invisible to other consumers for the duration of the visibility timeout.
|
|
/// The consumer must call <see cref="AcknowledgeAsync"/> to permanently remove the event,
|
|
/// or <see cref="NackAsync"/> to make it visible again (for retry).
|
|
/// </para>
|
|
/// <para>
|
|
/// If the visibility timeout expires without acknowledgment, the event automatically becomes
|
|
/// visible again for other consumers to process.
|
|
/// </para>
|
|
/// </remarks>
|
|
Task<ICorrelatedEvent?> DequeueAsync(
|
|
string streamName,
|
|
string consumerId,
|
|
TimeSpan visibilityTimeout,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Acknowledge successful processing of an event, permanently removing it from the queue.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</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 the event was acknowledged, false if not found or already acknowledged.</returns>
|
|
/// <remarks>
|
|
/// After acknowledgment, the event is permanently deleted from the ephemeral stream.
|
|
/// </remarks>
|
|
Task<bool> AcknowledgeAsync(
|
|
string streamName,
|
|
string eventId,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Negative acknowledge (NACK) an event, making it visible again for reprocessing.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</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, make the event immediately available. If false, send to dead letter queue.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>True if the event was nacked, false if not found.</returns>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Use NACK when processing fails and the event should be retried.
|
|
/// The event becomes immediately visible to other consumers if <paramref name="requeue"/> is true.
|
|
/// </para>
|
|
/// <para>
|
|
/// If <paramref name="requeue"/> is false, the event is moved to a dead letter queue
|
|
/// for manual inspection (useful after max retry attempts).
|
|
/// </para>
|
|
/// </remarks>
|
|
Task<bool> NackAsync(
|
|
string streamName,
|
|
string eventId,
|
|
string consumerId,
|
|
bool requeue = true,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Get the approximate count of pending events in an ephemeral stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The approximate number of events waiting to be processed.</returns>
|
|
/// <remarks>
|
|
/// This count is approximate and may not reflect in-flight events being processed.
|
|
/// Use for monitoring and metrics, not for critical business logic.
|
|
/// </remarks>
|
|
Task<int> GetPendingCountAsync(
|
|
string streamName,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
// ========================================================================
|
|
// PERSISTENT STREAM OPERATIONS (Event Log Semantics) - Phase 2+
|
|
// ========================================================================
|
|
|
|
/// <summary>
|
|
/// Append an event to a persistent stream (append-only log).
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="event">The event to append.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The offset (position) assigned to this event in the stream.</returns>
|
|
/// <remarks>
|
|
/// Phase 2 feature. For persistent streams, events are never deleted (except by retention policies).
|
|
/// Events are assigned sequential offsets starting from 0.
|
|
/// </remarks>
|
|
Task<long> AppendAsync(
|
|
string streamName,
|
|
ICorrelatedEvent @event,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Read events from a persistent stream starting at a specific offset.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="fromOffset">The offset to start reading from (inclusive).</param>
|
|
/// <param name="maxCount">Maximum number of events to return.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>List of events starting from the specified offset.</returns>
|
|
/// <remarks>
|
|
/// Phase 2 feature. Used for catch-up subscriptions and event replay.
|
|
/// </remarks>
|
|
Task<List<ICorrelatedEvent>> ReadStreamAsync(
|
|
string streamName,
|
|
long fromOffset,
|
|
int maxCount,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Get the current length (number of events) in a persistent stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The total number of events in the stream.</returns>
|
|
/// <remarks>
|
|
/// Phase 2 feature. Used for monitoring and to detect consumer lag.
|
|
/// </remarks>
|
|
Task<long> GetStreamLengthAsync(
|
|
string streamName,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Get metadata about a persistent stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>Stream metadata including length, retention info, and event timestamps.</returns>
|
|
/// <remarks>
|
|
/// Phase 2 feature. Provides comprehensive information about stream state for monitoring,
|
|
/// consumer lag detection, and retention policy verification.
|
|
/// </remarks>
|
|
Task<StreamMetadata> GetStreamMetadataAsync(
|
|
string streamName,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
// ========================================================================
|
|
// CONSUMER OFFSET TRACKING - Phase 6 (Monitoring & Health Checks)
|
|
// ========================================================================
|
|
|
|
/// <summary>
|
|
/// Get the current offset (position) of a consumer in a persistent stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="consumerId">The consumer ID.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The consumer's current offset, or 0 if no offset is stored.</returns>
|
|
/// <remarks>
|
|
/// Phase 6 feature. Used for health checks to detect consumer lag and stalled consumers.
|
|
/// </remarks>
|
|
Task<long> GetConsumerOffsetAsync(
|
|
string streamName,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Get the last time a consumer updated its offset in a persistent stream.
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="consumerId">The consumer ID.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The last update time, or DateTimeOffset.MinValue if no offset is stored.</returns>
|
|
/// <remarks>
|
|
/// Phase 6 feature. Used for health checks to detect stalled consumers (no progress for extended time).
|
|
/// </remarks>
|
|
Task<DateTimeOffset> GetConsumerLastUpdateTimeAsync(
|
|
string streamName,
|
|
string consumerId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Update a consumer's offset manually (for management operations).
|
|
/// </summary>
|
|
/// <param name="streamName">The name of the stream.</param>
|
|
/// <param name="consumerId">The consumer ID.</param>
|
|
/// <param name="newOffset">The new offset to set.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>A task representing the async operation.</returns>
|
|
/// <remarks>
|
|
/// Phase 6 feature. Used by management API to reset consumer positions.
|
|
/// Use with caution as this can cause events to be reprocessed or skipped.
|
|
/// </remarks>
|
|
Task UpdateConsumerOffsetAsync(
|
|
string streamName,
|
|
string consumerId,
|
|
long newOffset,
|
|
CancellationToken cancellationToken = default);
|
|
}
|