Current state: Under Discussion
Discussion thread: https://lists.apache.org/thread/vnzmqvcbfxo7hhyj9gzpgmdq59w3n7dy
JIRA:
Please keep the discussion on the mailing list rather than commenting on the wiki (wiki discussions get unwieldy fast).
The reason for this KIP is to remove the requirement of brokers needing to run kafka-storage format before starting Kafka. When running kafka-storage format , nodes are required to supply a --cluster-id argument, which represents the cluster ID to which the node belongs, and this is persisted to the node's meta.properties file. Below are the other data that each node persists to disk upon invoking kafka-storage format :
For brokers, meta.properties ’ other data: node.id and directory id , are obtained from the node’s static config and randomly generated, respectively. Persisting this data does not have to be done during storage formatting and can be done later during startup.
For controllers, meta.properties ’ directory id may come from —-initial-controllers or --standalone , but otherwise controllers are the same as brokers with respect to the above data.
The main purpose of cluster id is to prevent nodes from contacting other Kafka clusters (ref KIP-78). This KIP seeks to preserve this behavior around cluster id, while removing the necessity of formatting brokers and observer controllers.
Currently, Kafka relies on the operator/caller of kafka-storage format on all intended nodes in a cluster to generate consensus on the actual value of clusterID during format time. KRaft guarantees that upon forming a given quorum X, X's members will all have the same cluster id (or no cluster id) contained in meta.properties. Additionally, when a KRaft node sends a request with a non-null cluster id, this request is only handled if the request's cluster ID matches the handler's cluster ID.
Cluster id was a znode, /cluster/id , that was initially empty. During the startup of a cluster, brokers would race to write a random UUID in ZK to this znode, which would never change after being set, via getOrGenerateClusterId() .
meta.properties
Introduce meta.properties v2 with optional cluster id. However, node.id and directory.id are guaranteed to exist.
ClusterIdRecord + MetadataVersion
Introduce a new metadata record to store cluster id and a new MetadataVersion (MV) that supports encoding/decoding this record. This means that during formatting, the bootstrap ClusterIdRecord is only written if the node is formatted with a MV that supports this feature. When a node runs kafka-storage format, some value for MV is always resolved and written to disk alongside meta.properties . The value either comes from --release-version or --feature , or it defaults to the latest production MV.
If this feature is supported by the cluster's MV, the first elected KRaft leader will write the ClusterIdRecord upon becoming the active controller alongside the other bootstrap metadata records.
One invariant of this feature is there is at most one ClusterIdRecord in the metadata log. When the MV does not support ClusterIdRecord, there is no ClusterIdRecord in the metadata log, and instead the cluster id is a local to each node's meta.properties . When the MV does support ClusterIdRecord, there must be exactly one ClusterIdRecord in the metadata log. When the active controller writes the bootstrap metadata records when the MV supports this feature, or when it updates its MV to support this feature, it must write the ClusterIdRecord. This invariant is enforceable when replaying the log and checking the image and deltas.
{
"apiKey": 29,
"type": "metadata",
"name": "ClusterIdRecord",
"validVersions": "0",
"flexibleVersions": "0+",
"fields": [
{ "name": "ClusterId", "type": "string", "versions": "0+",
"about": "The unique ID of this cluster" }
]
} |
Storage Tool + Formatting requirements
Prior to this KIP, it is expected that all nodes running kafka have invoked the kafka-storage format command and have persisted a meta.properties with a cluster.id file prior to starting kafka. We do this by trying to read in the meta.properties during startup, and crashing if that file does not contain all the data we expect.
Observer controllers are controllers who are either not part of the static voter set in kraft.version=0, or controllers who are not part of the bootstrap voter set defined by --initial-controllers or --standalone in kraft.version=1 (i.e. controllers who format today with --no-initial-controllers). Non-observer/"bootstrap controllers" are therefore either part of the static voter set in kraft.version=0, or controllers who format with --initial-controllers or --standalone in a dynamic quorum setup. This distinction is important because observer controllers are not responsible for the initial KRaft leader election, but bootstrap controllers are.
We can still enforce that bootstrap controllers must have formatted (and therefore persisted a cluster id to meta.properties) prior to starting kafka. The validation of meta.properties during startup described above can be done whenever the node is a bootstrap controller (i.e. part of the static voters config, or if a 0-0.checkpoint exists with a VotersRecord). We still need to do this validation mainly for kraft.version=0 clusters with newer software versions but an older MV (kraft.version=1 clusters require formatting of at least one node to elect a leader). Otherwise, these clusters will have no way to persist a cluster id to meta.properties if the operator skips formatting on all nodes (which would be possible without this requirement).
Remove the requirement of brokers and observer controllers to format before starting kafka
controller.quorum.voters static config when using a static quorum, or the controllers who format with --initial-controllers/--standalone in a dynamic quorum setup.kafka-storage format on these nodes before starting kafka will no longer crash startup.meta.properties will be written during kafka broker/controller startup if it doesn't exist already (from formatting)
KafkaRaftServer, we attempt to read the meta.properties filemeta.properties does not exist, write meta.properties V2 with node.id and directory.idmeta.properties must be atomic.cluster.id, kafka behaves as it does today:Introduce a metadata record for cluster id
kafka-features upgrade --release-version/--feature flag and defaults to the latest production MV. If the MV at format time supports this feature, a ClusterIdRecord must be written as part of the bootstrap metadata checkpoint.VotersRecord meta.properties, which can occur if a majority of voters do not have a clusterIdcluster.id in order to elect a leader (i.e. the cluster id for all voters can be empty and elect a leader)When nodes discover cluster.id from the metadata publishing pipeline, they persist it to meta.properties
meta.properties do not contain cluster.id will discover this value via the metadata publishing pipelinePros:
Cons:
Since this feature is associated with a new metadata record and MetadataVersion, broker bootstrapping with cluster ID is required on all MVs < X where X is the first MV that supports this feature. Because some MetadataVersion is resolved during each node's formatting, we can determine at format time if a ClusterIdRecord is needed as part of a controller's 0-0/bootstrap.checkpoint.
Continue to persist cluster id in meta.properties but have KRaft discover it + persist it via FetchResponse
meta.properties is its source of truth, if this file is deleted or is changed, it means the broker/observer controller can join another cluster.