-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-13916; Fenced replicas should not be allowed to join the ISR in KRaft (KIP-841, Part 1) #12240
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
KAFKA-13916; Fenced replicas should not be allowed to join the ISR in KRaft (KIP-841, Part 1) #12240
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
27c0c40
add new metadata version
dajac 4a1052c
update BrokerRegistrationRecord and BrokerRegistrationChangeRecord
dajac 85a76a4
wire RegisterBrokerRecord
dajac 8ca98bd
wire BrokerRegistrationChangeRecord
dajac 6da0fed
write BrokerRegistrationChangeRecord with InControlledShutdown state …
dajac e1f2d60
enforce invariants
dajac bbc95da
fix core tests
dajac 3ea2126
refactor + image test
dajac d9a1af8
cleanups and fixes
dajac b65e57f
refactor
dajac db82247
address minor comments
dajac 13e7c0a
add javadoc
dajac bb2bb7c
address comments
dajac f42c8fa
Merge remote-tracking branch 'upstream/trunk' into KAFKA-13916-metada…
dajac 2761eef
use MetadataVersion.IBP_3_0_IV1 by default
dajac 87446cd
refactor and address comments
dajac 1450f43
don't automagically instanciate FeatureControlManager in ClusterContr…
dajac 4bd208c
refactor
dajac c3f8559
cleanup
dajac File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
|
|
||
| package org.apache.kafka.controller; | ||
|
|
||
| import org.apache.kafka.clients.ApiVersions; | ||
| import org.apache.kafka.common.Endpoint; | ||
| import org.apache.kafka.common.Uuid; | ||
| import org.apache.kafka.common.errors.DuplicateBrokerRegistrationException; | ||
|
|
@@ -39,13 +40,15 @@ | |
| import org.apache.kafka.common.utils.Time; | ||
| import org.apache.kafka.metadata.BrokerRegistration; | ||
| import org.apache.kafka.metadata.BrokerRegistrationFencingChange; | ||
| import org.apache.kafka.metadata.BrokerRegistrationInControlledShutdownChange; | ||
| import org.apache.kafka.metadata.BrokerRegistrationReply; | ||
| import org.apache.kafka.metadata.FinalizedControllerFeatures; | ||
| import org.apache.kafka.metadata.VersionRange; | ||
| import org.apache.kafka.metadata.placement.ReplicaPlacer; | ||
| import org.apache.kafka.metadata.placement.StripedReplicaPlacer; | ||
| import org.apache.kafka.metadata.placement.UsableBroker; | ||
| import org.apache.kafka.server.common.ApiMessageAndVersion; | ||
| import org.apache.kafka.server.common.MetadataVersion; | ||
| import org.apache.kafka.timeline.SnapshotRegistry; | ||
| import org.apache.kafka.timeline.TimelineHashMap; | ||
| import org.slf4j.Logger; | ||
|
|
@@ -64,8 +67,8 @@ | |
| import java.util.concurrent.TimeUnit; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import static java.util.Collections.singletonList; | ||
| import static java.util.concurrent.TimeUnit.NANOSECONDS; | ||
| import static org.apache.kafka.common.metadata.MetadataRecordType.REGISTER_BROKER_RECORD; | ||
|
|
||
|
|
||
| /** | ||
|
|
@@ -84,6 +87,7 @@ static class Builder { | |
| private long sessionTimeoutNs = DEFAULT_SESSION_TIMEOUT_NS; | ||
| private ReplicaPlacer replicaPlacer = null; | ||
| private ControllerMetrics controllerMetrics = null; | ||
| private FeatureControlManager featureControl = null; | ||
|
|
||
| Builder setLogContext(LogContext logContext) { | ||
| this.logContext = logContext; | ||
|
|
@@ -120,8 +124,15 @@ Builder setControllerMetrics(ControllerMetrics controllerMetrics) { | |
| return this; | ||
| } | ||
|
|
||
| Builder setFeatureControlManager(FeatureControlManager featureControl) { | ||
| this.featureControl = featureControl; | ||
| return this; | ||
| } | ||
|
|
||
| ClusterControlManager build() { | ||
| if (logContext == null) logContext = new LogContext(); | ||
| if (logContext == null) { | ||
| logContext = new LogContext(); | ||
| } | ||
| if (clusterId == null) { | ||
| clusterId = Uuid.randomUuid().toString(); | ||
| } | ||
|
|
@@ -132,15 +143,27 @@ ClusterControlManager build() { | |
| replicaPlacer = new StripedReplicaPlacer(new Random()); | ||
| } | ||
| if (controllerMetrics == null) { | ||
| throw new RuntimeException("You must specify controllerMetrics"); | ||
| throw new RuntimeException("You must specify ControllerMetrics"); | ||
| } | ||
| if (featureControl == null) { | ||
| featureControl = new FeatureControlManager.Builder(). | ||
| setLogContext(logContext). | ||
| setSnapshotRegistry(snapshotRegistry). | ||
| setQuorumFeatures(new QuorumFeatures(0, new ApiVersions(), | ||
| QuorumFeatures.defaultFeatureMap(), | ||
| singletonList(0))). | ||
| setMetadataVersion(MetadataVersion.latest()). | ||
| build(); | ||
| } | ||
| return new ClusterControlManager(logContext, | ||
| clusterId, | ||
| time, | ||
| snapshotRegistry, | ||
| sessionTimeoutNs, | ||
| replicaPlacer, | ||
| controllerMetrics); | ||
| controllerMetrics, | ||
| featureControl | ||
| ); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -218,14 +241,20 @@ boolean check() { | |
| */ | ||
| private Optional<ReadyBrokersFuture> readyBrokersFuture; | ||
|
|
||
| /** | ||
| * The feature control manager. | ||
| */ | ||
| private final FeatureControlManager featureControl; | ||
|
|
||
| private ClusterControlManager( | ||
| LogContext logContext, | ||
| String clusterId, | ||
| Time time, | ||
| SnapshotRegistry snapshotRegistry, | ||
| long sessionTimeoutNs, | ||
| ReplicaPlacer replicaPlacer, | ||
| ControllerMetrics metrics | ||
| ControllerMetrics metrics, | ||
| FeatureControlManager featureControl | ||
| ) { | ||
| this.logContext = logContext; | ||
| this.clusterId = clusterId; | ||
|
|
@@ -237,6 +266,7 @@ private ClusterControlManager( | |
| this.heartbeatManager = null; | ||
| this.readyBrokersFuture = Optional.empty(); | ||
| this.controllerMetrics = metrics; | ||
| this.featureControl = featureControl; | ||
| } | ||
|
|
||
| ReplicaPlacer replicaPlacer() { | ||
|
|
@@ -279,6 +309,13 @@ Set<Integer> fencedBrokerIds() { | |
| .collect(Collectors.toSet()); | ||
| } | ||
|
|
||
| private short registerBrokerRecordVersion() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about moving this method to MetadataVersion? I find this logic also used in |
||
| if (featureControl.metadataVersion().isInControlledShutdownStateSupported()) | ||
| return (short) 1; | ||
| else | ||
| return (short) 0; | ||
| } | ||
|
|
||
| /** | ||
| * Process an incoming broker registration request. | ||
| */ | ||
|
|
@@ -339,7 +376,7 @@ public ControllerResult<BrokerRegistrationReply> registerBroker( | |
| heartbeatManager.register(brokerId, record.fenced()); | ||
|
|
||
| List<ApiMessageAndVersion> records = new ArrayList<>(); | ||
| records.add(new ApiMessageAndVersion(record, REGISTER_BROKER_RECORD.highestSupportedVersion())); | ||
| records.add(new ApiMessageAndVersion(record, registerBrokerRecordVersion())); | ||
| return ControllerResult.atomicOf(records, new BrokerRegistrationReply(brokerEpoch)); | ||
| } | ||
|
|
||
|
|
@@ -361,7 +398,8 @@ public void replay(RegisterBrokerRecord record) { | |
| BrokerRegistration prevRegistration = brokerRegistrations.put(brokerId, | ||
| new BrokerRegistration(brokerId, record.brokerEpoch(), | ||
| record.incarnationId(), listeners, features, | ||
| Optional.ofNullable(record.rack()), record.fenced())); | ||
| Optional.ofNullable(record.rack()), record.fenced(), | ||
| record.inControlledShutdown())); | ||
| updateMetrics(prevRegistration, brokerRegistrations.get(brokerId)); | ||
| if (heartbeatManager != null) { | ||
| if (prevRegistration != null) heartbeatManager.remove(brokerId); | ||
|
|
@@ -395,12 +433,14 @@ public void replay(UnregisterBrokerRecord record) { | |
|
|
||
| public void replay(FenceBrokerRecord record) { | ||
| replayRegistrationChange(record, record.id(), record.epoch(), | ||
| BrokerRegistrationFencingChange.UNFENCE); | ||
| BrokerRegistrationFencingChange.UNFENCE, | ||
| BrokerRegistrationInControlledShutdownChange.NONE); | ||
| } | ||
|
|
||
| public void replay(UnfenceBrokerRecord record) { | ||
| replayRegistrationChange(record, record.id(), record.epoch(), | ||
| BrokerRegistrationFencingChange.FENCE); | ||
| BrokerRegistrationFencingChange.FENCE, | ||
| BrokerRegistrationInControlledShutdownChange.NONE); | ||
| } | ||
|
|
||
| public void replay(BrokerRegistrationChangeRecord record) { | ||
|
|
@@ -410,15 +450,22 @@ public void replay(BrokerRegistrationChangeRecord record) { | |
| throw new RuntimeException(String.format("Unable to replay %s: unknown " + | ||
| "value for fenced field: %d", record.toString(), record.fenced())); | ||
| } | ||
| Optional<BrokerRegistrationInControlledShutdownChange> inControlledShutdownChange = | ||
| BrokerRegistrationInControlledShutdownChange.fromValue(record.inControlledShutdown()); | ||
| if (!inControlledShutdownChange.isPresent()) { | ||
| throw new RuntimeException(String.format("Unable to replay %s: unknown " + | ||
| "value for inControlledShutdown field: %d", record.toString(), record.inControlledShutdown())); | ||
|
dajac marked this conversation as resolved.
Outdated
|
||
| } | ||
| replayRegistrationChange(record, record.brokerId(), record.brokerEpoch(), | ||
| fencingChange.get()); | ||
| fencingChange.get(), inControlledShutdownChange.get()); | ||
| } | ||
|
|
||
| private void replayRegistrationChange( | ||
| ApiMessage record, | ||
| int brokerId, | ||
| long brokerEpoch, | ||
| BrokerRegistrationFencingChange fencingChange | ||
| BrokerRegistrationFencingChange fencingChange, | ||
| BrokerRegistrationInControlledShutdownChange inControlledShutdownChange | ||
| ) { | ||
| BrokerRegistration curRegistration = brokerRegistrations.get(brokerId); | ||
| if (curRegistration == null) { | ||
|
|
@@ -429,8 +476,10 @@ private void replayRegistrationChange( | |
| "registration with that epoch found", record.toString())); | ||
| } else { | ||
| BrokerRegistration nextRegistration = curRegistration; | ||
| if (fencingChange != BrokerRegistrationFencingChange.NONE) { | ||
| nextRegistration = nextRegistration.cloneWithFencing(fencingChange.asBoolean().get()); | ||
| if (fencingChange != BrokerRegistrationFencingChange.NONE | ||
| || inControlledShutdownChange != BrokerRegistrationInControlledShutdownChange.NONE) { | ||
|
dajac marked this conversation as resolved.
Outdated
|
||
| nextRegistration = nextRegistration.cloneWith( | ||
| fencingChange.asBoolean(), inControlledShutdownChange.asBoolean()); | ||
| } | ||
| if (!curRegistration.equals(nextRegistration)) { | ||
| brokerRegistrations.put(brokerId, nextRegistration); | ||
|
|
@@ -491,6 +540,18 @@ public boolean unfenced(int brokerId) { | |
| return !registration.fenced(); | ||
| } | ||
|
|
||
| public boolean inControlledShutdown(int brokerId) { | ||
|
dajac marked this conversation as resolved.
|
||
| BrokerRegistration registration = brokerRegistrations.get(brokerId); | ||
| if (registration == null) return false; | ||
| return registration.inControlledShutdown(); | ||
| } | ||
|
|
||
| public boolean active(int brokerId) { | ||
| BrokerRegistration registration = brokerRegistrations.get(brokerId); | ||
| if (registration == null) return false; | ||
| return !registration.inControlledShutdown() && !registration.fenced(); | ||
| } | ||
|
|
||
| BrokerHeartbeatManager heartbeatManager() { | ||
| if (heartbeatManager == null) { | ||
| throw new RuntimeException("ClusterControlManager is not active."); | ||
|
|
@@ -549,16 +610,18 @@ public List<ApiMessageAndVersion> next() { | |
| setMaxSupportedVersion(featureEntry.getValue().max()). | ||
| setMinSupportedVersion(featureEntry.getValue().min())); | ||
| } | ||
| List<ApiMessageAndVersion> batch = new ArrayList<>(); | ||
| batch.add(new ApiMessageAndVersion(new RegisterBrokerRecord(). | ||
| RegisterBrokerRecord record = new RegisterBrokerRecord(). | ||
| setBrokerId(brokerId). | ||
| setIncarnationId(registration.incarnationId()). | ||
| setBrokerEpoch(registration.epoch()). | ||
| setEndPoints(endpoints). | ||
| setFeatures(features). | ||
| setRack(registration.rack().orElse(null)). | ||
| setFenced(registration.fenced()), REGISTER_BROKER_RECORD.highestSupportedVersion())); | ||
| return batch; | ||
| setFenced(registration.fenced()); | ||
| if (featureControl.metadataVersion().isInControlledShutdownStateSupported()) { | ||
| record.setInControlledShutdown(registration.inControlledShutdown()); | ||
| } | ||
| return singletonList(new ApiMessageAndVersion(record, registerBrokerRecordVersion())); | ||
| } | ||
| } | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.