4
4
"context"
5
5
"errors"
6
6
"fmt"
7
+ "slices"
7
8
"sync"
8
9
"time"
9
10
@@ -28,17 +29,21 @@ type Client struct {
28
29
consumerMutex sync.RWMutex
29
30
logger Logger
30
31
31
- dlqRecord * kgo.Record
32
- hook * hooker
33
- cancel context.CancelFunc
34
- Trigger []func ()
32
+ dlqRecord * kgo.Record
33
+ dlqCheckTrigger func ()
34
+ dlqRetryAt time.Time
35
+ hook * hooker
36
+ cancel context.CancelFunc
37
+ trigger []func (context.Context )
35
38
39
+ topicsCheck []string
40
+ appName string
36
41
// log purpose
37
42
38
- Brokers []string
39
- DLQTopics []string
40
- Topics []string
41
- Meter Meter
43
+ brokers []string
44
+ dlqTopics []string
45
+ topics []string
46
+ meter Meter
42
47
}
43
48
44
49
func New (ctx context.Context , cfg Config , opts ... Option ) (* Client , error ) {
@@ -76,8 +81,10 @@ func New(ctx context.Context, cfg Config, opts ...Option) (*Client, error) {
76
81
consumerConfig : o .ConsumerConfig ,
77
82
logger : o .Logger ,
78
83
clientID : []byte (o .ClientID ),
79
- Meter : o .Meter ,
80
- hook : & hooker {},
84
+ meter : o .Meter ,
85
+ hook : & hooker {
86
+ ctx : context .Background (),
87
+ },
81
88
}
82
89
83
90
kgoClient , err := newClient (c , cfg , & o , false )
@@ -123,13 +130,16 @@ func New(ctx context.Context, cfg Config, opts ...Option) (*Client, error) {
123
130
}
124
131
}
125
132
126
- c .Brokers = cfg .Brokers
133
+ c .brokers = cfg .Brokers
127
134
128
135
ctx , cancel := context .WithCancel (ctx )
129
136
c .cancel = cancel
130
137
138
+ c .topicsCheck = slices .Concat (c .topics , c .dlqTopics )
139
+ c .appName = o .AppName
140
+
131
141
for name , p := range o .Plugin .holder {
132
- if err := p (ctx , c , cfg .Plugin [name ]); err != nil {
142
+ if err := p (ctx , c , cfg .Plugins [name ]); err != nil {
133
143
return nil , fmt .Errorf ("plugin %s: %w" , name , err )
134
144
}
135
145
}
@@ -223,10 +233,10 @@ func newClient(c *Client, cfg Config, o *options, isDLQ bool) (*kgo.Client, erro
223
233
}
224
234
225
235
kgoOpt = append (kgoOpt , kgo .ConsumeTopics (topics ... ))
226
- c .DLQTopics = topics
236
+ c .dlqTopics = topics
227
237
} else {
228
238
kgoOpt = append (kgoOpt , kgo .ConsumeTopics (o .ConsumerConfig .Topics ... ))
229
- c .Topics = o .ConsumerConfig .Topics
239
+ c .topics = o .ConsumerConfig .Topics
230
240
}
231
241
}
232
242
@@ -278,7 +288,7 @@ func (c *Client) Consume(ctx context.Context, callback CallBackFunc, opts ...Opt
278
288
o := optionConsumer {
279
289
Client : c ,
280
290
ConsumerConfig : c .consumerConfig ,
281
- Meter : c .Meter ,
291
+ Meter : c .meter ,
282
292
}
283
293
284
294
opts = append ([]OptionConsumer {OptionConsumer (callback )}, opts ... )
@@ -295,9 +305,9 @@ func (c *Client) Consume(ctx context.Context, callback CallBackFunc, opts ...Opt
295
305
if c .KafkaDLQ == nil {
296
306
c .hook .setCtx (ctx )
297
307
298
- c .logger .Info ("wkafka start consuming" , "topics" , c .Topics )
308
+ c .logger .Info ("wkafka start consuming" , "topics" , c .topics )
299
309
if err := o .Consumer .Consume (ctx , c .Kafka ); err != nil {
300
- return fmt .Errorf ("failed to consume %v: %w" , c .Topics , err )
310
+ return fmt .Errorf ("failed to consume %v: %w" , c .topics , err )
301
311
}
302
312
303
313
return nil
@@ -311,18 +321,18 @@ func (c *Client) Consume(ctx context.Context, callback CallBackFunc, opts ...Opt
311
321
c .hook .setCtx (ctx )
312
322
313
323
g .Go (func () error {
314
- c .logger .Info ("wkafka start consuming" , "topics" , c .Topics )
324
+ c .logger .Info ("wkafka start consuming" , "topics" , c .topics )
315
325
if err := o .Consumer .Consume (ctx , c .Kafka ); err != nil {
316
- return fmt .Errorf ("failed to consume %v: %w" , c .Topics , err )
326
+ return fmt .Errorf ("failed to consume %v: %w" , c .topics , err )
317
327
}
318
328
319
329
return nil
320
330
})
321
331
322
332
g .Go (func () error {
323
- c .logger .Info ("wkafka start consuming DLQ" , "topics" , c .DLQTopics )
333
+ c .logger .Info ("wkafka start consuming DLQ" , "topics" , c .dlqTopics )
324
334
if err := o .ConsumerDLQ .Consume (ctx , c .KafkaDLQ ); err != nil {
325
- return fmt .Errorf ("failed to consume DLQ %v: %w" , c .Topics , err )
335
+ return fmt .Errorf ("failed to consume DLQ %v: %w" , c .topics , err )
326
336
}
327
337
328
338
return nil
@@ -351,22 +361,38 @@ func (c *Client) Admin() *kadm.Client {
351
361
return kadm .NewClient (c .Kafka )
352
362
}
353
363
354
- // Skip for modifying skip configuration in runtime.
355
- // - Useful for DLQ topic.
356
- // - Don't wait inside the modify function.
357
- func (c * Client ) Skip (modify func (SkipMap ) SkipMap ) {
364
+ func (c * Client ) modifySkip (modify func (SkipMap ) SkipMap ) {
358
365
c .consumerMutex .Lock ()
359
366
defer c .consumerMutex .Unlock ()
360
367
368
+ newSkip := modify (cloneSkip (c .consumerConfig .Skip ))
369
+
370
+ // eliminate not related topics
371
+ for topic := range newSkip {
372
+ if ! slices .Contains (c .topicsCheck , topic ) {
373
+ delete (newSkip , topic )
374
+ }
375
+ }
376
+
377
+ c .consumerConfig .Skip = newSkip
378
+ }
379
+
380
+ // Skip for modifying skip configuration in runtime.
381
+ // - Useful for DLQ topic.
382
+ // - Don't wait inside the modify function.
383
+ func (c * Client ) Skip (ctx context.Context , modify func (SkipMap ) SkipMap ) {
361
384
if modify == nil {
362
385
return
363
386
}
364
387
365
- c .consumerConfig . Skip = modify ( c . consumerConfig . Skip )
388
+ c .modifySkip ( modify )
366
389
367
- c .callTrigger ()
390
+ c .callTrigger (ctx )
391
+ if c .dlqCheckTrigger != nil {
392
+ c .dlqCheckTrigger ()
393
+ }
368
394
369
- c .logger .Debug ("wkafka skip modified" , "skip" , c .consumerConfig .Skip )
395
+ c .logger .Info ("wkafka skip modified" , "skip" , c .consumerConfig .Skip )
370
396
}
371
397
372
398
// SkipCheck returns skip configuration's deep clone.
@@ -381,27 +407,57 @@ func (c *Client) ClientID() []byte {
381
407
return c .clientID
382
408
}
383
409
384
- // SetDLQRecord to set stucked DLQRecord.
410
+ // setDLQRecord to set stucked DLQRecord.
385
411
// - Using in DLQ iteration.
386
- func (c * Client ) setDLQRecord (r * kgo.Record ) {
412
+ func (c * Client ) setDLQRecord (r * kgo.Record , t time. Time ) {
387
413
c .dlqRecord = r
414
+ c .dlqRetryAt = t
415
+ }
388
416
389
- c .callTrigger ()
417
+ // DLQRetryAt returns stucked DLQRecord's retry time.
418
+ // - Using in DLQ iteration only if DLQRecord is not nil.
419
+ func (c * Client ) DLQRetryAt () time.Time {
420
+ return c .dlqRetryAt
390
421
}
391
422
392
423
// DLQRecord returns stucked DLQRecord if exists.
424
+ // - Warning: return pointer and not modify it.
393
425
func (c * Client ) DLQRecord () * kgo.Record {
394
426
return c .dlqRecord
395
427
}
396
428
397
- func (c * Client ) callTrigger () {
429
+ func (c * Client ) callTrigger (ctx context. Context ) {
398
430
go func () {
399
- for _ , t := range c .Trigger {
400
- t ()
431
+ for _ , t := range c .trigger {
432
+ if ctx .Err () != nil {
433
+ return
434
+ }
435
+
436
+ t (ctx )
401
437
}
402
438
}()
403
439
}
404
440
405
- func (c * Client ) GetLogger () Logger {
441
+ func (c * Client ) AddTrigger (t func (context.Context )) {
442
+ c .trigger = append (c .trigger , t )
443
+ }
444
+
445
+ func (c * Client ) Logger () Logger {
406
446
return c .logger
407
447
}
448
+
449
+ func (c * Client ) Topics () []string {
450
+ return c .topics
451
+ }
452
+
453
+ func (c * Client ) Brokers () []string {
454
+ return c .brokers
455
+ }
456
+
457
+ func (c * Client ) DLQTopics () []string {
458
+ return c .dlqTopics
459
+ }
460
+
461
+ func (c * Client ) AppName () string {
462
+ return c .appName
463
+ }
0 commit comments