6
6
"math/rand"
7
7
"strconv"
8
8
"sync"
9
+ "time"
9
10
)
10
11
11
12
// ClusterAdmin is the administrative client for Kafka, which supports managing and inspecting topics,
@@ -134,8 +135,45 @@ func (ca *clusterAdmin) Controller() (*Broker, error) {
134
135
return ca .client .Controller ()
135
136
}
136
137
137
- func (ca * clusterAdmin ) CreateTopic (topic string , detail * TopicDetail , validateOnly bool ) error {
138
+ func (ca * clusterAdmin ) refreshController () (* Broker , error ) {
139
+ return ca .client .RefreshController ()
140
+ }
141
+
142
+ // isErrNoController returns `true` if the given error type unwraps to an
143
+ // `ErrNotController` response from Kafka
144
+ func isErrNoController (err error ) bool {
145
+ switch e := err .(type ) {
146
+ case * TopicError :
147
+ return e .Err == ErrNotController
148
+ case * TopicPartitionError :
149
+ return e .Err == ErrNotController
150
+ case KError :
151
+ return e == ErrNotController
152
+ }
153
+ return false
154
+ }
155
+
156
+ // retryOnError will repeatedly call the given (error-returning) func in the
157
+ // case that its response is non-nil and retriable (as determined by the
158
+ // provided retriable func) up to the maximum number of tries permitted by
159
+ // the admin client configuration
160
+ func (ca * clusterAdmin ) retryOnError (retriable func (error ) bool , fn func () error ) error {
161
+ var err error
162
+ for attempt := 0 ; attempt < ca .conf .Admin .Retry .Max ; attempt ++ {
163
+ err = fn ()
164
+ if err == nil || ! retriable (err ) {
165
+ return err
166
+ }
167
+ Logger .Printf (
168
+ "admin/request retrying after %dms... (%d attempts remaining)\n " ,
169
+ ca .conf .Admin .Retry .Backoff / time .Millisecond , ca .conf .Admin .Retry .Max - attempt )
170
+ time .Sleep (ca .conf .Admin .Retry .Backoff )
171
+ continue
172
+ }
173
+ return err
174
+ }
138
175
176
+ func (ca * clusterAdmin ) CreateTopic (topic string , detail * TopicDetail , validateOnly bool ) error {
139
177
if topic == "" {
140
178
return ErrInvalidTopic
141
179
}
@@ -160,26 +198,31 @@ func (ca *clusterAdmin) CreateTopic(topic string, detail *TopicDetail, validateO
160
198
request .Version = 2
161
199
}
162
200
163
- b , err := ca .Controller ()
164
- if err != nil {
165
- return err
166
- }
201
+ return ca .retryOnError (isErrNoController , func () error {
202
+ b , err := ca .Controller ()
203
+ if err != nil {
204
+ return err
205
+ }
167
206
168
- rsp , err := b .CreateTopics (request )
169
- if err != nil {
170
- return err
171
- }
207
+ rsp , err := b .CreateTopics (request )
208
+ if err != nil {
209
+ return err
210
+ }
172
211
173
- topicErr , ok := rsp .TopicErrors [topic ]
174
- if ! ok {
175
- return ErrIncompleteResponse
176
- }
212
+ topicErr , ok := rsp .TopicErrors [topic ]
213
+ if ! ok {
214
+ return ErrIncompleteResponse
215
+ }
177
216
178
- if topicErr .Err != ErrNoError {
179
- return topicErr
180
- }
217
+ if topicErr .Err != ErrNoError {
218
+ if topicErr .Err == ErrNotController {
219
+ _ , _ = ca .refreshController ()
220
+ }
221
+ return topicErr
222
+ }
181
223
182
- return nil
224
+ return nil
225
+ })
183
226
}
184
227
185
228
func (ca * clusterAdmin ) DescribeTopics (topics []string ) (metadata []* TopicMetadata , err error ) {
@@ -320,7 +363,6 @@ func (ca *clusterAdmin) ListTopics() (map[string]TopicDetail, error) {
320
363
}
321
364
322
365
func (ca * clusterAdmin ) DeleteTopic (topic string ) error {
323
-
324
366
if topic == "" {
325
367
return ErrInvalidTopic
326
368
}
@@ -334,25 +376,31 @@ func (ca *clusterAdmin) DeleteTopic(topic string) error {
334
376
request .Version = 1
335
377
}
336
378
337
- b , err := ca .Controller ()
338
- if err != nil {
339
- return err
340
- }
379
+ return ca .retryOnError (isErrNoController , func () error {
380
+ b , err := ca .Controller ()
381
+ if err != nil {
382
+ return err
383
+ }
341
384
342
- rsp , err := b .DeleteTopics (request )
343
- if err != nil {
344
- return err
345
- }
385
+ rsp , err := b .DeleteTopics (request )
386
+ if err != nil {
387
+ return err
388
+ }
346
389
347
- topicErr , ok := rsp .TopicErrorCodes [topic ]
348
- if ! ok {
349
- return ErrIncompleteResponse
350
- }
390
+ topicErr , ok := rsp .TopicErrorCodes [topic ]
391
+ if ! ok {
392
+ return ErrIncompleteResponse
393
+ }
351
394
352
- if topicErr != ErrNoError {
353
- return topicErr
354
- }
355
- return nil
395
+ if topicErr != ErrNoError {
396
+ if topicErr == ErrNotController {
397
+ _ , _ = ca .refreshController ()
398
+ }
399
+ return topicErr
400
+ }
401
+
402
+ return nil
403
+ })
356
404
}
357
405
358
406
func (ca * clusterAdmin ) CreatePartitions (topic string , count int32 , assignment [][]int32 , validateOnly bool ) error {
@@ -368,26 +416,31 @@ func (ca *clusterAdmin) CreatePartitions(topic string, count int32, assignment [
368
416
Timeout : ca .conf .Admin .Timeout ,
369
417
}
370
418
371
- b , err := ca .Controller ()
372
- if err != nil {
373
- return err
374
- }
419
+ return ca .retryOnError (isErrNoController , func () error {
420
+ b , err := ca .Controller ()
421
+ if err != nil {
422
+ return err
423
+ }
375
424
376
- rsp , err := b .CreatePartitions (request )
377
- if err != nil {
378
- return err
379
- }
425
+ rsp , err := b .CreatePartitions (request )
426
+ if err != nil {
427
+ return err
428
+ }
380
429
381
- topicErr , ok := rsp .TopicPartitionErrors [topic ]
382
- if ! ok {
383
- return ErrIncompleteResponse
384
- }
430
+ topicErr , ok := rsp .TopicPartitionErrors [topic ]
431
+ if ! ok {
432
+ return ErrIncompleteResponse
433
+ }
385
434
386
- if topicErr .Err != ErrNoError {
387
- return topicErr
388
- }
435
+ if topicErr .Err != ErrNoError {
436
+ if topicErr .Err == ErrNotController {
437
+ _ , _ = ca .refreshController ()
438
+ }
439
+ return topicErr
440
+ }
389
441
390
- return nil
442
+ return nil
443
+ })
391
444
}
392
445
393
446
func (ca * clusterAdmin ) DeleteRecords (topic string , partitionOffsets map [int32 ]int64 ) error {
0 commit comments