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}"))}

");
    }
Example #2
0
    protected override void SetUp()
    {
        _positionsStorage = new InMemPositionsStorage();

        _topic = GetNewTopic();

        _producer = Configure
                    .Producer(c => c.UseKafka(KafkaTestConfig.Address))
                    .Logging(l => l.UseSerilog())
                    .Create();

        Using(_producer);
    }
Example #3
0
    public static void StoreInMemory(this StandardConfigurer <IPositionManager> configurer, InMemPositionsStorage positionsStorage = null)
    {
        var registrar = StandardConfigurer.Open(configurer);

        registrar.Register(_ => new InMemPositionsManager(positionsStorage ?? new InMemPositionsStorage()));
    }
 public InMemPositionsManager(InMemPositionsStorage positionsStorage)
 {
     _positionsStorage = positionsStorage ?? throw new ArgumentNullException(nameof(positionsStorage));
 }
    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();
            }
        }));
Example #6
0
        public async Task ConsumerCanPickUpWhereItLeftOff()
        {
            var receivedStrings = new ConcurrentQueue <string>();
            var topic           = BrokerFactory.GetNewTopic();

            var producer = BrokerFactory.ConfigureProducer()
                           .Topics(m => m.Map <string>(topic))
                           .Create();

            Using(producer);

            IDisposable CreateConsumer(InMemPositionsStorage storage)
            {
                return(BrokerFactory.ConfigureConsumer("default-group")
                       .Handle(async(messages, token) =>
                {
                    var strings = messages.Select(m => m.Body).Cast <string>();

                    receivedStrings.Enqueue(strings);
                })
                       .Topics(t => t.Subscribe(topic))
                       .Positions(p => p.StoreInMemory(storage))
                       .Start());
            }

            var positionsStorage = new InMemPositionsStorage();

            const string partitionKey = "same-every-time";

            using (CreateConsumer(positionsStorage))
            {
                await producer.Send("HEJ", partitionKey : partitionKey);

                await producer.Send("MED", partitionKey : partitionKey);

                await producer.Send("DIG", partitionKey : partitionKey);

                string GetFailureDetailsFunction() => $@"Got these strings:

{receivedStrings.ToPrettyJson()}";

                await receivedStrings.WaitOrDie(
                    completionExpression : q => q.Count == 3,
                    failExpression : q => q.Count > 3,
                    failureDetailsFunction : GetFailureDetailsFunction
                    );
            }

            Console.WriteLine($@"Got these positions after FIRST run:

{string.Join(Environment.NewLine, positionsStorage.GetAll(topic).Select(position => $"    {position}"))}

");

            using (CreateConsumer(positionsStorage))
            {
                await producer.Send("MIN", partitionKey : partitionKey);

                await producer.Send("SØDE", partitionKey : partitionKey);

                await producer.Send("VEN", partitionKey : partitionKey);

                await receivedStrings.WaitOrDie(q => q.Count == 6, failExpression : q => q.Count > 6);

                // additional delay to be absolutely sure that no additional messages arrive after this point
                await Task.Delay(TimeSpan.FromSeconds(1));
            }

            Console.WriteLine($@"Got these positions after SECOND run:

{string.Join(Environment.NewLine, positionsStorage.GetAll(topic).Select(position => $"    {position}"))}

");

            Assert.That(receivedStrings.Count, Is.EqualTo(6), $@"Queue did not contain 6 strings as expected:

{receivedStrings.ToPrettyJson()}");

            var expectedReceivedStrings = new[]
            {
                "HEJ",
                "MED",
                "DIG",
                "MIN",
                "SØDE",
                "VEN",
            };

            Assert.That(receivedStrings, Is.EqualTo(expectedReceivedStrings), $@"

Expected

    {string.Join(", ", expectedReceivedStrings)}

but got

    {string.Join(", ", receivedStrings)}

");
        }