@@ -2,6 +2,7 @@ package uk.sky.fs2.kafka.topicloader
22
33import cats .data .{NonEmptyList , NonEmptyMap , OptionT }
44import cats .effect .Async
5+ import cats .effect .kernel .Resource
56import cats .syntax .all .*
67import cats .{Monad , Order }
78import fs2 .kafka .{ConsumerRecord , ConsumerSettings , KafkaConsumer }
@@ -43,21 +44,45 @@ trait TopicLoader {
4344 .evalMap { consumer =>
4445 {
4546 for {
46- allLogOffsets <- OptionT .liftF(logOffsetsForTopics(topics, strategy, consumer))
47- logOffsets <- OptionT .fromOption(NonEmptyMap .fromMap(SortedMap .from(allLogOffsets)))
47+ logOffsets <- OptionT (logOffsetsForTopics(topics, strategy, consumer))
48+ _ <- OptionT .liftF(
49+ consumer.assign(logOffsets.keys) *>
50+ logOffsets.toNel.traverse { case (tp, o) => consumer.seek(tp, o.lowest) }
51+ )
52+ } yield load(consumer, logOffsets)
53+ }.getOrElse(Stream .empty)
54+ }
55+ .flatten
56+
57+ private def load [F [_] : Async : LoggerFactory , K , V ](
58+ consumer : KafkaConsumer [F , K , V ],
59+ logOffsets : NonEmptyMap [TopicPartition , LogOffsets ]
60+ ): Stream [F , ConsumerRecord [K , V ]] = consumer.records.map(_.record).through(filterBelowHighestOffset(logOffsets))
61+
62+ def loadAndRun [F [_] : Async : LoggerFactory , K , V ](
63+ topics : NonEmptyList [String ],
64+ consumerSettings : ConsumerSettings [F , K , V ]
65+ )(onLoad : Resource .ExitCase => F [Unit ]): Stream [F , ConsumerRecord [K , V ]] =
66+ KafkaConsumer
67+ .stream(consumerSettings)
68+ .evalMap { consumer =>
69+ {
70+ for {
71+ logOffsets <- OptionT (logOffsetsForTopics(topics, LoadAll , consumer))
4872 _ <- OptionT .liftF(
4973 consumer.assign(logOffsets.keys) *>
5074 logOffsets.toNel.traverse { case (tp, o) => consumer.seek(tp, o.lowest) }
5175 )
52- } yield consumer.records
53- .map(_.record)
54- .through(filterBelowHighestOffset(logOffsets))
76+ preLoadStream <- OptionT .pure(load(consumer, logOffsets))
77+ _ <- OptionT .liftF(
78+ consumer.assign(logOffsets.keys) *>
79+ logOffsets.toNel.traverse { case (tp, o) => consumer.seek(tp, o.highest) }
80+ )
81+ } yield preLoadStream.onFinalizeCase(onLoad) ++ consumer.records.map(_.record)
5582 }.getOrElse(Stream .empty)
5683 }
5784 .flatten
5885
59- def loadAndRun (): Unit = ()
60-
6186 private def filterBelowHighestOffset [F [_] : Monad : LoggerFactory , K , V ](
6287 logOffsets : NonEmptyMap [TopicPartition , LogOffsets ]
6388 ): Pipe [F , ConsumerRecord [K , V ], ConsumerRecord [K , V ]] = {
@@ -73,20 +98,24 @@ trait TopicLoader {
7398 topics : NonEmptyList [String ],
7499 strategy : LoadTopicStrategy ,
75100 consumer : KafkaConsumer [F , K , V ]
76- ): F [Map [TopicPartition , LogOffsets ]] = for {
77- _ <- consumer.subscribe(topics)
78- partitionInfo <- topics.toList.flatTraverse(consumer.partitionsFor)
79- topicPartitions = partitionInfo.map(pi => new TopicPartition (pi.topic, pi.partition)).toSet
80- beginningOffsetPerPartition <- consumer.beginningOffsets(topicPartitions)
81- endOffsets <- strategy match {
82- case LoadAll => consumer.endOffsets(topicPartitions)
83- case LoadCommitted => earliestOffsets(consumer, beginningOffsetPerPartition)
84- }
85- logOffsets = beginningOffsetPerPartition.map { case (partition, offset) =>
86- partition -> LogOffsets (offset, endOffsets(partition))
87- }
88- _ <- consumer.unsubscribe
89- } yield logOffsets.filter { case (_, o) => o.highest > o.lowest }
101+ ): F [Option [NonEmptyMap [TopicPartition , LogOffsets ]]] =
102+ for {
103+ _ <- consumer.subscribe(topics)
104+ partitionInfo <- topics.toList.flatTraverse(consumer.partitionsFor)
105+ topicPartitions = partitionInfo.map(pi => new TopicPartition (pi.topic, pi.partition)).toSet
106+ beginningOffsetPerPartition <- consumer.beginningOffsets(topicPartitions)
107+ endOffsets <- strategy match {
108+ case LoadAll => consumer.endOffsets(topicPartitions)
109+ case LoadCommitted => earliestOffsets(consumer, beginningOffsetPerPartition)
110+ }
111+ logOffsets = beginningOffsetPerPartition.map { case (partition, offset) =>
112+ partition -> LogOffsets (offset, endOffsets(partition))
113+ }
114+ _ <- consumer.unsubscribe
115+ } yield {
116+ val offsets = logOffsets.filter { case (_, o) => o.highest > o.lowest }
117+ NonEmptyMap .fromMap(SortedMap .from(offsets))
118+ }
90119
91120 private def earliestOffsets [F [_] : Monad , K , V ](
92121 consumer : KafkaConsumer [F , K , V ],
0 commit comments