public async Task SourceWithOffsetContext_at_least_once_consuming_should_work()
        {
            var topic        = CreateTopic(1);
            var settings     = CreateConsumerSettings <string>(CreateGroup(1));
            var elementCount = 10;
            var batchSize    = 2;
            var messages     = Enumerable.Range(1, elementCount).ToList();

            await ProduceStrings(topic, messages, ProducerSettings);

            var committerSettings = CommitterSettings.WithMaxBatch(batchSize);

            var(control, probe) = KafkaConsumer.SourceWithOffsetContext(settings, Subscriptions.Topics(topic))
                                  .SelectAsync(10, message => Task.FromResult(Done.Instance))
                                  .Via(Committer.FlowWithOffsetContext <Done>(committerSettings))
                                  .AsSource()
                                  .ToMaterialized(this.SinkProbe <(NotUsed, ICommittableOffsetBatch)>(), Keep.Both)
                                  .Run(Materializer);

            probe.Request(10);
            var committedBatches = probe.Within(TimeSpan.FromSeconds(10), () => probe.ExpectNextN(elementCount / batchSize));

            probe.Cancel();

            AwaitCondition(() => control.IsShutdown.IsCompletedSuccessfully, TimeSpan.FromSeconds(10));

            committedBatches.Select(r => r.Item2).Sum(batch => batch.BatchSize).Should().Be(10);
        }
예제 #2
0
        public static async Task Main(string[] args)
        {
            Config fallbackConfig = ConfigurationFactory.ParseString(@"
                    akka.suppress-json-serializer-warning=true
                    akka.loglevel = DEBUG
                ").WithFallback(ConfigurationFactory.FromResource <ConsumerSettings <object, object> >("Akka.Streams.Kafka.reference.conf"));

            var system       = ActorSystem.Create("TestKafka", fallbackConfig);
            var materializer = system.Materializer();

            var consumerSettings = ConsumerSettings <Null, string> .Create(system, null, null)
                                   .WithBootstrapServers($"{EventHubNamespace}.servicebus.windows.net:9093")
                                   .WithGroupId(EventHubConsumerGroup)
                                   .WithProperties(new Dictionary <string, string>
            {
                { "security.protocol", "SASL_SSL" },
                { "sasl.mechanism", "PLAIN" },
                { "sasl.username", "$ConnectionString" },
                { "sasl.password", EventHubConnectionString },
            });

            var subscription = Subscriptions.Topics(EventHubName);

            var committerDefaults = CommitterSettings.Create(system);

            // Comment for simple no-commit consumer
            DrainingControl <NotUsed> control = KafkaConsumer.CommittableSource(consumerSettings, subscription)
                                                .SelectAsync(1, msg =>
                                                             Business(msg.Record).ContinueWith(done => (ICommittable)msg.CommitableOffset))
                                                .ToMaterialized(
                Committer.Sink(committerDefaults.WithMaxBatch(1)),
                DrainingControl <NotUsed> .Create)
                                                .Run(materializer);

            // Uncomment for simple no-commit consumer

            /*
             * await KafkaConsumer.PlainSource(consumerSettings, subscription)
             *  .RunForeach(result =>
             *  {
             *      Console.WriteLine($"Consumer: {result.Topic}/{result.Partition} {result.Offset}: {result.Value}");
             *  }, materializer);
             */

            Console.WriteLine("Press any key to stop consumer.");
            Console.ReadKey();

            // Comment for simple no-commit consumer
            await control.Stop();

            await system.Terminate();
        }
예제 #3
0
        public async Task CommitterFlow_commits_offsets_from_CommittableSource(int batchSize)
        {
            var topic1          = CreateTopic(1);
            var topicPartition1 = new TopicPartition(topic1, 0);
            var group1          = CreateGroup(1);

            await GivenInitializedTopic(topicPartition1);

            await Source
            .From(Enumerable.Range(1, 100))
            .Select(elem => new ProducerRecord <Null, string>(topicPartition1, elem.ToString()))
            .RunWith(KafkaProducer.PlainSink(ProducerSettings), Materializer);

            var consumerSettings  = CreateConsumerSettings <string>(group1);
            var committedElements = new ConcurrentQueue <string>();
            var committerSettings = CommitterSettings.WithMaxBatch(batchSize);

            var(task, probe1) = KafkaConsumer.CommittableSource(consumerSettings, Subscriptions.Assignment(topicPartition1))
                                .WhereNot(c => c.Record.Value == InitialMsg)
                                .SelectAsync(10, elem =>
            {
                committedElements.Enqueue(elem.Record.Value);
                return(Task.FromResult(elem.CommitableOffset as ICommittable));
            })
                                .Via(Committer.Flow(committerSettings))
                                .ToMaterialized(this.SinkProbe <Done>(), Keep.Both)
                                .Run(Materializer);

            probe1.Request(25 / batchSize);

            foreach (var _ in Enumerable.Range(1, 25 / batchSize))
            {
                probe1.ExpectNext(Done.Instance, TimeSpan.FromSeconds(10));
            }

            probe1.Cancel();

            AwaitCondition(() => task.IsShutdown.IsCompletedSuccessfully);

            var probe2 = KafkaConsumer.PlainSource(consumerSettings, Subscriptions.Assignment(new TopicPartition(topic1, 0)))
                         .Select(_ => _.Value)
                         .RunWith(this.SinkProbe <string>(), Materializer);

            probe2.Request(75);
            foreach (var i in Enumerable.Range(committedElements.Count + 1, 75).Select(c => c.ToString()))
            {
                probe2.ExpectNext(i, TimeSpan.FromSeconds(10));
            }

            probe2.Cancel();
        }
예제 #4
0
 /// <summary>
 /// Batches offsets and commits them to Kafka.
 /// </summary>
 public static Sink <ICommittable, Task> Sink(CommitterSettings settings)
 {
     return(Flow(settings).ToMaterialized(Streams.Dsl.Sink.Ignore <Done>(), Keep.Right));
 }
예제 #5
0
        public static FlowWithContext <ICommittableOffset, E, ICommittableOffsetBatch, NotUsed, NotUsed> FlowWithOffsetContext <E>(CommitterSettings settings)
        {
            var value = Akka.Streams.Dsl.Flow.Create <(E, ICommittableOffset)>()
                        .Select(m => m.Item2 as ICommittable)
                        .Via(BatchFlow(settings))
                        .Select(b => (NotUsed.Instance, b));

            return(FlowWithContext.From(value));
        }
예제 #6
0
 /// <summary>
 /// Batches offsets and commits them to Kafka, emits <see cref="Done.Instance"/> for every committed batch.
 /// </summary>
 public static Flow <ICommittable, Done, NotUsed> Flow(CommitterSettings settings)
 {
     return(BatchFlow(settings).Select(_ => Done.Instance));
 }
예제 #7
0
 /// <summary>
 /// Batches offsets and commits them to Kafka, emits <see cref="CommittableOffsetBatch"/> for every committed batch.
 /// </summary>
 public static Flow <ICommittable, ICommittableOffsetBatch, NotUsed> BatchFlow(CommitterSettings settings)
 {
     return(Akka.Streams.Dsl.Flow.Create <ICommittable>().GroupedWithin(settings.MaxBatch, settings.MaxInterval)
            .Select(CommittableOffsetBatch.Create)
            .SelectAsync(settings.Parallelism, async batch =>
     {
         await batch.Commit();
         return batch;
     }));
 }
예제 #8
0
        public async Task SupervisionStrategy_Decider_on_complex_stream_should_work()
        {
            var topic                   = CreateTopic(1);
            var group                   = CreateGroup(1);
            var topicPartition          = new TopicPartition(topic, 0);
            var committedTopicPartition = new TopicPartition($"{topic}-done", 0);
            var callCount               = 0;

            Directive Decider(Exception cause)
            {
                callCount++;
                return(Directive.Resume);
            }

            var committerSettings = CommitterSettings.Create(Sys);
            var consumerSettings  = CreateConsumerSettings <string>(group);
            var counter           = 0;

            // arrange
            await Source.From(Enumerable.Range(1, 10))
            .Select(elem => new ProducerRecord <Null, string>(topicPartition, elem.ToString()))
            .RunWith(KafkaProducer.PlainSink(ProducerSettings), Materializer);

            // act
            var drainingControl = KafkaConsumer.CommittableSource(consumerSettings, Subscriptions.Assignment(topicPartition))
                                  .Via(Flow.Create <CommittableMessage <Null, string> >().Select(x =>
            {
                counter++;
                // Exception happened here, fail once, when counter is 5
                if (counter == 5)
                {
                    throw new Exception("BOOM!");
                }
                return(x);
            }))
                                  .WithAttributes(Attributes.CreateName("CommitableSource").And(ActorAttributes.CreateSupervisionStrategy(Decider)))
                                  .Select(c => (c.Record.Topic, c.Record.Message.Value, c.CommitableOffset))
                                  .SelectAsync(1, async t =>
            {
                Log.Info($"[{t.Topic}]: {t.Value}");
                // simulate a request-response call that takes 10ms to complete here
                await Task.Delay(10);
                return(t);
            })
                                  .Select(t => ProducerMessage.Single(new ProducerRecord <Null, string>(committedTopicPartition, t.Value),
                                                                      t.CommitableOffset))
                                  .Via(KafkaProducer.FlexiFlow <Null, string, ICommittableOffset>(ProducerSettings)).WithAttributes(Attributes.CreateName("FlexiFlow"))
                                  .Select(m => (ICommittable)m.PassThrough)
                                  .AlsoToMaterialized(Committer.Sink(committerSettings), DrainingControl <NotUsed> .Create)
                                  .To(Flow.Create <ICommittable>()
                                      .Async()
                                      .GroupedWithin(1000, TimeSpan.FromSeconds(1))
                                      .Select(c => c.Count())
                                      .Log("MsgCount").AddAttributes(Attributes.CreateLogLevels(LogLevel.InfoLevel))
                                      .To(Sink.Ignore <int>()))
                                  .Run(Sys.Materializer());

            await Task.Delay(TimeSpan.FromSeconds(5));

            await GuardWithTimeoutAsync(drainingControl.DrainAndShutdown(), TimeSpan.FromSeconds(10));

            // There should be only 1 decider call
            callCount.Should().Be(1);

            // Assert that all of the messages, except for those that failed in the stage, got committed
            var settings = CreateConsumerSettings <Null, string>(group);
            var probe    = KafkaConsumer
                           .PlainSource(settings, Subscriptions.Assignment(committedTopicPartition))
                           .Select(c => c.Message.Value)
                           .RunWith(this.SinkProbe <string>(), Materializer);

            probe.Request(9);
            var messages = new List <string>();

            for (var i = 0; i < 9; ++i)
            {
                var message = probe.RequestNext();
                messages.Add(message);
            }

            // Message "5" is missing because the exception happened downstream of the source and we chose to
            // ignore it in the decider
            messages.Should().BeEquivalentTo(new[] { "1", "2", "3", "4", "6", "7", "8", "9", "10" });
            probe.Cancel();
        }