public async Task TryDoingIt() { var numberOfMessages = 60; var positionsStorage = new InMemPositionsStorage(); var topic = GetNewTopic(numberOfPartitions: 4); var locks = new InMemExclusiveLockBandit(); var stopEverything = Using(new CancellationTokenSource()); var stopSecondConsumer = Using(new CancellationTokenSource()); var cancellationToken = stopEverything.Token; cancellationToken.Register(() => stopSecondConsumer.Cancel()); var receivedEvents = new ConcurrentQueue <MyEvent>(); // start producer that produces an event every second var producer = StartProducer(cancellationToken, topic, numberOfMessages); // when producer has finished producing, give everyone 10 more seconds to complete what they're doing producer.ContinueWith(_ => stopEverything.CancelAfter(TimeSpan.FromSeconds(10)), cancellationToken); // wait short while, start consumer await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); var consumer1 = StartConsumer(cancellationToken, "CONSUMER 1", topic, positionsStorage, receivedEvents, locks); // wait a while and start another consumer await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken); var consumer2 = StartConsumer(stopSecondConsumer.Token, "CONSUMER 2", topic, positionsStorage, receivedEvents, locks); // stop the second consumer after 20 seconds, so the first goes back to being the only one there stopSecondConsumer.CancelAfter(TimeSpan.FromSeconds(20)); // let things run, but ensure that we die if things don't stop by themselves within 1 minute stopEverything.CancelAfter(TimeSpan.FromMinutes(1)); // wait for all tasks await Task.WhenAll(producer, consumer1, consumer2); // check stuff Assert.That(receivedEvents.Count, Is.GreaterThanOrEqualTo(numberOfMessages), $@"Got the following messages: {string.Join(Environment.NewLine, receivedEvents.OrderBy(e => e.Number).Select(e => $" {e.Number}: {e.Text}"))} "); Assert.That(receivedEvents.Select(e => e.Text).Distinct().Count(), Is.EqualTo(numberOfMessages), $@"Got the following messages: {string.Join(Environment.NewLine, receivedEvents.OrderBy(e => e.Number).Select(e => $" {e.Number}: {e.Text}"))} "); }
static Task StartConsumer(CancellationToken cancellationToken, string name, string topic, InMemPositionsStorage positionsStorage, ConcurrentQueue <MyEvent> receivedEvents, InMemExclusiveLockBandit locks) { return(StartTask(cancellationToken, name, async() => { var consumer = Configure.Consumer("default", c => { c.UseKafka(KafkaTestConfig.Address) .OnPartitionsAssigned(async(context, partitions) => { var tasks = partitions .Select(async partition => { var key = GetLockKey(partition); var grabbedLock = await locks.GrabLock(key); context.SetItem(key, grabbedLock); return grabbedLock; }) .ToList(); await Task.WhenAll(tasks); }) .OnPartitionsRevoked(async(context, partitions) => { var keys = partitions.Select(GetLockKey).ToList(); foreach (var lockToRelease in keys.Select(context.GetItem <IDisposable>)) { lockToRelease?.Dispose(); } }) ; }) .Topics(t => t.Subscribe(topic)) .Serialization(s => s.UseNewtonsoftJson()) .Handle(async(messages, _, _) => { foreach (var message in messages) { if (message.Body is MyEvent myEvent) { Console.WriteLine($"{name} got event {myEvent.Number}"); receivedEvents.Enqueue(myEvent); } } }) .Positions(s => s.StoreInMemory(positionsStorage)) .Start(); using (consumer) { cancellationToken.WaitHandle.WaitOne(); } }));
public LockReleaser(string key, InMemExclusiveLockBandit bandit) { _key = key; _bandit = bandit; }