77 "fmt"
88 "regexp"
99 "slices"
10- "strings"
1110 "sync"
1211 "time"
1312
@@ -48,6 +47,7 @@ type Kafka struct {
4847 readerManager * kafkapkg.ReaderManager
4948 checkpointMessage sync.Map // last message for each reader w.r.t. partition to be used for checkpointing
5049 schemaRegistryClient * kafkapkg.SchemaRegistryClient
50+ admin * kadm.Client
5151}
5252
5353func (k * Kafka ) GetConfigRef () abstract.Config {
@@ -100,9 +100,11 @@ func (k *Kafka) Setup(ctx context.Context) error {
100100 }
101101
102102 k .client = client
103+ k .admin = kadm .NewClient (client )
103104
104105 // Test connectivity by fetching metadata
105- if err := client .Ping (ctx ); err != nil {
106+ err = client .Ping (ctx )
107+ if err != nil {
106108 return fmt .Errorf ("failed to ping kafka brokers: %s" , err )
107109 }
108110
@@ -125,16 +127,22 @@ func (k *Kafka) Setup(ctx context.Context) error {
125127}
126128
127129func (k * Kafka ) Close () error {
130+ ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
131+ defer cancel ()
132+
133+ if k .readerManager != nil {
134+ k .readerManager .RemoveExistingConsumers (ctx , k .client )
135+ }
136+
128137 if k .client != nil {
129138 k .client .Close ()
130139 }
131- k .readerManager .Close ()
132140 return nil
133141}
134142
135143func (k * Kafka ) GetStreamNames (ctx context.Context ) ([]string , error ) {
136144 logger .Infof ("Starting discover for Kafka" )
137- metadata , err := kadm . NewClient ( k . client ) .ListTopics (ctx )
145+ metadata , err := k . admin .ListTopics (ctx )
138146 if err != nil {
139147 return nil , fmt .Errorf ("failed to list topics: %s" , err )
140148 }
@@ -160,9 +168,8 @@ func (k *Kafka) ProduceSchema(ctx context.Context, streamName string) (*types.St
160168
161169 // create reader manager for schema discovery
162170 readerManager := kafkapkg .NewReaderManager (kafkapkg.ReaderConfig {
163- BootstrapServers : k .config .BootstrapServers ,
164- Dialer : k .dialer ,
165- Client : k .client ,
171+ Dialer : k .dialer ,
172+ Admin : k .admin ,
166173 })
167174
168175 // get the topic metadata
@@ -171,21 +178,19 @@ func (k *Kafka) ProduceSchema(ctx context.Context, streamName string) (*types.St
171178 return nil , fmt .Errorf ("failed to fetch topic metadata for topic %s: %s" , streamName , err )
172179 }
173180
174- admin := kadm .NewClient (k .client )
175-
176181 // get offsets for all partitions
177- startOffsets , err := admin .ListStartOffsets (ctx , streamName )
182+ startOffsets , err := k . admin .ListStartOffsets (ctx , streamName )
178183 if err != nil {
179184 return nil , fmt .Errorf ("failed to list start offsets for topic %s: %s" , streamName , err )
180185 }
181186
182- endOffsets , err := admin .ListEndOffsets (ctx , streamName )
187+ endOffsets , err := k . admin .ListEndOffsets (ctx , streamName )
183188 if err != nil {
184189 return nil , fmt .Errorf ("failed to list end offsets for topic %s: %s" , streamName , err )
185190 }
186191
187192 if topicDetail .Err != nil {
188- return nil , fmt .Errorf ("topic metadata for %s: %w " , streamName , topicDetail .Err )
193+ return nil , fmt .Errorf ("topic metadata for %s: %v " , streamName , topicDetail .Err )
189194 }
190195
191196 partitionList := topicDetail .Partitions .Sorted ()
@@ -194,7 +199,7 @@ func (k *Kafka) ProduceSchema(ctx context.Context, streamName string) (*types.St
194199 // get messages from partitions for schema discovery
195200 err = utils .Concurrent (ctx , partitionList , len (partitionList ), func (ctx context.Context , partitionDetail kadm.PartitionDetail , _ int ) error {
196201 if partitionDetail .Err != nil {
197- return fmt .Errorf ("partition %d: %w " , partitionDetail .Partition , partitionDetail .Err )
202+ return fmt .Errorf ("partition %d: %v " , partitionDetail .Partition , partitionDetail .Err )
198203 }
199204
200205 startOffset , exists := startOffsets .Lookup (streamName , partitionDetail .Partition )
@@ -232,7 +237,7 @@ func (k *Kafka) ProduceSchema(ctx context.Context, streamName string) (*types.St
232237 fetchCtx , cancel := context .WithTimeout (ctx , 30 * time .Second )
233238 defer cancel ()
234239
235- return k .processKafkaMessages (fetchCtx , reader , func (record types.KafkaRecord ) (bool , error ) {
240+ _ = k .processKafkaMessages (fetchCtx , reader , func (record types.KafkaRecord ) (bool , error ) {
236241 messageCount ++
237242 if record .Data != nil {
238243 mu .Lock ()
@@ -248,6 +253,7 @@ func (k *Kafka) ProduceSchema(ctx context.Context, streamName string) (*types.St
248253 shouldExit := messageCount >= 10000 || record .Message .Offset >= endOffset .Offset - 1
249254 return shouldExit , nil
250255 })
256+ return nil
251257 })
252258 if err != nil {
253259 return nil , fmt .Errorf ("failed to fetch schema for topic %s: %s" , streamName , err )
@@ -363,7 +369,7 @@ func (k *Kafka) buildTLSConfig() (*tls.Config, error) {
363369}
364370
365371// checkPartitionCompletion checks if a partition is complete and handles loop termination
366- func (k * Kafka ) checkPartitionCompletion (ctx context.Context , readerID int , reader * kgo. Client , completedPartitions , observedPartitions map [types.PartitionKey ]struct {}) (bool , error ) {
372+ func (k * Kafka ) checkPartitionCompletion (ctx context.Context , readerID int , completedPartitions , observedPartitions map [types.PartitionKey ]struct {}) (bool , error ) {
367373 // cache observed partitions
368374 if len (observedPartitions ) == 0 {
369375 // Ensure we have all assigned partitions tracked
@@ -377,53 +383,37 @@ func (k *Kafka) checkPartitionCompletion(ctx context.Context, readerID int, read
377383 observedPartitions [assignedPk ] = struct {}{}
378384 }
379385 }
380- // DescribeGroups member matching can miss the live consumer; fall back to what this client is actually consuming.
381- if len (observedPartitions ) == 0 && reader != nil {
382- for topic , parts := range reader .UncommittedOffsets () {
383- for part := range parts {
384- pk := types.PartitionKey {Topic : topic , Partition : int (part )}
385- if _ , exists := k .readerManager .GetPartitionIndex (fmt .Sprintf ("%s:%d" , pk .Topic , pk .Partition )); exists {
386- observedPartitions [pk ] = struct {}{}
387- }
388- }
389- }
390- }
391386 }
392387
393- // Require a non-empty observed set so 0==0 is not treated as "done" before we know assignments.
394- if len (observedPartitions ) == 0 {
395- return false , nil
396- }
388+ // exit when all partitions are done
397389 return len (completedPartitions ) == len (observedPartitions ), nil
398390}
399391
400392// getReaderAssignedPartitions queries the consumer group and returns topic/partition pairs
401- // assigned to the reader identified by readerIndex. We match on the per-reader ClientID .
393+ // assigned to the reader identified by readerIndex. We match on the per-reader readerID .
402394func (k * Kafka ) getReaderAssignedPartitions (ctx context.Context , readerIndex int ) ([]types.PartitionKey , error ) {
403- readerID , clientID := k .readerManager .GetReaderIDAndClientID (readerIndex )
404- if clientID == "" {
405- return nil , fmt .Errorf ("clientID not found for reader %s " , readerID )
395+ readerID , _ := k .readerManager .GetReaderIDAndClientID (readerIndex )
396+ if readerID == "" {
397+ return nil , fmt .Errorf ("readerID not found for reader index %d " , readerIndex )
406398 }
407399
408- admin := kadm .NewClient (k .client )
409-
410- resp , err := admin .DescribeGroups (ctx , k .consumerGroupID )
400+ response , err := k .admin .DescribeGroups (ctx , k .consumerGroupID )
411401 if err != nil {
412- return nil , fmt .Errorf ("DescribeGroups failed: %w " , err )
402+ return nil , fmt .Errorf ("DescribeGroups failed: %s " , err )
413403 }
414404
415- if err := resp .Error (); err != nil {
416- return nil , fmt .Errorf ("DescribeGroups response error: %w " , err )
405+ if err := response .Error (); err != nil {
406+ return nil , fmt .Errorf ("DescribeGroups response error: %s " , err )
417407 }
418408
419409 var assigned []types.PartitionKey
420- for _ , group := range resp {
410+ for _ , group := range response {
421411 if group .Group != k .consumerGroupID || group .Err != nil {
422412 continue
423413 }
424414 for _ , member := range group .Members {
425- // try to match the client we created: primary on ClientID, fallback to MemberID or suffix match
426- if member .ClientID != clientID && member .MemberID != clientID && ! strings . Contains ( member . ClientID , readerID ) && ! strings . Contains ( member . MemberID , readerID ) {
415+ // try to match the reader we created: primary on readerID
416+ if member .InstanceID == nil || * member .InstanceID != readerID {
427417 continue
428418 }
429419
@@ -434,10 +424,7 @@ func (k *Kafka) getReaderAssignedPartitions(ctx context.Context, readerIndex int
434424
435425 for _ , topic := range assignment .Topics {
436426 for _ , partition := range topic .Partitions {
437- assigned = append (assigned , types.PartitionKey {
438- Topic : topic .Topic ,
439- Partition : int (partition ),
440- })
427+ assigned = append (assigned , types.PartitionKey {Topic : topic .Topic , Partition : int (partition )})
441428 }
442429 }
443430 }
0 commit comments