public async Task ExecuteInstanceWithQueuedSnapshot() { var ssMock = new Mock <ISnapshotStore>(MockBehavior.Strict); ssMock.Setup(s => s.SaveSnapshots(It.IsAny <IList <DomainObjectSnapshot> >())) .ReturnsAsync(Result.Success) .Verifiable("SaveSnapshots was never called"); var provider = new ServiceCollection() .AddLogging() .AddSingleton <IncrementalSnapshotService>() .AddSingleton(ssMock.Object) .BuildServiceProvider(); var instance = provider.GetRequiredService <IncrementalSnapshotService>(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); await instance.StartAsync(cts.Token); IncrementalSnapshotService.QueueSnapshot(new DomainObjectSnapshot("Test-Snapshot", "Snapshot1", "{}", 4711, 4711)); await Task.Delay(TimeSpan.FromSeconds(4), cts.Token); await instance.StopAsync(cts.Token); ssMock.Verify(); }
private async Task <IResult <T> > ReplayObjectInternal <T>(T domainObject, string identifier, long maxVersion, string cacheKey) where T : DomainObject { try { if (TryGetCachedItem(cacheKey, maxVersion, out T cachedInstance)) { domainObject = cachedInstance; } else { await ApplyLatestSnapshot(domainObject, identifier, maxVersion, cacheKey); } _logger.LogDebug($"replaying events for '{identifier}', '{domainObject.MetaVersion}' => '{maxVersion}'"); // if the target-object is in any way streamed we save a snapshot of it if ((await StreamObjectToVersion(domainObject, maxVersion, identifier)).Any()) { IncrementalSnapshotService.QueueSnapshot(domainObject.CreateSnapshot()); } var size = domainObject.CalculateCacheSize(); var priority = domainObject.GetCacheItemPriority(); var cacheDuration = GetCacheTime(); _logger.LogInformation($"item cached: duration={cacheDuration:g}; priority={priority}; size={size}; key={cacheKey}"); var cts = new CancellationTokenSource(cacheDuration); (CancellationTokenSource, ILogger <DomainObjectStore>)callbackParams = (cts, _logger); _memoryCache.Set(cacheKey, domainObject, new MemoryCacheEntryOptions() .SetPriority(priority) .SetSize(size) .AddExpirationToken(new CancellationChangeToken(cts.Token)) .RegisterPostEvictionCallback((key, value, reason, state) => { var(tokenSource, logger) = ((CancellationTokenSource, ILogger <DomainObjectStore>))state; tokenSource?.Dispose(); logger?.LogInformation($"cached item '{key}' evicted: {reason}"); }, callbackParams)); return(Result.Success(domainObject)); } catch (Exception e) { _logger.LogWarning(e, $"failed to retrieve {typeof(T).Name} from EventStore (" + $"{nameof(identifier)}: {identifier}, " + $"{nameof(maxVersion)}: {maxVersion}, " + $"{nameof(cacheKey)}: {cacheKey})"); return(Result.Error <T>($"failed to retrieve {typeof(T).Name} from EventStore", ErrorCode.FailedToRetrieveItem)); } }
/// <summary> /// write the recorded events to the given <see cref="IEventStore" /> /// </summary> /// <param name="store"></param> /// <returns></returns> public virtual async Task <IResult> WriteRecordedEvents(IEventStore store) { try { // if we don't have anything to store, we return immediately if (!CapturedDomainEvents.Any()) { return(Result.Success()); } // take lock and see if another instance may already drain this queue lock (_eventLock) { if (_eventsBeingDrained) { return(Result.Success()); } _eventsBeingDrained = true; } MetaVersion = CurrentVersion = await store.WriteEvents(CapturedDomainEvents); CapturedDomainEvents.Clear(); IncrementalSnapshotService.QueueSnapshot(CreateSnapshot()); return(Result.Success()); } catch (Exception e) { return(Result.Error($"could not write events to IEventStore: {e.Message}", ErrorCode.Undefined)); } finally { lock (_eventLock) { _eventsBeingDrained = false; } } }
public void QueueNullSnapshot() { IncrementalSnapshotService.QueueSnapshot(null); }
public void QueueSnapshot() { IncrementalSnapshotService.QueueSnapshot(new DomainObjectSnapshot("Test-Snapshot", "Snapshot1", "{}", 4711, 4711)); }