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); }
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(); }
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(); }
/// <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)); }
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)); }
/// <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)); }
/// <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; })); }
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(); }