public void Compare_SameInstance_ReturnsTrue() { var config1 = new Confluent.Kafka.ConsumerConfig { BootstrapServers = "myserver", PartitionAssignmentStrategy = Confluent.Kafka.PartitionAssignmentStrategy.Range, EnableAutoCommit = false }; var config2 = config1; _dictionary.TryAdd(config1, null); _dictionary.Should().ContainKey(config2); }
public async Task RunAsync_SubProcessListSupplied_AllAreRun(int processCount) { // arrange var processId = 0; var expectedKeys = Enumerable.Range(1, processCount); var dict = new ConcurrentDictionary <int, DateTime>(); _fixture.Register(() => { var process = A.Fake <IProcess>(); A.CallTo(() => process.RunAsync(CancellationToken.None)).ReturnsLazily(() => { var id = Interlocked.Increment(ref processId); dict.TryAdd(id, DateTime.Now); return(Task.CompletedTask); }); return(process); }); var processes = _fixture.CreateMany <IProcess>(processCount); var sut = new ParallelProcess(processes); // act await sut.RunAsync(CancellationToken.None); // assert dict.Should().ContainKeys(expectedKeys); }
public async Task ShouldRunThreadsWithDistinctKeysInParallel() { // Arrange var currentParallelism = 0; var maxParallelism = 0; var parallelismLock = new object(); var index = new ConcurrentDictionary <string, IKeyedSemaphore>(); using var keyedSemaphores = new KeyedSemaphoresCollection(index); // 100 threads, 100 keys var threads = Enumerable.Range(0, 100) .Select(i => Task.Run(async() => await OccupyTheLockALittleBit(i).ConfigureAwait(false))) .ToList(); // Act await Task.WhenAll(threads).ConfigureAwait(false); maxParallelism.Should().BeGreaterThan(10); index.Should().BeEmpty(); async Task OccupyTheLockALittleBit(int key) { var keyedSemaphore = keyedSemaphores.Provide(key.ToString()); try { await keyedSemaphore.WaitAsync().ConfigureAwait(false); try { var incrementedCurrentParallelism = Interlocked.Increment(ref currentParallelism); lock (parallelismLock) { maxParallelism = Math.Max(incrementedCurrentParallelism, maxParallelism); } const int delay = 250; await Task.Delay(TimeSpan.FromMilliseconds(delay)).ConfigureAwait(false); Interlocked.Decrement(ref currentParallelism); } finally { keyedSemaphore.Release(); } } finally { keyedSemaphore.Dispose(); } } }
public void Analyze_LineHasOneWord_ShouldBeAddedToDict() { var dict = new ConcurrentDictionary <string, int>(); var line = "line"; _lineSplitter.Setup(x => x.Split(line)).Returns(new[] { line }); _sut.Analyze(line, dict); dict.Should().Contain(new KeyValuePair <string, int>(line, 1)); _lineSplitter.Verify(); }
public void Analyze_LineHasFewWords_DictShouldHasCorrectCount() { var dict = new ConcurrentDictionary <string, int>(); var word = "line"; var line = $"{word}{word}{word}"; _lineSplitter.Setup(x => x.Split(line)).Returns(new[] { word, word, word }); _sut.Analyze(line, dict); dict.Should().Contain(new KeyValuePair <string, int>(word, 3)); _lineSplitter.Verify(); }
public async Task An_interval_can_be_specified_before_which_a_released_lease_will_be_granted_again() { var tally = new ConcurrentDictionary <string, int>(); var distributor = CreateDistributor(waitInterval: TimeSpan.FromMilliseconds(500)).Trace(); distributor.OnReceive(async w => { tally.AddOrUpdate(w.Lease.Name, addValueFactory: s => 1, updateValueFactory: (s, v) => v + 1); }); await distributor.Start(); await Task.Delay(100); await distributor.Stop(); tally.Count.Should().Be(10); tally.Should().ContainKeys("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); tally.Should().ContainValues(1, 1, 1, 1, 1, 1, 1, 1, 1, 1); }
public void Learn_should_store_and_log_new_health_when_health_gets_increased_and_there_was_not_old_stored_health() { SetupTuningAction(AdaptiveHealthAction.Increase); implementation.CreateDefaultHealth().Returns(10); implementation.IncreaseHealth(10).Returns(20); modifier.Learn(CreateResult(replica1), storageProvider); storage.Should().Contain(replica1, 20); implementation.Received().LogHealthChange(replica1, 10, 20, log); }
public void Learn_should_correctly_store_new_health(bool?old, bool isLeader, bool?expected) { if (old.HasValue) { storage[replica] = old.Value; } resultDetector.IsLeaderResult(result).Returns(isLeader); modifier.Learn(result, storageProvider); if (expected.HasValue) { storage[replica].Should().Be(expected.Value); } else { storage.Should().NotContainKey(replica); } }
public virtual async Task A_lease_can_be_extended() { var tally = new ConcurrentDictionary <string, int>(); var pool = DateTimeOffset.UtcNow.Ticks.ToString(); var distributor1 = CreateDistributor(pool: pool).Trace(); var distributor2 = CreateDistributor(pool: pool).Trace(); Func <Lease <int>, Task> onReceive = async lease => { tally.AddOrUpdate(lease.Leasable.Name, addValueFactory: s => 1, updateValueFactory: (s, v) => v + 1); if (lease.Leasable.Name == "5") { // extend the lease await lease.Extend(TimeSpan.FromDays(2)); // wait longer than the lease would normally last await Task.Delay((int)(DefaultLeaseDuration.TotalMilliseconds * 5)); } }; distributor1.OnReceive(onReceive); distributor2.OnReceive(onReceive); await distributor1.Start(); await distributor2.Start(); await Task.Delay((int)(DefaultLeaseDuration.TotalMilliseconds * 2.5)); await distributor1.Stop(); await distributor2.Stop(); Console.WriteLine(tally.ToLogString()); tally.Should().ContainKey("5") .And .Subject["5"].Should().Be(1); }
public void Remove_with_value_should_not_remove_anything_when_value_does_not_match() { dictionary.Remove("key1", 2).Should().BeFalse(); dictionary.Should().HaveCount(3); }
public virtual async Task A_lease_can_be_extended() { var tally = new ConcurrentDictionary<string, int>(); var scope = DateTimeOffset.UtcNow.Ticks.ToString(); var distributor1 = CreateDistributor(scope: scope).Trace(); var distributor2 = CreateDistributor(scope: scope).Trace(); Func<Lease, Task> onReceive = async lease => { tally.AddOrUpdate(lease.LeasableResource.Name, addValueFactory: s => 1, updateValueFactory: (s, v) => v + 1); if (lease.LeasableResource.Name == "5") { // extend the lease await lease.Extend(TimeSpan.FromDays(2)); // wait longer than the lease would normally last await Task.Delay((int) (DefaultLeaseDuration.TotalMilliseconds*5)); } }; distributor1.OnReceive(onReceive); distributor2.OnReceive(onReceive); await distributor1.Start(); await distributor2.Start(); await Task.Delay((int) (DefaultLeaseDuration.TotalMilliseconds * 2.5)); await distributor1.Stop(); await distributor2.Stop(); Console.WriteLine(tally.ToLogString()); tally.Should().ContainKey("5") .And .Subject["5"].Should().Be(1); }
public async Task An_interval_can_be_specified_before_which_a_released_lease_will_be_granted_again() { var tally = new ConcurrentDictionary<string, int>(); var distributor = CreateDistributor(waitInterval: TimeSpan.FromMilliseconds(5000)).Trace(); var countdown = new AsyncCountdownEvent(10); distributor.OnReceive(async lease => { tally.AddOrUpdate(lease.LeasableResource.Name, addValueFactory: s => 1, updateValueFactory: (s, v) => v + 1); countdown.Signal(); }); await distributor.Start(); await countdown.WaitAsync().Timeout(); await distributor.Stop(); tally.Count.Should().Be(10); tally.Should().ContainKeys("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); tally.Should().ContainValues(1, 1, 1, 1, 1, 1, 1, 1, 1, 1); }
public async Task ShouldRunThreadsWithSameKeysLinearly() { // Arrange var runningTasksIndex = new ConcurrentDictionary <int, int>(); var parallelismLock = new object(); var currentParallelism = 0; var maxParallelism = 0; var index = new ConcurrentDictionary <string, IKeyedSemaphore>(); using var keyedSemaphores = new KeyedSemaphoresCollection(index); // 100 threads, 10 keys var threads = Enumerable.Range(0, 100) .Select(i => Task.Run(async() => await OccupyTheLockALittleBit(i % 10).ConfigureAwait(false))) .ToList(); // Act + Assert await Task.WhenAll(threads).ConfigureAwait(false); maxParallelism.Should().BeLessOrEqualTo(10); index.Should().BeEmpty(); async Task OccupyTheLockALittleBit(int key) { var keyedSemaphore = keyedSemaphores.Provide(key.ToString()); try { await keyedSemaphore.WaitAsync().ConfigureAwait(false); try { var incrementedCurrentParallelism = Interlocked.Increment(ref currentParallelism); lock (parallelismLock) { maxParallelism = Math.Max(incrementedCurrentParallelism, maxParallelism); } var currentTaskId = Task.CurrentId ?? -1; if (runningTasksIndex.TryGetValue(key, out var otherThread)) { throw new Exception($"Thread #{currentTaskId} acquired a lock using key ${key} " + $"but another thread #{otherThread} is also still running using this key!"); } runningTasksIndex[key] = currentTaskId; const int delay = 10; await Task.Delay(TimeSpan.FromMilliseconds(delay)).ConfigureAwait(false); if (!runningTasksIndex.TryRemove(key, out var value)) { var ex = new Exception($"Thread #{currentTaskId} has finished " + $"but when trying to cleanup the running threads index, the value is already gone"); throw ex; } if (value != currentTaskId) { var ex = new Exception($"Thread #{currentTaskId} has finished and has removed itself from the running threads index," + $" but that index contained an incorrect value: #{value}!"); throw ex; } Interlocked.Decrement(ref currentParallelism); } finally { keyedSemaphore.Release(); } } finally { keyedSemaphore.Dispose(); } } }
public async Task DisposingTheKeyedSemaphoresCollectionShouldInterruptAllThreads() { // Arrange var runningTasksIndex = new ConcurrentDictionary <int, int>(); var parallelismLock = new object(); var currentParallelism = 0; var maxParallelism = 0; var random = new Random(); var index = new ConcurrentDictionary <string, IKeyedSemaphore>(); using var keyedSemaphores = new KeyedSemaphoresCollection(index); // 50 threads, 1 key var numberOfThreads = 50; Log($"Starting {numberOfThreads} threads"); var threads = Enumerable.Range(0, numberOfThreads) .Select(i => Task.Run(async() => await OccupyTheLockALittleBit(i, 1).ConfigureAwait(false))) .ToList(); // Act + Assert await Task.Delay(100); Log($"[WAITING] keyedSemaphores.Dispose"); keyedSemaphores.Dispose(); Log($"[OK] keyedSemaphores.Dispose"); await Task.WhenAll(threads).ConfigureAwait(false); maxParallelism.Should().Be(1); index.Should().BeEmpty(); async Task OccupyTheLockALittleBit(int thread, int key) { var currentTaskId = Task.CurrentId ?? -1; var delay = random.Next(0, 200); await Task.Delay(delay).ConfigureAwait(false); IKeyedSemaphore keyedSemaphore = null; try { try { Log($"[{thread, 2}] [WAITING] KeyedSemaphores.Provide : {key,3}"); keyedSemaphore = keyedSemaphores.Provide(key.ToString()); Log($"[{thread, 2}] [OK] KeyedSemaphores.Provide : {key,3}"); } catch (ObjectDisposedException) { Log($"[{thread, 2}] [DISPOSED]KeyedSemaphores.Provide : {key,3}"); return; } try { Log($"[{thread, 2}] [WAITING] KeyedSemaphores.WaitAsync : {key,3}"); await keyedSemaphore.WaitAsync(); Log($"[{thread, 2}] [OK] KeyedSemaphores.WaitAsync : {key,3}"); } catch (OperationCanceledException) { Log($"[{thread, 2}] [CANCELED]KeyedSemaphores.WaitAsync : {key,3}"); return; } try { var incrementedCurrentParallelism = Interlocked.Increment(ref currentParallelism); lock (parallelismLock) { maxParallelism = Math.Max(incrementedCurrentParallelism, maxParallelism); } if (runningTasksIndex.TryGetValue(key, out var otherThread)) { throw new Exception($"[{thread, 2}] Task [{currentTaskId,3}] has a lock for key ${key} " + $"but another task [{otherThread,3}] also has an active lock for this key!"); } runningTasksIndex[key] = currentTaskId; if (!runningTasksIndex.TryRemove(key, out var value)) { var ex = new Exception($"[{thread, 2}] Task [{currentTaskId,3}] has finished " + $"but when trying to cleanup the running tasks index, the value is already gone"); throw ex; } if (value != currentTaskId) { var ex = new Exception($"[{thread, 2}] Task [{currentTaskId,3}] has finished and has removed itself from the running tasks index," + $" but that index contained a task ID of another task: [{value}]!"); throw ex; } Interlocked.Decrement(ref currentParallelism); } finally { try { Log($"[{thread, 2}] [WAITING] KeyedSemaphore.Release : {key,3}"); keyedSemaphore.Release(); Log($"[{thread, 2}] [OK] KeyedSemaphore.Release : {key,3}"); } catch (ObjectDisposedException e) { Log($"[{thread, 2}] [DISPOSED]KeyedSemaphore.Release : {e}"); } } } finally { if (keyedSemaphore != null) { Log($"[{thread, 2}] [WAITING] KeyedSemaphore.Dispose : {key,3}"); keyedSemaphore.Dispose(); Log($"[{thread, 2}] [OK] KeyedSemaphore.Dispose : {key,3}"); } } } }
public void Constructor_CalledMultipleTimes_ExpectStreamAddsItselfToTheSessionCollectionWithDifferentGuids() { var sessionCollection = new ConcurrentDictionary<Guid, IEventStream>(); using (var firstStream = new NEventStoreSessionStream(sessionCollection, DummyEventStream())) { using (var secondStream = new NEventStoreSessionStream(sessionCollection, DummyEventStream())) { sessionCollection.Should().ContainValues(firstStream, secondStream); } } }
public async Task ShouldNeverCreateTwoSemaphoresForTheSameKey() { // Arrange var runningTasksIndex = new ConcurrentDictionary <int, int>(); var parallelismLock = new object(); var currentParallelism = 0; var maxParallelism = 0; var random = new Random(); var index = new ConcurrentDictionary <string, IKeyedSemaphore>(); using var keyedSemaphores = new KeyedSemaphoresCollection(index); // Many threads, 1 key var threads = Enumerable.Range(0, 100) .Select(i => Task.Run(async() => await OccupyTheLockALittleBit(1).ConfigureAwait(false))) .ToList(); // Act + Assert await Task.WhenAll(threads).ConfigureAwait(false); maxParallelism.Should().Be(1); index.Should().BeEmpty(); async Task OccupyTheLockALittleBit(int key) { var currentTaskId = Task.CurrentId ?? -1; var delay = random.Next(500); await Task.Delay(delay).ConfigureAwait(false); IKeyedSemaphore keyedSemaphore = null; try { keyedSemaphore = keyedSemaphores.Provide(key.ToString()); await keyedSemaphore.WaitAsync().ConfigureAwait(false); try { var incrementedCurrentParallelism = Interlocked.Increment(ref currentParallelism); lock (parallelismLock) { maxParallelism = Math.Max(incrementedCurrentParallelism, maxParallelism); } if (runningTasksIndex.TryGetValue(key, out var otherThread)) { throw new Exception($"Task [{currentTaskId,3}] has a lock for key ${key} " + $"but another task [{otherThread,3}] also has an active lock for this key!"); } runningTasksIndex[key] = currentTaskId; if (!runningTasksIndex.TryRemove(key, out var value)) { var ex = new Exception($"Task [{currentTaskId,3}] has finished " + $"but when trying to cleanup the running tasks index, the value is already gone"); throw ex; } if (value != currentTaskId) { var ex = new Exception($"Task [{currentTaskId,3}] has finished and has removed itself from the running tasks index," + $" but that index contained a task ID of another task: [{value}]!"); throw ex; } Interlocked.Decrement(ref currentParallelism); } finally { keyedSemaphore.Release(); } } finally { keyedSemaphore?.Dispose(); } } }
public void Dispose_CalledWhenEventStreamThrowsAnException_ExpectStreamRemovesItselfFromTheSessionCollection() { var eventStream = StubEventStream(); eventStream.When(x => x.Dispose()).Do(_ => { throw new Exception(); }); var sessionCollection = new ConcurrentDictionary<Guid, IEventStream>(); var stream = new NEventStoreSessionStream(sessionCollection, eventStream); stream.Invoking(x => x.Dispose()).ShouldThrow<Exception>(); sessionCollection.Should().NotContainValue(stream); }
public void Learn_method_should_do_nothing_when_response_verdict_is_accept() { modifier.Learn(CreateResult(replica1, ResponseVerdict.Accept), storageProvider); storage.Should().BeEmpty(); }
public void Dispose_Called_ExpectStreamRemovesItselfFromTheSessionCollection() { var sessionCollection = new ConcurrentDictionary<Guid, IEventStream>(); var stream = new NEventStoreSessionStream(sessionCollection, DummyEventStream()); stream.Dispose(); sessionCollection.Should().BeEmpty(); }