Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1d2999f
Add pre-migration state for the KRaft controller
mumrah Mar 16, 2023
64e9df9
Add integration test for pre-migration
mumrah Mar 16, 2023
4d1a9cb
Add migration state to MetadataImage
mumrah Mar 16, 2023
078fcdf
checkstyle
mumrah Mar 16, 2023
96eff83
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Mar 16, 2023
c9c3bfc
Only write ZkMigrationStateRecord if we've seen one
mumrah Mar 17, 2023
f9608cf
Rename some things
mumrah Mar 17, 2023
5fa9671
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Mar 17, 2023
8f4b5aa
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Mar 17, 2023
e57f996
Make sure we don't start a migration when we're in KRaft mode
mumrah Mar 17, 2023
da3f9dc
Add ZkMigrationState metric
mumrah Mar 17, 2023
cef8e6a
Fix unit tests
mumrah Mar 20, 2023
4c68c5d
Handle the case of updating metadata.version
mumrah Mar 21, 2023
35165fb
Handle PRE_MIGRATION record when upgrading from 3.4
mumrah Mar 22, 2023
2314a52
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Mar 27, 2023
b3f52b0
Better log message in ZkMigrationControlManager
mumrah Mar 27, 2023
2cf031b
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Mar 28, 2023
b52e5a1
Remove ControllerMetrics
mumrah Mar 28, 2023
6f70a14
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Mar 30, 2023
ce8fe6c
Move ZkMigrationControlManager into FeatureControlManager
mumrah Mar 30, 2023
71727a6
cleanup
mumrah Mar 30, 2023
f56fc6f
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Mar 31, 2023
308798b
PR feedback
mumrah Apr 12, 2023
301296b
More PR feedback
mumrah Apr 14, 2023
a49ddc6
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Apr 14, 2023
7b93271
Add transition to POST_MIGRATION
mumrah Apr 17, 2023
5ae5363
Update system tests for recent changes
mumrah Apr 17, 2023
c183eef
Fix some logic in the migration driver
mumrah Apr 18, 2023
3d8f3b9
Merge remote-tracking branch 'origin/trunk' into KAFKA-14805-pre-migr…
mumrah Apr 18, 2023
5107fb4
Fix some tests
mumrah Apr 19, 2023
8923c4d
PR feedback
mumrah Apr 20, 2023
e43d924
Fix broker count in test
mumrah Apr 20, 2023
816e976
Fix unit test
mumrah Apr 22, 2023
bb6093e
Add another test case for old MV
mumrah Apr 24, 2023
15608e4
Don't allow KRaft brokers to register during pre-migration
mumrah Apr 25, 2023
a4422d6
Fix one more activation case, add tests
mumrah Apr 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion checkstyle/suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@
<suppress checks="CyclomaticComplexity"
files="(ClientQuotasImage|KafkaEventQueue|MetadataDelta|QuorumController|ReplicationControlManager).java"/>
<suppress checks="NPathComplexity"
files="(ClientQuotasImage|KafkaEventQueue|ReplicationControlManager|FeatureControlManager|KRaftMigrationDriver).java"/>
files="(ClientQuotasImage|KafkaEventQueue|MetadataDelta|ReplicationControlManager|FeatureControlManager|KRaftMigrationDriver).java"/>
<suppress checks="(NPathComplexity|ClassFanOutComplexity|CyclomaticComplexity|ClassDataAbstractionCoupling|LocalVariableName|MemberName|ParameterName|MethodLength|JavaNCSS|AvoidStarImport)"
files="metadata[\\/]src[\\/](generated|generated-test)[\\/].+.java$"/>
<suppress checks="BooleanExpressionComplexity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import kafka.test.junit.ClusterTestExtensions
import kafka.test.junit.ZkClusterInvocationContext.ZkClusterInstance
import kafka.testkit.{KafkaClusterTestKit, TestKitNodes}
import kafka.utils.{PasswordEncoder, TestUtils}
import org.apache.kafka.clients.admin._
import org.apache.kafka.clients.admin.{Admin, AlterClientQuotasResult, AlterConfigOp, AlterConfigsResult, ConfigEntry, CreateTopicsOptions, NewTopic}
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerRecord}
import org.apache.kafka.common.Uuid
import org.apache.kafka.common.acl.AclOperation.{DESCRIBE, READ, WRITE}
import org.apache.kafka.common.acl.AclPermissionType.ALLOW
import org.apache.kafka.common.acl.{AccessControlEntry, AclBinding}
import org.apache.kafka.common.config.{ConfigResource, TopicConfig}
import org.apache.kafka.common.errors.TimeoutException
import org.apache.kafka.common.quota.{ClientQuotaAlteration, ClientQuotaEntity}
import org.apache.kafka.common.resource.PatternType.{LITERAL, PREFIXED}
import org.apache.kafka.common.resource.ResourcePattern
Expand All @@ -43,13 +44,13 @@ import org.apache.kafka.metadata.authorizer.StandardAcl
import org.apache.kafka.metadata.migration.ZkMigrationLeadershipState
import org.apache.kafka.raft.RaftConfig
import org.apache.kafka.server.common.{ApiMessageAndVersion, MetadataVersion, ProducerIdsBlock}
import org.junit.jupiter.api.Assertions.{assertEquals, assertFalse, assertNotNull, assertTrue}
import org.junit.jupiter.api.Assertions.{assertEquals, assertFalse, assertNotNull, assertThrows, assertTrue}
import org.junit.jupiter.api.Timeout
import org.junit.jupiter.api.extension.ExtendWith
import org.slf4j.LoggerFactory

import java.util
import java.util.concurrent.TimeUnit
import java.util.concurrent.{ExecutionException, TimeUnit}
import java.util.{Properties, UUID}
import scala.collection.Seq
import scala.jdk.CollectionConverters._
Expand Down Expand Up @@ -120,6 +121,11 @@ class ZkMigrationIntegrationTest {
}
}

/**
* Test ZkMigrationClient against a real ZooKeeper-backed Kafka cluster. This test creates a ZK cluster
* and modifies data using AdminClient. The ZkMigrationClient is then used to read the metadata from ZK
* as would happen during a migration. The generated records are then verified.
*/
@ClusterTest(brokers = 3, clusterType = Type.ZK, metadataVersion = MetadataVersion.IBP_3_4_IV0)
def testMigrate(clusterInstance: ClusterInstance): Unit = {
val admin = clusterInstance.createAdminClient()
Expand Down Expand Up @@ -323,4 +329,23 @@ class ZkMigrationIntegrationTest {
assertTrue(firstProducerIdBlock.firstProducerId() < producerIdBlock.firstProducerId())
}
}

/**
* Start a KRaft cluster with migrations enabled, verify that the controller does not accept metadata changes
* through the RPCs
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also explain in this comment why the migration does not proceed beyond premigration (I assume it's because we're waiting for brokers and we don't have any due to brokers = 0)

@ClusterTest(clusterType = Type.KRAFT, brokers = 0, controllers = 1, metadataVersion = MetadataVersion.IBP_3_4_IV0,
Comment thread
mumrah marked this conversation as resolved.
Outdated
serverProperties = Array(new ClusterConfigProperty(key = "zookeeper.metadata.migration.enable", value = "true")))
def testPreMigrationMode(clusterInstance: ClusterInstance): Unit = {
Comment thread
mumrah marked this conversation as resolved.
Outdated
val adminClient = clusterInstance.createAdminClient()
val result = adminClient.createTopics(
java.util.Collections.singleton(new NewTopic("test", 10, 1.asInstanceOf[Short])),
new CreateTopicsOptions().timeoutMs(1000)
)

assertEquals(
classOf[TimeoutException],
assertThrows(classOf[ExecutionException], () => result.all().get(5, TimeUnit.MINUTES)).getCause.getClass
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import kafka.test.ClusterInstance
import kafka.test.annotation.{ClusterConfigProperty, ClusterTest, Type}
import kafka.test.junit.ClusterTestExtensions
import org.apache.kafka.clients.ClientResponse
import org.apache.kafka.common.errors.TimeoutException
import org.apache.kafka.common.message.{BrokerRegistrationRequestData, BrokerRegistrationResponseData}
import org.apache.kafka.common.metrics.Metrics
import org.apache.kafka.common.network.ListenerName
Expand All @@ -35,7 +36,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.{Tag, Timeout}

import java.util.concurrent.{CompletableFuture, TimeUnit, TimeoutException}
import java.util.concurrent.{CompletableFuture, TimeUnit}

/**
* This test simulates a broker registering with the KRaft quorum under different configurations.
Expand Down
5 changes: 3 additions & 2 deletions core/src/test/scala/unit/kafka/server/MetadataCacheTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import org.apache.kafka.common.requests.UpdateMetadataRequest
import org.apache.kafka.common.security.auth.SecurityProtocol
import org.apache.kafka.common.metadata.{BrokerRegistrationChangeRecord, PartitionRecord, RegisterBrokerRecord, RemoveTopicRecord, TopicRecord}
import org.apache.kafka.common.metadata.RegisterBrokerRecord.{BrokerEndpoint, BrokerEndpointCollection}
import org.apache.kafka.image.{ClusterImage, MetadataDelta, MetadataImage, MetadataProvenance}
import org.apache.kafka.image.{ClusterImage, MetadataDelta, MetadataImage, MetadataProvenance, ZkMigrationStateImage}
import org.apache.kafka.server.common.MetadataVersion

import org.junit.jupiter.api.Assertions._
Expand Down Expand Up @@ -72,7 +72,8 @@ object MetadataCacheTest {
image.clientQuotas(),
image.producerIds(),
image.acls(),
image.scram())
image.scram(),
ZkMigrationStateImage.EMPTY)
val delta = new MetadataDelta.Builder().setImage(partialImage).build()

def toRecord(broker: UpdateMetadataBroker): RegisterBrokerRecord = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4364,7 +4364,8 @@ class ReplicaManagerTest {
ClientQuotasImage.EMPTY,
ProducerIdsImage.EMPTY,
AclsImage.EMPTY,
ScramImage.EMPTY
ScramImage.EMPTY,
ZkMigrationStateImage.EMPTY
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public static class Builder {
private QuorumFeatures quorumFeatures = null;
private MetadataVersion metadataVersion = MetadataVersion.latest();
private MetadataVersion minimumBootstrapVersion = MetadataVersion.MINIMUM_BOOTSTRAP_VERSION;
private ZkMigrationBootstrap zkMigrationBootstrap = null;

Builder setLogContext(LogContext logContext) {
this.logContext = logContext;
Expand All @@ -78,18 +79,29 @@ Builder setMinimumBootstrapVersion(MetadataVersion minimumBootstrapVersion) {
return this;
}

Builder setZkMigrationBootstrap(ZkMigrationBootstrap zkMigrationBootstrap) {
this.zkMigrationBootstrap = zkMigrationBootstrap;
return this;
}

public FeatureControlManager build() {
if (logContext == null) logContext = new LogContext();
if (snapshotRegistry == null) snapshotRegistry = new SnapshotRegistry(logContext);
if (quorumFeatures == null) {
quorumFeatures = new QuorumFeatures(0, new ApiVersions(), QuorumFeatures.defaultFeatureMap(),
Collections.emptyList());
}
return new FeatureControlManager(logContext,
if (zkMigrationBootstrap == null) {
throw new IllegalStateException("ZkMigrationBootstrap cannot be null");
}
return new FeatureControlManager(
logContext,
quorumFeatures,
snapshotRegistry,
metadataVersion,
minimumBootstrapVersion);
minimumBootstrapVersion,
zkMigrationBootstrap
);
}
}

Expand All @@ -115,18 +127,22 @@ public FeatureControlManager build() {
*/
private final MetadataVersion minimumBootstrapVersion;

private final ZkMigrationBootstrap zkMigrationBootstrap;

private FeatureControlManager(
LogContext logContext,
QuorumFeatures quorumFeatures,
SnapshotRegistry snapshotRegistry,
MetadataVersion metadataVersion,
MetadataVersion minimumBootstrapVersion
MetadataVersion minimumBootstrapVersion,
ZkMigrationBootstrap zkMigrationBootstrap
) {
this.log = logContext.logger(FeatureControlManager.class);
this.quorumFeatures = quorumFeatures;
this.finalizedVersions = new TimelineHashMap<>(snapshotRegistry, 0);
this.metadataVersion = new TimelineObject<>(snapshotRegistry, metadataVersion);
this.minimumBootstrapVersion = minimumBootstrapVersion;
this.zkMigrationBootstrap = zkMigrationBootstrap;
}

ControllerResult<Map<String, ApiError>> updateFeatures(
Expand Down Expand Up @@ -266,6 +282,11 @@ private ApiError updateMetadataVersion(
new FeatureLevelRecord()
.setName(MetadataVersion.FEATURE_NAME)
.setFeatureLevel(newVersionLevel), FEATURE_LEVEL_RECORD.lowestSupportedVersion()));

// If we are moving to a version that supports migrations, we need to write the correct state into the log
Comment thread
mumrah marked this conversation as resolved.
Outdated
if (newVersion.isMigrationSupported()) {
zkMigrationBootstrap.bootstrapInitialMigrationState(newVersion, false, recordConsumer);
}
return ApiError.NONE;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void replay(ProducerIdsRecord record) {
ProducerIdsBlock nextBlock = nextProducerBlock.get();
if (nextBlock != ProducerIdsBlock.EMPTY && record.nextProducerId() <= nextBlock.firstProducerId()) {
throw new RuntimeException("Next Producer ID from replayed record (" + record.nextProducerId() + ")" +
" is not greater than current next Producer ID (" + nextBlock.firstProducerId() + ")");
" is not greater than current next Producer ID in block (" + nextBlock + ")");
} else {
nextProducerBlock.set(new ProducerIdsBlock(record.brokerId(), record.nextProducerId(), ProducerIdsBlock.PRODUCER_ID_BLOCK_SIZE));
brokerEpoch.set(record.brokerEpoch());
Expand Down
Loading