dotnet-cqrs/Svrnty.CQRS.Events/Storage/InMemoryEventStore.cs

102 lines
3.0 KiB
C#

using System;
using Svrnty.CQRS.Events.Abstractions.EventStore;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Svrnty.CQRS.Events.Abstractions;
using Svrnty.CQRS.Events.Abstractions.Models;
namespace Svrnty.CQRS.Events.Storage;
/// <summary>
/// In-memory implementation of IEventStore for testing and development.
/// Thread-safe but data is lost on application restart.
/// </summary>
public sealed class InMemoryEventStore : IEventStore
{
private readonly ConcurrentDictionary<string, StoredEvent> _events = new();
private long _sequenceCounter = 0;
public Task<long> AppendAsync(ICorrelatedEvent @event, CancellationToken cancellationToken = default)
{
var sequence = Interlocked.Increment(ref _sequenceCounter);
var storedEvent = new StoredEvent
{
EventId = @event.EventId,
CorrelationId = @event.CorrelationId,
EventType = @event.GetType().Name,
Sequence = sequence,
Event = @event,
OccurredAt = @event.OccurredAt,
StoredAt = DateTimeOffset.UtcNow
};
_events.TryAdd(@event.EventId, storedEvent);
return Task.FromResult(sequence);
}
public async Task<Dictionary<string, long>> AppendBatchAsync(IEnumerable<ICorrelatedEvent> events, CancellationToken cancellationToken = default)
{
var result = new Dictionary<string, long>();
foreach (var @event in events)
{
var sequence = await AppendAsync(@event, cancellationToken);
result[@event.EventId] = sequence;
}
return result;
}
public Task<List<StoredEvent>> GetEventsAsync(
string correlationId,
long afterSequence = 0,
HashSet<string>? eventTypes = null,
CancellationToken cancellationToken = default)
{
var query = _events.Values
.Where(e => e.CorrelationId == correlationId)
.Where(e => e.Sequence > afterSequence);
if (eventTypes != null && eventTypes.Count > 0)
{
query = query.Where(e => eventTypes.Contains(e.EventType));
}
var result = query
.OrderBy(e => e.Sequence)
.ToList();
return Task.FromResult(result);
}
public Task<StoredEvent?> GetEventByIdAsync(string eventId, CancellationToken cancellationToken = default)
{
_events.TryGetValue(eventId, out var storedEvent);
return Task.FromResult(storedEvent);
}
public Task<int> DeleteOldEventsAsync(DateTimeOffset olderThan, CancellationToken cancellationToken = default)
{
var toDelete = _events.Values
.Where(e => e.StoredAt < olderThan)
.Select(e => e.EventId)
.ToList();
int deletedCount = 0;
foreach (var eventId in toDelete)
{
if (_events.TryRemove(eventId, out _))
{
deletedCount++;
}
}
return Task.FromResult(deletedCount);
}
}