public virtual async Task CanSendDelayedMessage() { const int numConcurrentMessages = 10000; var messageBus = GetMessageBus(); if (messageBus == null) return; using (messageBus) { var countdown = new AsyncCountdownEvent(numConcurrentMessages); messageBus.Subscribe<SimpleMessageA>(msg => { Logger.Trace().Message("Got message").Write(); Assert.Equal("Hello", msg.Data); countdown.Signal(); Logger.Trace().Message("Set event").Write(); }); var sw = Stopwatch.StartNew(); await Run.InParallel(numConcurrentMessages, async i => { await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" }, TimeSpan.FromMilliseconds(RandomData.GetInt(0, 300))); Logger.Trace().Message("Published one...").Write(); }); await countdown.WaitAsync(TimeSpan.FromSeconds(2)); sw.Stop(); Assert.True(sw.Elapsed > TimeSpan.FromMilliseconds(80)); } }
public async Task SearchByQuery() { var identity = IdentityGenerator.Default; var result = await _identityRepository.AddAsync(identity); Assert.Equal(identity, result); await _client.RefreshAsync(); var results = await _identityRepository.SearchAsync(null, "id:test"); Assert.Equal(0, results.Documents.Count); var disposables = new List<IDisposable>(1); var countdownEvent = new AsyncCountdownEvent(1); try { var filter = $"id:{identity.Id}"; disposables.Add(_identityRepository.BeforeQuery.AddSyncHandler((o, args) => { Assert.Equal(filter, ((ElasticQuery)args.Query).Filter); countdownEvent.Signal(); })); results = await _identityRepository.SearchAsync(null, filter); Assert.Equal(1, results.Documents.Count); await countdownEvent.WaitAsync(new CancellationTokenSource(TimeSpan.FromMilliseconds(250)).Token); Assert.Equal(0, countdownEvent.CurrentCount); } finally { foreach (var disposable in disposables) disposable.Dispose(); disposables.Clear(); } }
public async Task Add() { var identity1 = await _identityRepository.AddAsync(IdentityGenerator.Generate()); Assert.NotNull(identity1?.Id); var disposables = new List<IDisposable>(2); var countdownEvent = new AsyncCountdownEvent(2); try { var identity2 = IdentityGenerator.Default; disposables.Add(_identityRepository.DocumentsAdding.AddSyncHandler((o, args) => { Assert.Equal(identity2, args.Documents.First()); countdownEvent.Signal(); })); disposables.Add(_identityRepository.DocumentsAdded.AddSyncHandler((o, args) => { Assert.Equal(identity2, args.Documents.First()); countdownEvent.Signal(); })); var result = await _identityRepository.AddAsync(identity2); Assert.Equal(IdentityGenerator.Default.Id, result.Id); await countdownEvent.WaitAsync(new CancellationTokenSource(TimeSpan.FromMilliseconds(250)).Token); Assert.Equal(0, countdownEvent.CurrentCount); } finally { foreach (var disposable in disposables) disposable.Dispose(); disposables.Clear(); } }
public void WaitAsync_Unset_IsNotCompleted() { AsyncContext.Run(async () => { var ce = new AsyncCountdownEvent(1); var task = ce.WaitAsync(); Assert.AreEqual(1, ce.CurrentCount); await AssertEx.NeverCompletesAsync(task); }); }
public void WaitAsync_AfterCountingDown_IsCompleted() { var ce = new AsyncCountdownEvent(2); Assert.AreEqual(2, ce.CurrentCount); ce.Signal(); var task = ce.WaitAsync(); Assert.AreEqual(1, ce.CurrentCount); Assert.IsFalse(task.IsCompleted); ce.Signal(); Assert.AreEqual(0, ce.CurrentCount); Assert.IsTrue(task.IsCompleted); }
private BackgroundTaskManager() { // Start the count at 1 and decrement it when ASP.NET notifies us we're shutting down. shutdown = new CancellationTokenSource(); count = new AsyncCountdownEvent(1); shutdown.Token.Register(() => count.Signal(), useSynchronizationContext: false); // Register the object and unregister it when the count reaches zero. HostingEnvironment.RegisterObject(this); done = count.WaitAsync() .ContinueWith(_ => HostingEnvironment.UnregisterObject(this), TaskContinuationOptions.ExecuteSynchronously); }
public async Task CanRun() { var countdown = new AsyncCountdownEvent(1); Func<Task<DateTime?>> callback = () => { countdown.Signal(); return null; }; using (var timer = new ScheduledTimer(callback, loggerFactory: Log)) { timer.ScheduleNext(); await countdown.WaitAsync(TimeSpan.FromMilliseconds(100)); Assert.Equal(0, countdown.CurrentCount); } }
public async Task CanRunWithMinimumInterval() { var countdown = new AsyncCountdownEvent(2); Func<Task<DateTime?>> callback = async () => { _logger.Info("Starting work."); countdown.Signal(); await SystemClock.SleepAsync(500); _logger.Info("Finished work."); return null; }; using (var timer = new ScheduledTimer(callback, minimumIntervalTime: TimeSpan.FromMilliseconds(100), loggerFactory: Log)) { for (int i = 0; i < 4; i++) { timer.ScheduleNext(); SystemClock.Sleep(1); } await countdown.WaitAsync(TimeSpan.FromMilliseconds(100)); Assert.Equal(1, countdown.CurrentCount); await countdown.WaitAsync(TimeSpan.FromSeconds(1.5)); Assert.Equal(0, countdown.CurrentCount); } }
public void AddCount_IncrementsCount() { var ce = new AsyncCountdownEvent(1); var task = ce.WaitAsync(); Assert.AreEqual(1, ce.CurrentCount); Assert.IsFalse(task.IsCompleted); ce.AddCount(); Assert.AreEqual(2, ce.CurrentCount); Assert.IsFalse(task.IsCompleted); ce.Signal(); Assert.AreEqual(1, ce.CurrentCount); Assert.IsFalse(task.IsCompleted); ce.Signal(); Assert.AreEqual(0, ce.CurrentCount); Assert.IsTrue(task.IsCompleted); }
public virtual async Task CanReceiveFromMultipleSubscribers() { var messageBus1 = GetMessageBus(); if (messageBus1 == null) return; using (messageBus1) { var countdown1 = new AsyncCountdownEvent(1); messageBus1.Subscribe<SimpleMessageA>(msg => { Assert.Equal("Hello", msg.Data); countdown1.Signal(); }); using (var messageBus2 = GetMessageBus()) { var countdown2 = new AsyncCountdownEvent(1); messageBus2.Subscribe<SimpleMessageA>(msg => { Assert.Equal("Hello", msg.Data); countdown2.Signal(); }); await messageBus1.PublishAsync(new SimpleMessageA { Data = "Hello" }); await countdown1.WaitAsync(TimeSpan.FromSeconds(2)); Assert.Equal(0, countdown1.CurrentCount); await countdown2.WaitAsync(TimeSpan.FromSeconds(2)); Assert.Equal(0, countdown2.CurrentCount); } } }
public virtual async Task CanCancelSubscription() { var messageBus = GetMessageBus(); if (messageBus == null) return; using (messageBus) { var countdown = new AsyncCountdownEvent(2); long messageCount = 0; var cancellationTokenSource = new CancellationTokenSource(); messageBus.Subscribe<SimpleMessageA>(msg => { _logger.Trace("SimpleAMessage received"); Interlocked.Increment(ref messageCount); cancellationTokenSource.Cancel(); countdown.Signal(); }, cancellationTokenSource.Token); messageBus.Subscribe<object>(msg => countdown.Signal()); await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" }); await countdown.WaitAsync(TimeSpan.FromSeconds(2)); Assert.Equal(0, countdown.CurrentCount); Assert.Equal(1, messageCount); countdown = new AsyncCountdownEvent(1); await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" }); await countdown.WaitAsync(TimeSpan.FromSeconds(2)); Assert.Equal(0, countdown.CurrentCount); Assert.Equal(1, messageCount); } }
public virtual async Task CanSubscribeToAllMessageTypes() { var messageBus = GetMessageBus(); if (messageBus == null) return; using (messageBus) { var countdown = new AsyncCountdownEvent(3); messageBus.Subscribe<object>(msg => { countdown.Signal(); }); await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" }); await messageBus.PublishAsync(new SimpleMessageB { Data = "Hello" }); await messageBus.PublishAsync(new SimpleMessageC { Data = "Hello" }); await countdown.WaitAsync(TimeSpan.FromSeconds(2)); Assert.Equal(0, countdown.CurrentCount); } }
public virtual async Task WillReceiveDerivedMessageTypes() { var messageBus = GetMessageBus(); if (messageBus == null) return; using (messageBus) { var countdown = new AsyncCountdownEvent(2); messageBus.Subscribe<ISimpleMessage>(msg => { Assert.Equal("Hello", msg.Data); countdown.Signal(); }); await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" }); await messageBus.PublishAsync(new SimpleMessageB { Data = "Hello" }); await messageBus.PublishAsync(new SimpleMessageC { Data = "Hello" }); await countdown.WaitAsync(TimeSpan.FromSeconds(5)); Assert.Equal(0, countdown.CurrentCount); } }
public virtual async Task WillExpireRemoteItems() { Logger.Trace().Message("Warm the log...").Write(); var firstCache = GetCacheClient() as HybridCacheClient; Assert.NotNull(firstCache); var secondCache = GetCacheClient() as HybridCacheClient; Assert.NotNull(secondCache); var countdownEvent = new AsyncCountdownEvent(2); firstCache.LocalCache.ItemExpired.AddSyncHandler((sender, args) => { _writer.WriteLine("First expired: " + args.Key); countdownEvent.Signal(); }); secondCache.LocalCache.ItemExpired.AddSyncHandler((sender, args) => { _writer.WriteLine("Second expired: " + args.Key); countdownEvent.Signal(); }); var cacheKey = "willexpireremote"; _writer.WriteLine("First Set"); await firstCache.SetAsync(cacheKey, new SimpleModel { Data1 = "test" }, TimeSpan.FromMilliseconds(150)); _writer.WriteLine("Done First Set"); Assert.Equal(1, firstCache.LocalCache.Count); Assert.Equal(0, secondCache.LocalCache.Count); Assert.Equal(0, firstCache.LocalCacheHits); _writer.WriteLine("First Get"); Assert.True((await firstCache.GetAsync<SimpleModel>(cacheKey)).HasValue); Assert.Equal(1, firstCache.LocalCacheHits); _writer.WriteLine("Second Get"); Assert.True((await secondCache.GetAsync<SimpleModel>(cacheKey)).HasValue); Assert.Equal(0, secondCache.LocalCacheHits); Assert.Equal(1, secondCache.LocalCache.Count); _writer.WriteLine("Second Get from local cache"); Assert.True((await secondCache.GetAsync<SimpleModel>(cacheKey)).HasValue); Assert.Equal(1, secondCache.LocalCacheHits); var sw = Stopwatch.StartNew(); await countdownEvent.WaitAsync(new CancellationTokenSource(500).Token); sw.Stop(); Trace.WriteLine(sw.Elapsed); Assert.Equal(0, firstCache.LocalCache.Count); Assert.Equal(0, secondCache.LocalCache.Count); //Assert.InRange(sw.Elapsed.TotalMilliseconds, 0, 200); }
public async Task CanReindexVersionedIndexWithDeletedDocs() { var version1Index = new VersionedEmployeeIndex(_configuration, 1); await version1Index.DeleteAsync(); var version2Index = new VersionedEmployeeIndex(_configuration, 2); await version2Index.DeleteAsync(); using (new DisposableAction(() => version1Index.DeleteAsync().GetAwaiter().GetResult())) { await version1Index.ConfigureAsync(); Assert.True(_client.IndexExists(version1Index.VersionedName).Exists); var repository = new EmployeeRepository(version1Index.Employee); var employee = await repository.AddAsync(EmployeeGenerator.Default); Assert.NotNull(employee?.Id); await _client.RefreshAsync(); using (new DisposableAction(() => version2Index.DeleteAsync().GetAwaiter().GetResult())) { await version2Index.ConfigureAsync(); Assert.True(_client.IndexExists(version2Index.VersionedName).Exists); Assert.Equal(1, await version2Index.GetCurrentVersionAsync()); // alias should still point to the old version until reindex var aliasResponse = await _client.GetAliasAsync(descriptor => descriptor.Alias(version2Index.Name)); Assert.True(aliasResponse.IsValid); Assert.Equal(1, aliasResponse.Indices.Count); Assert.Equal(version1Index.VersionedName, aliasResponse.Indices.First().Key); var countdown = new AsyncCountdownEvent(1); var reindexTask = version2Index.ReindexAsync((progress, message) => { _logger.Info($"Reindex Progress {progress}%: {message}"); if (progress == 95) { countdown.Signal(); SystemClock.Sleep(1000); } return Task.CompletedTask; }); // Wait until the first reindex pass is done. await countdown.WaitAsync(); Assert.Equal(1, await version1Index.GetCurrentVersionAsync()); await repository.RemoveAllAsync(); await _client.RefreshAsync(); // Resume after everythings been indexed. await reindexTask; aliasResponse = await _client.GetAliasAsync(descriptor => descriptor.Alias(version2Index.Name)); Assert.True(aliasResponse.IsValid, aliasResponse.GetErrorMessage()); Assert.Equal(1, aliasResponse.Indices.Count); Assert.Equal(version2Index.VersionedName, aliasResponse.Indices.First().Key); Assert.Equal(2, await version1Index.GetCurrentVersionAsync()); Assert.Equal(2, await version2Index.GetCurrentVersionAsync()); var countResponse = await _client.CountAsync(d => d.Index(version1Index.VersionedName)); _logger.Trace(() => countResponse.GetRequest()); Assert.True(countResponse.ConnectionStatus.HttpStatusCode == 404, countResponse.GetErrorMessage()); Assert.Equal(0, countResponse.Count); countResponse = await _client.CountAsync(d => d.Index(version2Index.VersionedName)); _logger.Trace(() => countResponse.GetRequest()); Assert.True(countResponse.IsValid, countResponse.GetErrorMessage()); Assert.Equal(1, countResponse.Count); Assert.Equal(employee, await repository.GetByIdAsync(employee.Id)); Assert.False(_client.IndexExists(d => d.Index(version1Index.VersionedName)).Exists); } } }
public async Task AddAndSave() { var log = await _dailyRepository.AddAsync(LogEventGenerator.Default, sendNotification: false); Assert.NotNull(log?.Id); var disposables = new List<IDisposable>(4); var countdownEvent = new AsyncCountdownEvent(5); // Save requires an id to be set. var addedLog = LogEventGenerator.Generate(id: ObjectId.GenerateNewId().ToString()); try { disposables.Add(_dailyRepository.DocumentsAdding.AddSyncHandler((o, args) => { Assert.Equal(addedLog, args.Documents.First()); countdownEvent.Signal(); })); disposables.Add(_dailyRepository.DocumentsAdded.AddSyncHandler((o, args) => { Assert.Equal(addedLog, args.Documents.First()); countdownEvent.Signal(); })); disposables.Add(_dailyRepository.DocumentsSaving.AddSyncHandler((o, args) => { Assert.Equal(log, args.Documents.First().Value); countdownEvent.Signal(); })); disposables.Add(_dailyRepository.DocumentsSaved.AddSyncHandler((o, args) => { Assert.Equal(log, args.Documents.First().Value); countdownEvent.Signal(); })); _messgeBus.Subscribe<EntityChanged>((msg, ct) => { Assert.Equal(nameof(LogEvent), msg.Type); Assert.Equal(log.Id, msg.Id); Assert.Equal(ChangeType.Saved, msg.ChangeType); countdownEvent.Signal(); return Task.CompletedTask; }); log.CompanyId = ObjectId.GenerateNewId().ToString(); await _dailyRepository.SaveAsync(new List<LogEvent> { log, addedLog }); await countdownEvent.WaitAsync(new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token); Assert.Equal(0, countdownEvent.CurrentCount); } finally { foreach (var disposable in disposables) disposable.Dispose(); disposables.Clear(); } }
public virtual async Task CanHaveMultipleQueueInstances() { var queue = GetQueue(retries: 0, retryDelay: TimeSpan.Zero); if (queue == null) return; using (queue) { Logger.Trace().Message("Queue Id: {0}", queue.QueueId).Write(); await queue.DeleteQueueAsync(); const int workItemCount = 50; const int workerCount = 3; var countdown = new AsyncCountdownEvent(workItemCount); var info = new WorkInfo(); var workers = new List<IQueue<SimpleWorkItem>> {queue}; for (int i = 0; i < workerCount; i++) { var q = GetQueue(retries: 0, retryDelay: TimeSpan.Zero); Logger.Trace().Message("Queue Id: {0}, I: {1}", q.QueueId, i).Write(); q.StartWorking(async w => await DoWorkAsync(w, countdown, info)); workers.Add(q); } await Run.InParallel(workItemCount, async i => { var id = await queue.EnqueueAsync(new SimpleWorkItem { Data = "Hello", Id = i }); Logger.Trace().Message("Enqueued Index: {0} Id: {1}", i, id).Write(); }); await countdown.WaitAsync(); await Task.Delay(50); Logger.Trace().Message("Completed: {0} Abandoned: {1} Error: {2}", info.CompletedCount, info.AbandonCount, info.ErrorCount).Write(); Logger.Info().Message($"Work Info Stats: Completed: {info.CompletedCount} Abandoned: {info.AbandonCount} Error: {info.ErrorCount}").Write(); Assert.Equal(workItemCount, info.CompletedCount + info.AbandonCount + info.ErrorCount); // In memory queue doesn't share state. if (queue.GetType() == typeof (InMemoryQueue<SimpleWorkItem>)) { var stats = await queue.GetQueueStatsAsync(); Assert.Equal(0, stats.Working); Assert.Equal(0, stats.Timeouts); Assert.Equal(workItemCount, stats.Enqueued); Assert.Equal(workItemCount, stats.Dequeued); Assert.Equal(info.CompletedCount, stats.Completed); Assert.Equal(info.ErrorCount, stats.Errors); Assert.Equal(info.AbandonCount, stats.Abandoned - info.ErrorCount); Assert.Equal(info.AbandonCount + stats.Errors, stats.Deadletter); } else { var workerStats = new List<QueueStats>(); for (int i = 0; i < workers.Count; i++) { var stats = await workers[i].GetQueueStatsAsync(); Logger.Info().Message($"Worker#{i} Working: {stats.Working} Completed: {stats.Completed} Abandoned: {stats.Abandoned} Error: {stats.Errors} Deadletter: {stats.Deadletter}").Write(); workerStats.Add(stats); } Assert.Equal(info.CompletedCount, workerStats.Sum(s => s.Completed)); Assert.Equal(info.ErrorCount, workerStats.Sum(s => s.Errors)); Assert.Equal(info.AbandonCount, workerStats.Sum(s => s.Abandoned) - info.ErrorCount); Assert.Equal(info.AbandonCount + workerStats.Sum(s => s.Errors), (workerStats.LastOrDefault()?.Deadletter ?? 0)); } workers.ForEach(w => w.Dispose()); } }
public virtual async Task CanSendMessageToMultipleSubscribers() { var messageBus = GetMessageBus(); if (messageBus == null) return; using (messageBus) { var countdown = new AsyncCountdownEvent(3); messageBus.Subscribe<SimpleMessageA>(msg => { Assert.Equal("Hello", msg.Data); countdown.Signal(); }); messageBus.Subscribe<SimpleMessageA>(msg => { Assert.Equal("Hello", msg.Data); countdown.Signal(); }); messageBus.Subscribe<SimpleMessageA>(msg => { Assert.Equal("Hello", msg.Data); countdown.Signal(); }); await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" }); await countdown.WaitAsync(TimeSpan.FromSeconds(2)); } }
public async Task MeasureWorkerThroughput() { var queue = GetQueue(retries: 3, workItemTimeout: TimeSpan.FromSeconds(2), retryDelay: TimeSpan.FromSeconds(1)); if (queue == null) return; using (queue) { await queue.DeleteQueueAsync(); const int workItemCount = 1; for (int i = 0; i < workItemCount; i++) { await queue.EnqueueAsync(new SimpleWorkItem { Data = "Hello" }); } Assert.Equal(workItemCount, (await queue.GetQueueStatsAsync()).Queued); var countdown = new AsyncCountdownEvent(workItemCount); var metrics = new InMemoryMetricsClient(); await queue.StartWorkingAsync(async workItem => { Assert.Equal("Hello", workItem.Value.Data); await workItem.CompleteAsync(); await metrics.CounterAsync("work"); countdown.Signal(); }); await countdown.WaitAsync(TimeSpan.FromMinutes(1)); Assert.Equal(0, countdown.CurrentCount); _logger.Trace((await metrics.GetCounterStatsAsync("work")).ToString()); var stats = await queue.GetQueueStatsAsync(); Assert.Equal(workItemCount, stats.Dequeued); Assert.Equal(workItemCount, stats.Completed); Assert.Equal(0, stats.Queued); _logger.Trace("# Keys: {0}", CountAllKeysAsync()); } }
public async Task Remove() { var log = await _dailyRepository.AddAsync(LogEventGenerator.Default); Assert.NotNull(log?.Id); var disposables = new List<IDisposable>(2); var countdownEvent = new AsyncCountdownEvent(2); try { disposables.Add(_dailyRepository.DocumentsRemoving.AddSyncHandler((o, args) => { Assert.Equal(log, args.Documents.First()); countdownEvent.Signal(); })); disposables.Add(_dailyRepository.DocumentsRemoved.AddSyncHandler((o, args) => { Assert.Equal(log, args.Documents.First()); countdownEvent.Signal(); })); await _dailyRepository.RemoveAsync(log); await countdownEvent.WaitAsync(new CancellationTokenSource(TimeSpan.FromMilliseconds(250)).Token); Assert.Equal(0, countdownEvent.CurrentCount); } finally { foreach (var disposable in disposables) disposable.Dispose(); disposables.Clear(); } }
public async Task RemoveAll() { await _identityRepository.RemoveAllAsync(); var identities = new List<Identity> { IdentityGenerator.Default }; await _identityRepository.AddAsync(identities); await _client.RefreshAsync(); var disposables = new List<IDisposable>(2); var countdownEvent = new AsyncCountdownEvent(2); try { disposables.Add(_identityRepository.DocumentsRemoving.AddSyncHandler((o, args) => { countdownEvent.Signal(); })); disposables.Add(_identityRepository.DocumentsRemoved.AddSyncHandler((o, args) => { countdownEvent.Signal(); })); await _identityRepository.RemoveAllAsync(); await countdownEvent.WaitAsync(new CancellationTokenSource(TimeSpan.FromMilliseconds(250)).Token); Assert.Equal(0, countdownEvent.CurrentCount); } finally { foreach (var disposable in disposables) disposable.Dispose(); disposables.Clear(); } await _client.RefreshAsync(); Assert.Equal(0, await _identityRepository.CountAsync()); }
public virtual async Task CanTolerateSubscriberFailure() { var messageBus = GetMessageBus(); if (messageBus == null) return; using (messageBus) { var countdown = new AsyncCountdownEvent(2); messageBus.Subscribe<SimpleMessageA>(msg => { throw new Exception(); }); messageBus.Subscribe<SimpleMessageA>(msg => { Assert.Equal("Hello", msg.Data); countdown.Signal(); }); messageBus.Subscribe<SimpleMessageA>(msg => { Assert.Equal("Hello", msg.Data); countdown.Signal(); }); await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" }); await countdown.WaitAsync(TimeSpan.FromSeconds(2)); Assert.Equal(0, countdown.CurrentCount); } }
public void Id_EqualsTaskId() { var ce = new AsyncCountdownEvent(1); var task = ce.WaitAsync(); Assert.AreEqual(task.Id, ce.Id); }
public virtual async Task WillExpireRemoteItems() { var countdownEvent = new AsyncCountdownEvent(2); using (var firstCache = GetCacheClient() as HybridCacheClient) { Assert.NotNull(firstCache); Action<object, ItemExpiredEventArgs> expiredHandler = (sender, args) => { _logger.Trace("First expired: {0}", args.Key); countdownEvent.Signal(); }; using (firstCache.LocalCache.ItemExpired.AddSyncHandler(expiredHandler)) { using (var secondCache = GetCacheClient() as HybridCacheClient) { Assert.NotNull(secondCache); Action<object, ItemExpiredEventArgs> expiredHandler2 = (sender, args) => { _logger.Trace("Second expired: {0}", args.Key); countdownEvent.Signal(); }; using (secondCache.LocalCache.ItemExpired.AddSyncHandler(expiredHandler2)) { string cacheKey = "willexpireremote"; _logger.Trace("First Set"); Assert.True(await firstCache.AddAsync(cacheKey, new SimpleModel { Data1 = "test" }, TimeSpan.FromMilliseconds(150))); _logger.Trace("Done First Set"); Assert.Equal(1, firstCache.LocalCache.Count); _logger.Trace("Second Get"); Assert.True((await secondCache.GetAsync<SimpleModel>(cacheKey)).HasValue); _logger.Trace("Done Second Get"); Assert.Equal(1, secondCache.LocalCache.Count); var sw = Stopwatch.StartNew(); await countdownEvent.WaitAsync(TimeSpan.FromMilliseconds(250)); sw.Stop(); _logger.Trace("Time {0}", sw.Elapsed); Assert.Equal(0, countdownEvent.CurrentCount); Assert.Equal(0, firstCache.LocalCache.Count); Assert.Equal(0, secondCache.LocalCache.Count); } } } } }