@@ -7,9 +7,16 @@ Dependency:
77```
88
99` Flow ` s which read from a Kafka topic, mapping stages and drains which publish to Kafka topics are available through
10- the ` KafkaFlow ` , ` KafkaStage ` and ` KafkaDrain ` objects. In all cases either a manually constructed instance of a
11- ` KafkaProducer ` / ` KafkaConsumer ` is needed, or ` ProducerSettings ` / ` ConsumerSetttings ` need to be provided with the
12- bootstrap servers, consumer group id, key / value serializers, etc.
10+ the ` KafkaFlow ` , ` KafkaStage ` and ` KafkaDrain ` objects.
11+
12+ In all cases kafka producers and consumers can be provided:
13+ * by manually creating (and closing) an instance of a ` KafkaProducer ` / ` KafkaConsumer `
14+ * through a ` ProducerSettings ` / ` ConsumerSettings ` , with the bootstrap servers, consumer group id, key/value
15+ serializers, etc. The lifetime is then managed by the flow operators.
16+ * through a thread-safe wrapper on a consumer (` ActorRef[KafkaConsumerWrapper[K, V]] ` ), for which the lifetime is bound
17+ to the current concurrency scope
18+
19+ ## Reading from Kafka
1320
1421To read from a Kafka topic, use:
1522
@@ -25,6 +32,8 @@ val source = KafkaFlow.subscribe(settings, topic)
2532 .runForeach { (msg : ReceivedMessage [String , String ]) => ??? }
2633```
2734
35+ ## Publishing to Kafka
36+
2837To publish data to a Kafka topic:
2938
3039``` scala mdoc:compile-only
4049 .pipe(KafkaDrain .runPublish(settings))
4150```
4251
52+ To publish data as a mapping stage:
53+
54+ ``` scala mdoc:compile-only
55+ import ox .flow .Flow
56+ import ox .kafka .ProducerSettings
57+ import ox .kafka .KafkaStage .*
58+ import org .apache .kafka .clients .producer .{ProducerRecord , RecordMetadata }
59+
60+ val settings = ProducerSettings .default.bootstrapServers(" localhost:9092" )
61+ val metadatas : Flow [RecordMetadata ] = Flow
62+ .fromIterable(List (" a" , " b" , " c" ))
63+ .map(msg => ProducerRecord [String , String ](" my_topic" , msg))
64+ .mapPublish(settings)
65+
66+ // process & run the metadatas flow further
67+ ```
68+
69+ ## Reading & publishing to Kafka with offset commits
70+
4371Quite often data to be published to a topic (` topic1 ` ) is computed basing on data received from another topic
4472(` topic2 ` ). In such a case, it's possible to commit messages from ` topic2 ` , after the messages to ` topic1 ` are
4573successfully published.
@@ -63,7 +91,7 @@ computed. For example:
6391``` scala mdoc:compile-only
6492import ox .kafka .{ConsumerSettings , KafkaDrain , KafkaFlow , ProducerSettings , SendPacket }
6593import ox .kafka .ConsumerSettings .AutoOffsetReset
66- import ox .pipe
94+ import ox .*
6795import org .apache .kafka .clients .producer .ProducerRecord
6896
6997val consumerSettings = ConsumerSettings .default(" my_group" )
@@ -72,29 +100,43 @@ val producerSettings = ProducerSettings.default.bootstrapServers("localhost:9092
72100val sourceTopic = " source_topic"
73101val destTopic = " dest_topic"
74102
75- KafkaFlow
76- .subscribe(consumerSettings, sourceTopic)
77- .map(in => (in.value.toLong * 2 , in))
78- .map((value, original) =>
79- SendPacket (ProducerRecord [String , String ](destTopic, value.toString), original))
80- .pipe(KafkaDrain .runPublishAndCommit(producerSettings))
103+ supervised:
104+ // the consumer is shared between the subscribe & offset stages
105+ // its lifetime is bound to the current concurrency scope
106+ val consumer = consumerSettings.toThreadSafeConsumerWrapper
107+ KafkaFlow
108+ .subscribe(consumer, sourceTopic)
109+ .map(in => (in.value.toLong * 2 , in))
110+ .map((value, original) =>
111+ SendPacket (ProducerRecord [String , String ](destTopic, value.toString), original))
112+ .pipe(KafkaDrain .runPublishAndCommit(producerSettings, consumer))
81113```
82114
83115The offsets are committed every second in a background process.
84116
85- To publish data as a mapping stage:
117+ ## Reading from Kafka, processing data & committing offsets
118+
119+ Offsets can also be committed after the data has been processed, without producing any records to write to a topic.
120+ For that, we can use the ` runCommit ` drain, or the ` mapCommit ` stage, both of which work with a ` Flow[CommitPacket] ` :
86121
87122``` scala mdoc:compile-only
88- import ox .flow .Flow
89- import ox .kafka .ProducerSettings
90- import ox .kafka .KafkaStage .*
91- import org .apache .kafka .clients .producer .{ProducerRecord , RecordMetadata }
123+ import ox .kafka .{ConsumerSettings , KafkaDrain , KafkaFlow , CommitPacket }
124+ import ox .kafka .ConsumerSettings .AutoOffsetReset
125+ import ox .*
92126
93- val settings = ProducerSettings .default.bootstrapServers(" localhost:9092" )
94- val metadatas : Flow [RecordMetadata ] = Flow
95- .fromIterable(List (" a" , " b" , " c" ))
96- .map(msg => ProducerRecord [String , String ](" my_topic" , msg))
97- .mapPublish(settings)
127+ val consumerSettings = ConsumerSettings .default(" my_group" )
128+ .bootstrapServers(" localhost:9092" ).autoOffsetReset(AutoOffsetReset .Earliest )
129+ val sourceTopic = " source_topic"
98130
99- // process & run the metadatas flow further
100- ```
131+ supervised:
132+ // the consumer is shared between the subscribe & offset stages
133+ // its lifetime is bound to the current concurrency scope
134+ val consumer = consumerSettings.toThreadSafeConsumerWrapper
135+ KafkaFlow
136+ .subscribe(consumer, sourceTopic)
137+ .mapPar(10 ) { in =>
138+ // process the message, e.g. send an HTTP request
139+ CommitPacket (in)
140+ }
141+ .pipe(KafkaDrain .runCommit(consumer))
142+ ```
0 commit comments