Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
{
  "apiKey": 6,
  "type": "request",
  "listeners": ["zkBroker"],
  "name": "UpdateMetadataRequest",
  "validVersions": "0-8",  // <-- New version 8
  "flexibleVersions": "6+",
  "fields": [
    { "name": "ControllerId", "type": "int32", "versions": "0+", "entityType": "brokerId",
      "about": "The controller id." },
    { "name": "KRaftControllerId", "type": "int32", "versions": "8+", "entityType": "brokerId",
      "about": "The KRaft controller id, used during migration." }, // <-- New field
    { "name": "ControllerEpoch", "type": "int32", "versions": "0+",
      "about": "The controller epoch." },
    ...
   ]
}

ApiVersionsResponse

A new tagged field on ApiVersionsResponse will be added to allow KRaft controllers to indicate their ability to perform the migration

Code Block
{
  "apiKey": 18,
  "type": "response",
  "name": "ApiVersionsResponse",
  "validVersions": "0-4",   // <-- New version 4
  "flexibleVersions": "3+",
  "fields": [
    // ...
    { "name": "ZkMigrationReady", "type": "int8", "versions": "4+", "taggedVersions": "4+", "tag": 3, "ignorable": true,
      "about": "Set by a KRaft controller if the required configurations for ZK migration are present" } // <-- New field
  ]
}

This field will only be set by the KRaft controller when sending ApiVersionsResponse to other KRaft controllers. Since this migration does not support combined mode KRaft nodes, this field will never be seen by clients when receiving ApiVersionsResponse sent by brokers.

The initial supported values will be:

  • 0: Not Ready
  • 1: Ready
  • unset: Not a ZK controller


Migration Metadata Record

A new metadata record is added to indicate if a ZK migration has been started or finalized. 

Code Block
{
  "apiKey": <NEXT KEY>21,
  "type": "metadata",
  "name": "ZkMigrationRecordZkMigrationStateRecord",
  "validVersions": "0",
  "flexibleVersions": "0+",
  "fields": [
    { "name": "ZkMigrationState", "type": "int8", "versions": "0+",
      "about": "One of the possible migration states." },
  ]
}

The possible values for ZkMigrationState are: Started None (0) and Finalized (1, Pre-Migration (1), Migration (2), and Post-Migration (3). A int8 type is used to give the possibility of additional states in the future.

...

A new version of the broker registration RPC will be added to support ZK brokers registering with the KRaft quorum. A new tagged boolean field is added to signify that indicate that the sender of the RPC is a ZK broker that is ready for migration. The presence of this field is used to indicate that the sending broker is a ZK broker. The usage of this RPC by a ZK broker indicates that it has "zookeeper.metadata.migration.enable" and quorum connection configs properly set. The values of this tagged field are the same as the equivalent field in ApiVersionsRequest.

...

Code Block
{
  "apiKey":62,
  "type": "request",
  "listeners": ["controller"],
  "name": "BrokerRegistrationRequest",
  "validVersions": "0-1", // <-- New version 1
  "flexibleVersions": "0+",
  "fields": [
    // ...
     
    { "name": "ZkMigrationReadyIsMigratingZkBroker", "type": "int8bool", "versions": "1+", "taggedVersions": "1+", "tag": 1, "ignorabledefault": true"false",
      "about": "Set by a ZK broker if If the required configurations for ZK migration are present., this value is set to true" } // <-- New field   
   ]
}

RegisterBrokerRecord

A new field is added to signify that a registered broker is a ZooKeeper broker.

Code Block
{
  "apiKey": 0,
  "type": "metadata",
  "name": "RegisterBrokerRecord",
  "validVersions": "0-2",  // <-- New version 2
  "flexibleVersions": "0+",
  "fields": [
    { "name": "BrokerId", "type": "int32", "versions": "0+", "entityType": "brokerId",
      "about": "The broker id." },     
    { "name": "IsZkBrokerIsMigratingZkBroker", "type": "bool", "versions": "2+", "default": "false",
      "about": "True if the registering broker is a ZK broker in migration mode. Otherwise, false" },  // <-- New field
    // ...
  ]
}

...

A new version of the JSON schema for "/controller" will be added to include a "isKRaftkraftControllerEpoch" boolean field.

Code Block
{
  "version": 2, // <-- New version 2
  "brokerid": 3000,
  "timestamp": 1234567890,
  "isKRaftkraftControllerEpoch": true42     // <-- New field
}

This field is intended to be informational to aid with debugging.

...

Here is a state machine description of the migration. There will likely be more internal states that the controller uses, but these four will be exposed as the ZkMigrationState metric.


State

Enum

Description

Enum

Description

None

0

MigrationIneligible

1

The brokers and controllers do not meet the migration criteria. The cluster is operating in ZooKeeper mode.

MigratingZkData

2

The controller is copying data from ZooKeeper into KRaft.

DualWriteMetadata

3

The controller is in KRaft mode making dual writes to ZooKeeper.

KRaft mode and was never migrated from ZooKeeper

PreMigration

1

A KRaft controller has been provisioned and has migration enabled.

Migration

2

The KRaft controller has begun the data migration, brokers are being restarted, dual-writes are in progress.

PostMigration

3

The cluster is in KRaft mode

MigrationFinalized

4

The cluster has been migrated to KRaft mode.


The active ZooKeeper controller always reports "MigrationIneligible" while the active KRaft controller reports the state corresponding to the state of the migration.

...

In order to prevent further writes to ZK, the first thing the new KRaft quorum must do is take over leadership of the ZK controller. This can be achieved by unconditionally overwriting two values in ZK. The "/controller" ZNode indicates the current active controller. By overwriting it, a watch will fire on all the ZK brokers to inform them of a new controller election. The active KRaft controller will write its node ID (e.g., 3000) and epoch into this ZNode to claim controller leadership. This write will be persistent rather than the usual ephemeral write used by the ZK controller election algorithm. This will ensure that no ZK broker can claim leadership during a KRaft controller failover.

...

A new version of the BrokerRegistration RPC will be used by the ZK brokers to register themselves with KRaft. The ZK brokers will set the new ZkMigrationReady IsMigrationZkBroker field and populate the Features field with a "metadata.version" min and max supported equal to their IBP. The KRaft controller will only accept the registration if the given "metadata.version" is equal to the IBP/MetadataVersion of the quorum. The controller will also only accept the registration if the ZkMigrationReady has a valid value.

...

If a KRaft broker attempts to register itself with the node ID of an existing ZK broker, the controller will reject the registration and the broker will terminate.

KRaft Controller

...

Pre-Migration State

When the KRaft quorum is first established prior to starting a migration, it should not handle most RPCs until the initial data migration from ZooKeeper has completed. This is necessary to prevent divergence of metadata during the initial data migration. The controller will need to process RPCs related to Raft as well as BrokerRegistration and BrokerHeartbeat. Other RPCs (such as CreateTopics) will be rejected with a NOT_CONTROLLER error.

...