From 91d56d30142fac20dfd99038785094f8f72e64c6 Mon Sep 17 00:00:00 2001 From: halgari Date: Thu, 11 Jan 2024 14:02:46 -0700 Subject: [PATCH] WIP --- .../TypeAttributeDefinition.cs | 18 ++-- .../DependencyInjectionExtensions.cs | 21 +++++ .../EntityAttribute.cs | 33 ++++++++ .../EntityDefinition.cs | 11 +++ .../EntityStructureRegistry.cs | 47 +++++++++++ .../IEventStore.cs | 21 ++++- .../ISnapshotEventIngester.cs | 14 ++++ .../RocksDBEventStore.cs | 45 +++------- .../EntityDefinitionSerializer.cs | 47 +++++++++++ .../Serialization/SerializationRegistry.cs | 43 ++++++++++ .../InMemoryEventStore.cs | 84 +++++++++++++++++-- .../Model/Collection.cs | 1 + .../Model/Loadout.cs | 1 + .../Model/LoadoutRegistry.cs | 1 + .../Model/Mod.cs | 1 + .../Services.cs | 17 ++-- .../AEventStoreTest.cs | 33 -------- 17 files changed, 351 insertions(+), 87 deletions(-) create mode 100644 src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs create mode 100644 src/NexusMods.EventSourcing.Abstractions/EntityDefinition.cs create mode 100644 src/NexusMods.EventSourcing.Abstractions/ISnapshotEventIngester.cs create mode 100644 src/NexusMods.EventSourcing/Serialization/EntityDefinitionSerializer.cs diff --git a/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs index 319412ff..d568637b 100644 --- a/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs +++ b/src/NexusMods.EventSourcing.Abstractions/AttributeDefinitions/TypeAttributeDefinition.cs @@ -1,5 +1,8 @@ using System; +using System.Buffers; +using System.Buffers.Binary; using System.Diagnostics; +using NexusMods.EventSourcing.Abstractions.Serialization; namespace NexusMods.EventSourcing.Abstractions.AttributeDefinitions; @@ -7,12 +10,12 @@ namespace NexusMods.EventSourcing.Abstractions.AttributeDefinitions; /// An attribute for the type of an entity. /// /// -public class TypeAttributeDefinition : IAttribute> +public class TypeAttributeDefinition : IAttribute> { /// - public ScalarAccumulator CreateAccumulator() + public ScalarAccumulator CreateAccumulator() { - return new ScalarAccumulator(); + return new ScalarAccumulator(); } /// @@ -31,9 +34,9 @@ public ScalarAccumulator CreateAccumulator() public Type Get(TCtx context, EntityId owner) where TCtx : IEntityContext { EntityStructureRegistry.Register(this); - if (context.GetReadOnlyAccumulator>( + if (context.GetReadOnlyAccumulator>( new EntityId(owner), this, out var accumulator)) - return accumulator.Value; + return accumulator.Value.Type; // TODO, make this a custom exception and extract it to another method throw new InvalidOperationException("No type attribute found for entity"); } @@ -50,7 +53,8 @@ public void New(TEventCtx context, EntityId id) where TEventCtx : IEventContext where TType : IEntity { - if (context.GetAccumulator>(EntityId.From(id.Value.Value), this, out var accumulator)) - accumulator.Value = typeof(TType); + var definition = EntityStructureRegistry.GetDefinition(); + if (context.GetAccumulator>(EntityId.From(id.Value.Value), IEntity.TypeAttribute, out var accumulator)) + accumulator.Value = definition; } } diff --git a/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs b/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs index 35e71bce..f154dea4 100644 --- a/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs +++ b/src/NexusMods.EventSourcing.Abstractions/DependencyInjectionExtensions.cs @@ -33,4 +33,25 @@ public static IServiceCollection AddEvent(this IServiceCollection collection) return collection; } + + /// + /// Registers an entity with the service collection, this is required for loading snapshots and properly + /// tracking entity revisions in the application + /// + /// + /// + /// + /// + public static IServiceCollection AddEntity(this IServiceCollection collection) where T : class, IEntity + { + var type = typeof(T); + var attribute = type.GetCustomAttribute(); + if (attribute is null) + { + throw new ArgumentException($"Entity type {type.Name} does not have an EntityAttribute."); + } + EntityStructureRegistry.Register(new EntityDefinition(type, attribute.UUID, attribute.Revision)); + return collection; + } + } diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs b/src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs new file mode 100644 index 00000000..b1624097 --- /dev/null +++ b/src/NexusMods.EventSourcing.Abstractions/EntityAttribute.cs @@ -0,0 +1,33 @@ +using System; +using System.Buffers.Binary; + +namespace NexusMods.EventSourcing.Abstractions; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +public class EntityAttribute : Attribute +{ + /// + /// Defines the UID of the entity, and what revision it is. Incrementing + /// the revision will cause the entity snapshots to be discarded, and regenerated + /// during the next load. + /// + /// + /// + public EntityAttribute(string guid, ushort revision) + { + Span span = stackalloc byte[16]; + Guid.Parse(guid).TryWriteBytes(span); + UUID = BinaryPrimitives.ReadUInt128BigEndian(span); + Revision = revision; + } + + /// + /// The revision of the entity *Type*. + /// + public ushort Revision { get; } + + /// + /// The unique identifier of the entity *Type*. + /// + public UInt128 UUID { get; } +} diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityDefinition.cs b/src/NexusMods.EventSourcing.Abstractions/EntityDefinition.cs new file mode 100644 index 00000000..071c1e4f --- /dev/null +++ b/src/NexusMods.EventSourcing.Abstractions/EntityDefinition.cs @@ -0,0 +1,11 @@ +using System; + +namespace NexusMods.EventSourcing.Abstractions; + +/// +/// Records the entity type, UUID and revision, for use in the DI container. +/// +/// +/// +/// +public record EntityDefinition(Type Type, UInt128 UUID, ushort Revision); diff --git a/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs b/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs index 9052b358..87e2eec1 100644 --- a/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs +++ b/src/NexusMods.EventSourcing.Abstractions/EntityStructureRegistry.cs @@ -13,6 +13,10 @@ public static class EntityStructureRegistry { private static readonly ConcurrentDictionary> _entityStructures = new(); + + private static readonly ConcurrentDictionary _entityDefinitionsByType = new(); + private static readonly ConcurrentDictionary _entityDefinitionsByUUID = new(); + /// /// Register an attribute in the global registry. /// @@ -34,6 +38,18 @@ public static void Register(IAttribute attribute) } } + /// + /// Registers an entity type in the global registry. + /// + /// + /// + /// + public static void Register(EntityDefinition definition) + { + _entityDefinitionsByType.TryAdd(definition.Type, definition); + _entityDefinitionsByUUID.TryAdd(definition.UUID, definition); + } + /// /// Returns all attributes for the given entity type. /// @@ -51,4 +67,35 @@ public static bool TryGetAttributes(Type owner, [NotNullWhen(true)] out Concurre return false; } + /// + /// Gets the entity definition for the given C# type. + /// + /// + /// + /// + public static EntityDefinition GetDefinition() where TType : IEntity + { + if (_entityDefinitionsByType.TryGetValue(typeof(TType), out var result)) + { + return result; + } + + throw new InvalidOperationException($"No entity definition found for type {typeof(TType).Name}"); + } + + /// + /// Gets the entity definition for the given UUID. + /// + /// + /// + /// + public static EntityDefinition GetDefinitionByUUID(UInt128 uuid) + { + if (_entityDefinitionsByUUID.TryGetValue(uuid, out var result)) + { + return result; + } + + throw new InvalidOperationException($"No entity definition found for UUID {uuid}"); + } } diff --git a/src/NexusMods.EventSourcing.Abstractions/IEventStore.cs b/src/NexusMods.EventSourcing.Abstractions/IEventStore.cs index 23a627de..d59e35a7 100644 --- a/src/NexusMods.EventSourcing.Abstractions/IEventStore.cs +++ b/src/NexusMods.EventSourcing.Abstractions/IEventStore.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; namespace NexusMods.EventSourcing.Abstractions; @@ -22,6 +23,24 @@ public interface IEventStore /// The ingester to handle the events /// If true, plays the events in reverse /// - public void EventsForEntity(EntityId entityId, TIngester ingester, bool reverse = false) + public void EventsForEntity(EntityId entityId, TIngester ingester) where TIngester : IEventIngester; + + /// + /// Replays the most recent snapshot for the given entity id, if one exists, then + /// replays every event. + /// + /// + /// + /// + public void EventsAndSnapshotForEntity(EntityId entityId, TIngester ingester) + where TIngester : ISnapshotEventIngester; + + /// + /// Sets the snapshot for the given entity id and transaction id. + /// + /// + /// + /// + public void SetSnapshot(TransactionId txId, EntityId id, IEnumerable<(string AttributeName, IAttribute attribute)> attributes); } diff --git a/src/NexusMods.EventSourcing.Abstractions/ISnapshotEventIngester.cs b/src/NexusMods.EventSourcing.Abstractions/ISnapshotEventIngester.cs new file mode 100644 index 00000000..768fbb88 --- /dev/null +++ b/src/NexusMods.EventSourcing.Abstractions/ISnapshotEventIngester.cs @@ -0,0 +1,14 @@ +namespace NexusMods.EventSourcing.Abstractions; + +/// +/// An event ingester that supports entity snapshots +/// +public interface ISnapshotEventIngester : IEventIngester +{ + /// + /// This method will be called for each attribute snapshotted, before the normal event ingestion is called + /// + /// + /// + public void IngestSnapshotAttribute(string attributeName, IAttribute attribute); +} diff --git a/src/NexusMods.EventSourcing.RocksDB/RocksDBEventStore.cs b/src/NexusMods.EventSourcing.RocksDB/RocksDBEventStore.cs index fb4e801e..5ff93711 100644 --- a/src/NexusMods.EventSourcing.RocksDB/RocksDBEventStore.cs +++ b/src/NexusMods.EventSourcing.RocksDB/RocksDBEventStore.cs @@ -66,7 +66,7 @@ public TransactionId Add(T eventValue) where T : IEvent } } - public void EventsForEntity(EntityId entityId, TIngester ingester, bool reverse = false) where TIngester : IEventIngester + public void EventsForEntity(EntityId entityId, TIngester ingester) where TIngester : IEventIngester { Span startKey = stackalloc byte[24]; entityId.TryWriteBytes(startKey); @@ -81,42 +81,19 @@ public void EventsForEntity(EntityId entityId, TIngester ingester, bo { fixed (byte* endKeyPtr = endKey) { - if (!reverse) - { - - options.SetIterateUpperBound(endKeyPtr, 24); - options.SetIterateLowerBound(startKeyPtr, 24); - using var iterator = _db.NewIterator(_entityIndexColumn, options); + options.SetIterateUpperBound(endKeyPtr, 24); + options.SetIterateLowerBound(startKeyPtr, 24); + using var iterator = _db.NewIterator(_entityIndexColumn, options); - iterator.SeekToFirst(); - while (iterator.Valid()) - { - var key = iterator.GetKeySpan(); - var txId = TransactionId.From(key); - var evt = _db.Get(key[16..], _deserializer, _eventsColumn); - if (!ingester.Ingest(txId, evt)) break; - iterator.Next(); - } - } - else + iterator.SeekToFirst(); + while (iterator.Valid()) { - options.SetIterateUpperBound(endKeyPtr, 24); - options.SetIterateLowerBound(startKeyPtr, 24); - using var iterator = _db.NewIterator(_entityIndexColumn, options); - - iterator.SeekToLast(); - while (iterator.Valid()) - { - var key = iterator.GetKeySpan(); - var keySpan = key.SliceFast(16); - var txId = TransactionId.From(keySpan); - var evt = _db.Get(keySpan, _deserializer, _eventsColumn); - if (!ingester.Ingest(txId, evt)) break; - iterator.Prev(); - } - + var key = iterator.GetKeySpan(); + var txId = TransactionId.From(key); + var evt = _db.Get(key[16..], _deserializer, _eventsColumn); + if (!ingester.Ingest(txId, evt)) break; + iterator.Next(); } - } } } diff --git a/src/NexusMods.EventSourcing/Serialization/EntityDefinitionSerializer.cs b/src/NexusMods.EventSourcing/Serialization/EntityDefinitionSerializer.cs new file mode 100644 index 00000000..5c3ae390 --- /dev/null +++ b/src/NexusMods.EventSourcing/Serialization/EntityDefinitionSerializer.cs @@ -0,0 +1,47 @@ +using System; +using System.Buffers.Binary; +using NexusMods.EventSourcing.Abstractions; +using NexusMods.EventSourcing.Abstractions.Serialization; +using Reloaded.Memory.Extensions; + +namespace NexusMods.EventSourcing.Serialization; + +/// +/// Serializes an entity definition, while intentionally not serializing the type of the entity. +/// +public sealed class EntityDefinitionSerializer : IFixedSizeSerializer +{ + /// + public bool CanSerialize(Type valueType) + { + return valueType == typeof(EntityDefinition); + } + + /// + public bool TryGetFixedSize(Type valueType, out int size) + { + size = 16 + sizeof(ushort); + return true; + } + + /// + public void Serialize(EntityDefinition value, Span output) + { + BinaryPrimitives.WriteUInt128BigEndian(output, value.UUID); + BinaryPrimitives.WriteUInt16BigEndian(output.SliceFast(16), value.Revision); + } + + /// + public EntityDefinition Deserialize(ReadOnlySpan from) + { + var uuid = BinaryPrimitives.ReadUInt128BigEndian(from); + var revision = BinaryPrimitives.ReadUInt16BigEndian(from.SliceFast(16)); + + var existing = EntityStructureRegistry.GetDefinitionByUUID(uuid); + + if (existing.Revision != revision) + return new EntityDefinition(existing.Type, uuid, revision); + + return existing; + } +} diff --git a/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs b/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs index ff57678b..b3b448a2 100644 --- a/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs +++ b/src/NexusMods.EventSourcing/Serialization/SerializationRegistry.cs @@ -1,8 +1,10 @@ using System; +using System.Buffers; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using NexusMods.EventSourcing.Abstractions.Serialization; +using Reloaded.Memory.Extensions; namespace NexusMods.EventSourcing.Serialization; @@ -79,4 +81,45 @@ public void RegisterSerializer(Type serializedType, ISerializer serializer) { _cachedSerializers.TryAdd(serializedType, serializer); } + + + /// + public void Serialize(IBufferWriter writer, TVal value) + { + var serializer = GetSerializer(typeof(TVal)); + if (serializer.TryGetFixedSize(typeof(TVal), out var size) && serializer is IFixedSizeSerializer fixedSizeSerializer) + { + var span = writer.GetSpan(size); + fixedSizeSerializer.Serialize(value, span); + writer.Advance(size); + } + else if (serializer is IVariableSizeSerializer variableSizeSerializer) + { + variableSizeSerializer.Serialize(value, writer); + } + else + { + throw new Exception($"Unknown serializer type {serializer.GetType().Name}"); + } + + } + + /// + public int Deserialize(ReadOnlySpan bytes, out TVal value) + { + var serializer = GetSerializer(typeof(TVal)); + if (serializer.TryGetFixedSize(typeof(TVal), out var size) && serializer is IFixedSizeSerializer fixedSizeSerializer) + { + value = fixedSizeSerializer.Deserialize(bytes.SliceFast(0, size)); + return size; + } + else if (serializer is IVariableSizeSerializer variableSizeSerializer) + { + return variableSizeSerializer.Deserialize(bytes, out value); + } + else + { + throw new Exception($"Unknown serializer type {serializer.GetType().Name}"); + } + } } diff --git a/tests/NexusMods.EventSourcing.TestModel/InMemoryEventStore.cs b/tests/NexusMods.EventSourcing.TestModel/InMemoryEventStore.cs index adfa698a..6214273f 100644 --- a/tests/NexusMods.EventSourcing.TestModel/InMemoryEventStore.cs +++ b/tests/NexusMods.EventSourcing.TestModel/InMemoryEventStore.cs @@ -1,20 +1,35 @@ using System.Buffers; using NexusMods.EventSourcing.Abstractions; +using NexusMods.EventSourcing.Abstractions.Serialization; +using Reloaded.Memory.Extensions; namespace NexusMods.EventSourcing.TestModel; -public class InMemoryEventStore(TSerializer serializer) : IEventStore +public class InMemoryEventStore : IEventStore where TSerializer : IEventSerializer { private TransactionId _tx = TransactionId.From(0); private readonly Dictionary> _events = new(); + private readonly Dictionary> _snapshots = new(); + private TSerializer _serializer; + private readonly IVariableSizeSerializer _stringSerializer; + private readonly PooledMemoryBufferWriter _writer; + private readonly ISerializationRegistry _serializationRegistry; + + public InMemoryEventStore(TSerializer serializer, ISerializationRegistry serializationRegistry) + { + _serializer = serializer; + _stringSerializer = (serializationRegistry.GetSerializer(typeof(string)) as IVariableSizeSerializer)!; + _serializationRegistry = serializationRegistry; + _writer = new PooledMemoryBufferWriter(); + } public TransactionId Add(T entity) where T : IEvent { lock (this) { _tx = _tx.Next(); - var data = serializer.Serialize(entity); + var data = _serializer.Serialize(entity); var logger = new ModifiedEntitiesIngester(); entity.Apply(logger); foreach (var id in logger.Entities) @@ -33,19 +48,76 @@ public TransactionId Add(T entity) where T : IEvent } - public void EventsForEntity(EntityId entityId, TIngester ingester, bool reverse = false) + public void EventsForEntity(EntityId entityId, TIngester ingester) where TIngester : IEventIngester { if (!_events.TryGetValue(entityId, out var events)) return; - if (reverse) - events = events.Reverse().ToList(); + foreach (var data in events) + { + var @event = _serializer.Deserialize(data.Data)!; + if (!ingester.Ingest(data.TxId, @event)) break; + } + } + + public void EventsAndSnapshotForEntity(TransactionId asOf, EntityId entityId, TIngester ingester) where TIngester : ISnapshotEventIngester + { + if (!_snapshots.TryGetValue(entityId, out var snapshots)) + return; + + var startPoint = snapshots.LastOrDefault(s => s.Key <= asOf); + + if (startPoint != default) + { + var snapshot = startPoint.Value.AsSpanFast(); + + while (snapshot.Length > 0) + { + var attributeName = _stringSerializer.Deserialize(snapshot, out var read); + snapshot = snapshot.SliceFast(read); + var accumulator = _serializationRegistry.GetAccumulator(attributeName); + accumulator.ReadFrom(snapshot, _serializationRegistry, out read); + snapshot = snapshot.Slice(read); + ingester.IngestSnapshot(attributeName, accumulator); + } + + } + + foreach (var (txId, data) in snapshots.) + { + var @event = _serializer.Deserialize(data)!; + if (!ingester.Ingest(txId, @event)) break; + } + + if (!_events.TryGetValue(entityId, out var events)) + return; foreach (var data in events) { - var @event = serializer.Deserialize(data.Data)!; + var @event = _serializer.Deserialize(data.Data)!; if (!ingester.Ingest(data.TxId, @event)) break; } } + + public void SetSnapshot(TransactionId txId, EntityId id, IEnumerable<(string AttributeName, IAccumulator Accumulator)> attributes) + { + _writer.Reset(); + + foreach (var (attributeName, accumulator) in attributes) + { + _stringSerializer.Serialize(attributeName, _writer); + accumulator.WriteTo(_writer, _serializationRegistry); + } + + var span = _writer.GetWrittenSpan(); + + if (!_snapshots.TryGetValue(id, out var snapshots)) + { + snapshots = new SortedDictionary(); + _snapshots.Add(id, snapshots); + } + + snapshots.Add(txId, span.ToArray()); + } } diff --git a/tests/NexusMods.EventSourcing.TestModel/Model/Collection.cs b/tests/NexusMods.EventSourcing.TestModel/Model/Collection.cs index 87933a1a..83d1c6b7 100644 --- a/tests/NexusMods.EventSourcing.TestModel/Model/Collection.cs +++ b/tests/NexusMods.EventSourcing.TestModel/Model/Collection.cs @@ -3,6 +3,7 @@ namespace NexusMods.EventSourcing.TestModel.Model; +[Entity("12055587-81CE-44F7-AF2B-6324B0CBB3C4", 0)] public class Collection : AEntity { public Collection(IEntityContext context, EntityId id) : base(context, id) { } diff --git a/tests/NexusMods.EventSourcing.TestModel/Model/Loadout.cs b/tests/NexusMods.EventSourcing.TestModel/Model/Loadout.cs index de199961..9750a98a 100644 --- a/tests/NexusMods.EventSourcing.TestModel/Model/Loadout.cs +++ b/tests/NexusMods.EventSourcing.TestModel/Model/Loadout.cs @@ -4,6 +4,7 @@ namespace NexusMods.EventSourcing.TestModel.Model; +[Entity("49C0A910-A022-4AB5-9CF8-3F2730A4BA15", 0)] public class Loadout(IEntityContext context, EntityId id) : AEntity(context, id) { /// diff --git a/tests/NexusMods.EventSourcing.TestModel/Model/LoadoutRegistry.cs b/tests/NexusMods.EventSourcing.TestModel/Model/LoadoutRegistry.cs index 824f1142..d98072c7 100644 --- a/tests/NexusMods.EventSourcing.TestModel/Model/LoadoutRegistry.cs +++ b/tests/NexusMods.EventSourcing.TestModel/Model/LoadoutRegistry.cs @@ -4,6 +4,7 @@ namespace NexusMods.EventSourcing.TestModel.Model; +[Entity("4FE4CDC6-7A14-4FBC-BCF4-8B39D93F8EE4", 0)] public class LoadoutRegistry(IEntityContext context) : AEntity(context, SingletonId), ISingletonEntity { /// diff --git a/tests/NexusMods.EventSourcing.TestModel/Model/Mod.cs b/tests/NexusMods.EventSourcing.TestModel/Model/Mod.cs index 0cede3c3..e6fb06a5 100644 --- a/tests/NexusMods.EventSourcing.TestModel/Model/Mod.cs +++ b/tests/NexusMods.EventSourcing.TestModel/Model/Mod.cs @@ -2,6 +2,7 @@ namespace NexusMods.EventSourcing.TestModel.Model; +[Entity("ACB7AF43-4FB2-4E1A-8C32-7CF7D912A911", 0)] public class Mod(IEntityContext context, EntityId id) : AEntity(context, id) { public Loadout Loadout => _loadout.Get(this); diff --git a/tests/NexusMods.EventSourcing.TestModel/Services.cs b/tests/NexusMods.EventSourcing.TestModel/Services.cs index 8854e189..109aaa5a 100644 --- a/tests/NexusMods.EventSourcing.TestModel/Services.cs +++ b/tests/NexusMods.EventSourcing.TestModel/Services.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using NexusMods.EventSourcing.Abstractions; using NexusMods.EventSourcing.Events; +using NexusMods.EventSourcing.TestModel.Model; namespace NexusMods.EventSourcing.TestModel; @@ -13,12 +14,16 @@ public static class Services /// public static IServiceCollection AddEvents(this IServiceCollection coll) { - coll.AddEvent(); - coll.AddEvent(); - coll.AddEvent(); - coll.AddEvent(); - coll.AddEvent(); - coll.AddEvent(); + coll.AddEvent() + .AddEvent() + .AddEvent() + .AddEvent() + .AddEvent() + .AddEvent() + .AddEntity() + .AddEntity() + .AddEntity() + .AddEntity(); return coll; } diff --git a/tests/NexusMods.EventSourcing.Tests/AEventStoreTest.cs b/tests/NexusMods.EventSourcing.Tests/AEventStoreTest.cs index 39bb8942..8d8d31cd 100644 --- a/tests/NexusMods.EventSourcing.Tests/AEventStoreTest.cs +++ b/tests/NexusMods.EventSourcing.Tests/AEventStoreTest.cs @@ -33,39 +33,6 @@ public void CanGetAndReturnEvents() } } - [Fact] - public void CanIterateInReverse() - { - var entityId = EntityId.NewId(); - var txIds = new List<(TransactionId Id, string Name)>(); - - for (var i = 0; i < 1000; i++) - { - var name = $"Test {i}"; - var txid = Store.Add(new RenameLoadout(entityId, name)); - txIds.Add((txid, name)); - } - - var accumulator = new InverseChecker(txIds.ToArray()); - Store.EventsForEntity(entityId.Value, accumulator, true); - - accumulator.Count.Should().Be(txIds.Count); - } - - private class InverseChecker((TransactionId Id, string Name)[] txEs, int count = 0) : IEventIngester - { - public int Count => count; - - public bool Ingest(TransactionId id, IEvent @event) - { - var current = txEs[^(count + 1)]; - count += 1; - id.Should().Be(current.Id); - ((RenameLoadout) @event).Name.Should().Be(current.Name); - return true; - } - } - private class EventAccumulator : IEventIngester { public List Events { get; } = new();