27
27
import org .apache .kafka .common .errors .UnsupportedVersionException ;
28
28
import org .apache .kafka .common .message .BrokerRegistrationRequestData ;
29
29
import org .apache .kafka .common .message .ControllerRegistrationRequestData ;
30
+ import org .apache .kafka .common .message .GetKVsRequestData ;
31
+ import org .apache .kafka .common .message .PutKVsRequestData ;
30
32
import org .apache .kafka .common .metadata .BrokerRegistrationChangeRecord ;
31
33
import org .apache .kafka .common .metadata .FenceBrokerRecord ;
32
34
import org .apache .kafka .common .metadata .RegisterBrokerRecord ;
39
41
import org .apache .kafka .common .protocol .ApiMessage ;
40
42
import org .apache .kafka .common .utils .LogContext ;
41
43
import org .apache .kafka .common .utils .Time ;
44
+ import org .apache .kafka .controller .stream .KVControlManager ;
42
45
import org .apache .kafka .metadata .BrokerRegistration ;
43
46
import org .apache .kafka .metadata .BrokerRegistrationFencingChange ;
44
47
import org .apache .kafka .metadata .BrokerRegistrationInControlledShutdownChange ;
59
62
60
63
import org .slf4j .Logger ;
61
64
65
+ import java .nio .ByteBuffer ;
62
66
import java .util .AbstractMap ;
63
67
import java .util .ArrayList ;
68
+ import java .util .Collections ;
64
69
import java .util .HashMap ;
65
70
import java .util .HashSet ;
66
71
import java .util .Iterator ;
@@ -100,11 +105,17 @@ static class Builder {
100
105
101
106
// AutoMQ for Kafka inject start
102
107
private List <String > quorumVoters ;
108
+ private KVControlManager kvControlManager ;
103
109
104
110
Builder setQuorumVoters (List <String > quorumVoters ) {
105
111
this .quorumVoters = quorumVoters ;
106
112
return this ;
107
113
}
114
+
115
+ Builder setKVControlManager (KVControlManager kvControlManager ) {
116
+ this .kvControlManager = kvControlManager ;
117
+ return this ;
118
+ }
108
119
// AutoMQ for Kafka inject end
109
120
110
121
Builder setLogContext (LogContext logContext ) {
@@ -180,7 +191,10 @@ ClusterControlManager build() {
180
191
featureControl ,
181
192
zkMigrationEnabled ,
182
193
brokerUncleanShutdownHandler ,
183
- quorumVoters
194
+ // AutoMQ inject start
195
+ quorumVoters ,
196
+ kvControlManager
197
+ // AutoMQ inject end
184
198
);
185
199
}
186
200
}
@@ -292,6 +306,12 @@ boolean check() {
292
306
* The real next available node id is generally one greater than this value.
293
307
*/
294
308
private AtomicInteger nextNodeId = new AtomicInteger (-1 );
309
+
310
+ /**
311
+ * A set of node IDs that have been unregistered and can be reused for new node assignments.
312
+ */
313
+ private final KVControlManager kvControlManager ;
314
+ private static final String REUSABLE_NODE_IDS_KEY = "__automq_reusable_node_ids/" ;
295
315
// AutoMQ for Kafka inject end
296
316
297
317
private ClusterControlManager (
@@ -304,7 +324,10 @@ private ClusterControlManager(
304
324
FeatureControlManager featureControl ,
305
325
boolean zkMigrationEnabled ,
306
326
BrokerUncleanShutdownHandler brokerUncleanShutdownHandler ,
307
- List <String > quorumVoters
327
+ // AutoMQ inject start
328
+ List <String > quorumVoters ,
329
+ KVControlManager kvControlManager
330
+ // AutoMQ inject end
308
331
) {
309
332
this .logContext = logContext ;
310
333
this .clusterId = clusterId ;
@@ -323,6 +346,7 @@ private ClusterControlManager(
323
346
this .brokerUncleanShutdownHandler = brokerUncleanShutdownHandler ;
324
347
// AutoMQ for Kafka inject start
325
348
this .maxControllerId = QuorumConfig .parseVoterConnections (quorumVoters ).keySet ().stream ().max (Integer ::compareTo ).orElse (0 );
349
+ this .kvControlManager = kvControlManager ;
326
350
// AutoMQ for Kafka inject end
327
351
}
328
352
@@ -369,16 +393,73 @@ boolean zkRegistrationAllowed() {
369
393
370
394
// AutoMQ for Kafka inject start
371
395
public ControllerResult <Integer > getNextNodeId () {
372
- int maxBrokerId = brokerRegistrations .keySet ().stream ().max (Integer ::compareTo ).orElse (0 );
373
- int maxNodeId = Math .max (maxBrokerId , maxControllerId );
374
- int nextId = this .nextNodeId .accumulateAndGet (maxNodeId , (x , y ) -> Math .max (x , y ) + 1 );
375
- // Let the broker's nodeId start from 1000 to easily distinguish broker and controller.
376
- nextId = Math .max (nextId , 1000 );
377
- UpdateNextNodeIdRecord record = new UpdateNextNodeIdRecord ().setNodeId (nextId );
396
+ int nextId ;
397
+ Set <Integer > reusableNodeIds = getReusableNodeIds ();
398
+ if (!reusableNodeIds .isEmpty ()) {
399
+ Iterator <Integer > iterator = reusableNodeIds .iterator ();
400
+ nextId = iterator .next ();
401
+ // we simply remove the id from reusable id set because we're unable to determine if the id
402
+ // will finally be used.
403
+ iterator .remove ();
404
+ return ControllerResult .atomicOf (putReusableNodeIds (reusableNodeIds ), nextId );
405
+ } else {
406
+ int maxBrokerId = brokerRegistrations .keySet ().stream ().max (Integer ::compareTo ).orElse (0 );
407
+ int maxNodeId = Math .max (maxBrokerId , maxControllerId );
408
+ nextId = this .nextNodeId .accumulateAndGet (maxNodeId , (x , y ) -> Math .max (x , y ) + 1 );
409
+ // Let the broker's nodeId start from 1000 to easily distinguish broker and controller.
410
+ nextId = Math .max (nextId , 1000 );
411
+ UpdateNextNodeIdRecord record = new UpdateNextNodeIdRecord ().setNodeId (nextId );
378
412
379
- List <ApiMessageAndVersion > records = new ArrayList <>();
380
- records .add (new ApiMessageAndVersion (record , (short ) 0 ));
381
- return ControllerResult .atomicOf (records , nextId );
413
+ List <ApiMessageAndVersion > records = new ArrayList <>();
414
+ records .add (new ApiMessageAndVersion (record , (short ) 0 ));
415
+ return ControllerResult .atomicOf (records , nextId );
416
+ }
417
+ }
418
+
419
+ Set <Integer > getReusableNodeIds () {
420
+ return deserializeReusableNodeIds (kvControlManager .getKV (
421
+ new GetKVsRequestData .GetKVRequest ().setKey (REUSABLE_NODE_IDS_KEY )).value ());
422
+ }
423
+
424
+ List <ApiMessageAndVersion > putReusableNodeIds (Set <Integer > reusableNodeIds ) {
425
+ return kvControlManager .putKV (new PutKVsRequestData .PutKVRequest ()
426
+ .setKey (REUSABLE_NODE_IDS_KEY )
427
+ .setValue (serializeReusableNodeIds (reusableNodeIds ))
428
+ .setOverwrite (true ))
429
+ .records ();
430
+ }
431
+
432
+ private Set <Integer > deserializeReusableNodeIds (byte [] value ) {
433
+ if (value == null ) {
434
+ return new HashSet <>();
435
+ }
436
+ ByteBuffer buffer = ByteBuffer .wrap (value );
437
+ Set <Integer > reusableNodeIds = new HashSet <>();
438
+ while (buffer .hasRemaining ()) {
439
+ reusableNodeIds .add (buffer .getInt ());
440
+ }
441
+ return reusableNodeIds ;
442
+ }
443
+
444
+ private byte [] serializeReusableNodeIds (Set <Integer > reusableNodeIds ) {
445
+ ByteBuffer buffer = ByteBuffer .allocate (reusableNodeIds .size () * Integer .BYTES );
446
+ reusableNodeIds .forEach (buffer ::putInt );
447
+ return buffer .array ();
448
+ }
449
+
450
+ public List <ApiMessageAndVersion > registerBrokerRecords (int brokerId ) {
451
+ Set <Integer > reusableNodeIds = getReusableNodeIds ();
452
+ if (reusableNodeIds .contains (brokerId )) {
453
+ reusableNodeIds .remove (brokerId );
454
+ return putReusableNodeIds (reusableNodeIds );
455
+ }
456
+ return Collections .emptyList ();
457
+ }
458
+
459
+ public List <ApiMessageAndVersion > unRegisterBrokerRecords (int brokerId ) {
460
+ Set <Integer > reusableNodeIds = getReusableNodeIds ();
461
+ reusableNodeIds .add (brokerId );
462
+ return putReusableNodeIds (reusableNodeIds );
382
463
}
383
464
// AutoMQ for Kafka inject end
384
465
@@ -496,6 +577,10 @@ public ControllerResult<BrokerRegistrationReply> registerBroker(
496
577
}
497
578
heartbeatManager .register (brokerId , record .fenced ());
498
579
580
+ // AutoMQ for Kafka inject start
581
+ records .addAll (registerBrokerRecords (brokerId ));
582
+ // AutoMQ for Kafka inject end
583
+
499
584
return ControllerResult .atomicOf (records , new BrokerRegistrationReply (record .brokerEpoch ()));
500
585
}
501
586
@@ -583,6 +668,7 @@ public void replay(RegisterBrokerRecord record, long offset) {
583
668
if (prevRegistration != null ) heartbeatManager .remove (brokerId );
584
669
heartbeatManager .register (brokerId , record .fenced ());
585
670
}
671
+
586
672
if (prevRegistration == null ) {
587
673
log .info ("Replayed initial RegisterBrokerRecord for broker {}: {}" , record .brokerId (), record );
588
674
} else if (prevRegistration .incarnationId ().equals (record .incarnationId ())) {
@@ -608,6 +694,7 @@ public void replay(UnregisterBrokerRecord record) {
608
694
if (heartbeatManager != null ) heartbeatManager .remove (brokerId );
609
695
updateDirectories (brokerId , registration .directories (), null );
610
696
brokerRegistrations .remove (brokerId );
697
+ // AutoMQ injection end
611
698
log .info ("Replayed {}" , record );
612
699
}
613
700
}
0 commit comments