102 lines
3.0 KiB
C#
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);
|
|
}
|
|
}
|