mirror of https://github.com/apache/kafka.git
KAFKA-14121: AlterPartitionReassignments API should allow callers to specify the option of preserving the replication factor (#18983)
Reviewers: Christo Lolov <lolovc@amazon.com>, Chia-Ping Tsai <chia7712@gmail.com>, TengYao Chi <kitingiao@gmail.com>
This commit is contained in:
parent
c3a9b0fc84
commit
cbd72cc216
|
@ -1083,6 +1083,13 @@ public interface Admin extends AutoCloseable {
|
||||||
* if the request timed out before the controller could record the new assignments.</li>
|
* if the request timed out before the controller could record the new assignments.</li>
|
||||||
* <li>{@link org.apache.kafka.common.errors.InvalidReplicaAssignmentException}
|
* <li>{@link org.apache.kafka.common.errors.InvalidReplicaAssignmentException}
|
||||||
* If the specified assignment was not valid.</li>
|
* If the specified assignment was not valid.</li>
|
||||||
|
* <li>{@link org.apache.kafka.common.errors.InvalidReplicationFactorException}
|
||||||
|
* If the replication factor was changed in an invalid way.
|
||||||
|
* Only thrown when {@link AlterPartitionReassignmentsOptions#allowReplicationFactorChange()} is set to false and
|
||||||
|
* the request is attempting to alter reassignments (not cancel)</li>
|
||||||
|
* <li>{@link org.apache.kafka.common.errors.UnsupportedVersionException}
|
||||||
|
* If {@link AlterPartitionReassignmentsOptions#allowReplicationFactorChange()} was changed outside the default
|
||||||
|
* and the server does not support the option (e.g due to an old Kafka version).</li>
|
||||||
* <li>{@link org.apache.kafka.common.errors.NoReassignmentInProgressException}
|
* <li>{@link org.apache.kafka.common.errors.NoReassignmentInProgressException}
|
||||||
* If there was an attempt to cancel a reassignment for a partition which was not being reassigned.</li>
|
* If there was an attempt to cancel a reassignment for a partition which was not being reassigned.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
|
|
@ -23,4 +23,25 @@ import java.util.Map;
|
||||||
* Options for {@link AdminClient#alterPartitionReassignments(Map, AlterPartitionReassignmentsOptions)}
|
* Options for {@link AdminClient#alterPartitionReassignments(Map, AlterPartitionReassignmentsOptions)}
|
||||||
*/
|
*/
|
||||||
public class AlterPartitionReassignmentsOptions extends AbstractOptions<AlterPartitionReassignmentsOptions> {
|
public class AlterPartitionReassignmentsOptions extends AbstractOptions<AlterPartitionReassignmentsOptions> {
|
||||||
|
|
||||||
|
private boolean allowReplicationFactorChange = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the option indicating if the alter partition reassignments call should be
|
||||||
|
* allowed to alter the replication factor of a partition.
|
||||||
|
* In cases where it is not allowed, any replication factor change will result in an exception thrown by the API.
|
||||||
|
*/
|
||||||
|
public AlterPartitionReassignmentsOptions allowReplicationFactorChange(boolean allow) {
|
||||||
|
this.allowReplicationFactorChange = allow;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean indicating if the alter partition reassignments should be
|
||||||
|
* allowed to alter the replication factor of a partition.
|
||||||
|
* In cases where it is not allowed, any replication factor change will result in an exception thrown by the API.
|
||||||
|
*/
|
||||||
|
public boolean allowReplicationFactorChange() {
|
||||||
|
return this.allowReplicationFactorChange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3944,6 +3944,7 @@ public class KafkaAdminClient extends AdminClient {
|
||||||
data.topics().add(reassignableTopic);
|
data.topics().add(reassignableTopic);
|
||||||
}
|
}
|
||||||
data.setTimeoutMs(timeoutMs);
|
data.setTimeoutMs(timeoutMs);
|
||||||
|
data.setAllowReplicationFactorChange(options.allowReplicationFactorChange());
|
||||||
return new AlterPartitionReassignmentsRequest.Builder(data);
|
return new AlterPartitionReassignmentsRequest.Builder(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.apache.kafka.common.requests;
|
package org.apache.kafka.common.requests;
|
||||||
|
|
||||||
|
import org.apache.kafka.common.errors.UnsupportedVersionException;
|
||||||
import org.apache.kafka.common.message.AlterPartitionReassignmentsRequestData;
|
import org.apache.kafka.common.message.AlterPartitionReassignmentsRequestData;
|
||||||
import org.apache.kafka.common.message.AlterPartitionReassignmentsRequestData.ReassignableTopic;
|
import org.apache.kafka.common.message.AlterPartitionReassignmentsRequestData.ReassignableTopic;
|
||||||
import org.apache.kafka.common.message.AlterPartitionReassignmentsResponseData;
|
import org.apache.kafka.common.message.AlterPartitionReassignmentsResponseData;
|
||||||
|
@ -42,6 +43,11 @@ public class AlterPartitionReassignmentsRequest extends AbstractRequest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlterPartitionReassignmentsRequest build(short version) {
|
public AlterPartitionReassignmentsRequest build(short version) {
|
||||||
|
if (!data.allowReplicationFactorChange() && version < 1) {
|
||||||
|
throw new UnsupportedVersionException("The broker does not support the AllowReplicationFactorChange " +
|
||||||
|
"option for the AlterPartitionReassignments API. Consider re-sending the request without the " +
|
||||||
|
"option or updating the server version");
|
||||||
|
}
|
||||||
return new AlterPartitionReassignmentsRequest(data, version);
|
return new AlterPartitionReassignmentsRequest(data, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,14 @@
|
||||||
"type": "request",
|
"type": "request",
|
||||||
"listeners": ["broker", "controller"],
|
"listeners": ["broker", "controller"],
|
||||||
"name": "AlterPartitionReassignmentsRequest",
|
"name": "AlterPartitionReassignmentsRequest",
|
||||||
"validVersions": "0",
|
// Version 1 adds the ability to allow/disallow changing the replication factor as part of the request.
|
||||||
|
"validVersions": "0-1",
|
||||||
"flexibleVersions": "0+",
|
"flexibleVersions": "0+",
|
||||||
"fields": [
|
"fields": [
|
||||||
{ "name": "TimeoutMs", "type": "int32", "versions": "0+", "default": "60000",
|
{ "name": "TimeoutMs", "type": "int32", "versions": "0+", "default": "60000",
|
||||||
"about": "The time in ms to wait for the request to complete." },
|
"about": "The time in ms to wait for the request to complete." },
|
||||||
|
{ "name": "AllowReplicationFactorChange", "type": "bool", "versions": "1+", "default": "true",
|
||||||
|
"about": "The option indicating whether changing the replication factor of any given partition as part of this request is a valid move." },
|
||||||
{ "name": "Topics", "type": "[]ReassignableTopic", "versions": "0+",
|
{ "name": "Topics", "type": "[]ReassignableTopic", "versions": "0+",
|
||||||
"about": "The topics to reassign.", "fields": [
|
"about": "The topics to reassign.", "fields": [
|
||||||
{ "name": "Name", "type": "string", "versions": "0+", "entityType": "topicName",
|
{ "name": "Name", "type": "string", "versions": "0+", "entityType": "topicName",
|
||||||
|
|
|
@ -17,11 +17,14 @@
|
||||||
"apiKey": 45,
|
"apiKey": 45,
|
||||||
"type": "response",
|
"type": "response",
|
||||||
"name": "AlterPartitionReassignmentsResponse",
|
"name": "AlterPartitionReassignmentsResponse",
|
||||||
"validVersions": "0",
|
// Version 1 adds the ability to allow/disallow changing the replication factor as part of the request.
|
||||||
|
"validVersions": "0-1",
|
||||||
"flexibleVersions": "0+",
|
"flexibleVersions": "0+",
|
||||||
"fields": [
|
"fields": [
|
||||||
{ "name": "ThrottleTimeMs", "type": "int32", "versions": "0+",
|
{ "name": "ThrottleTimeMs", "type": "int32", "versions": "0+",
|
||||||
"about": "The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota." },
|
"about": "The duration in milliseconds for which the request was throttled due to a quota violation, or zero if the request did not violate any quota." },
|
||||||
|
{ "name": "AllowReplicationFactorChange", "type": "bool", "versions": "1+", "default": "true", "ignorable": true,
|
||||||
|
"about": "The option indicating whether changing the replication factor of any given partition as part of the request was allowed." },
|
||||||
{ "name": "ErrorCode", "type": "int16", "versions": "0+",
|
{ "name": "ErrorCode", "type": "int16", "versions": "0+",
|
||||||
"about": "The top-level error code, or 0 if there was no error." },
|
"about": "The top-level error code, or 0 if there was no error." },
|
||||||
{ "name": "ErrorMessage", "type": "string", "versions": "0+", "nullableVersions": "0+",
|
{ "name": "ErrorMessage", "type": "string", "versions": "0+", "nullableVersions": "0+",
|
||||||
|
|
|
@ -3452,13 +3452,28 @@ class PlaintextAdminIntegrationTest extends BaseAdminIntegrationTest {
|
||||||
val tp1 = new TopicPartition(topic, 0)
|
val tp1 = new TopicPartition(topic, 0)
|
||||||
val tp2 = new TopicPartition(topic, 1)
|
val tp2 = new TopicPartition(topic, 1)
|
||||||
val tp3 = new TopicPartition(topic, 2)
|
val tp3 = new TopicPartition(topic, 2)
|
||||||
createTopic(topic, numPartitions = 4)
|
createTopic(topic, numPartitions = 4, replicationFactor = 2)
|
||||||
|
|
||||||
|
|
||||||
val validAssignment = Optional.of(new NewPartitionReassignment(
|
val validAssignment = Optional.of(new NewPartitionReassignment(
|
||||||
(0 until brokerCount).map(_.asInstanceOf[Integer]).asJava
|
(0 until brokerCount).map(_.asInstanceOf[Integer]).asJava
|
||||||
))
|
))
|
||||||
|
|
||||||
|
val alterOptions = new AlterPartitionReassignmentsOptions
|
||||||
|
alterOptions.allowReplicationFactorChange(false)
|
||||||
|
val alterReplicaNumberTo1 = Optional.of(new NewPartitionReassignment(List(1.asInstanceOf[Integer]).asJava))
|
||||||
|
val alterReplicaNumberTo2 = Optional.of(new NewPartitionReassignment((0 until brokerCount - 1).map(_.asInstanceOf[Integer]).asJava))
|
||||||
|
val alterReplicaNumberTo3 = Optional.of(new NewPartitionReassignment((0 until brokerCount).map(_.asInstanceOf[Integer]).asJava))
|
||||||
|
val alterReplicaResults = client.alterPartitionReassignments(Map(
|
||||||
|
tp1 -> alterReplicaNumberTo1,
|
||||||
|
tp2 -> alterReplicaNumberTo2,
|
||||||
|
tp3 -> alterReplicaNumberTo3,
|
||||||
|
).asJava, alterOptions).values()
|
||||||
|
assertDoesNotThrow(() => alterReplicaResults.get(tp2).get())
|
||||||
|
assertEquals("The replication factor is changed from 2 to 1",
|
||||||
|
assertFutureThrows(classOf[InvalidReplicationFactorException], alterReplicaResults.get(tp1)).getMessage)
|
||||||
|
assertEquals("The replication factor is changed from 2 to 3",
|
||||||
|
assertFutureThrows(classOf[InvalidReplicationFactorException], alterReplicaResults.get(tp3)).getMessage)
|
||||||
|
|
||||||
val nonExistentTp1 = new TopicPartition("topicA", 0)
|
val nonExistentTp1 = new TopicPartition("topicA", 0)
|
||||||
val nonExistentTp2 = new TopicPartition(topic, 4)
|
val nonExistentTp2 = new TopicPartition(topic, 4)
|
||||||
val nonExistentPartitionsResult = client.alterPartitionReassignments(Map(
|
val nonExistentPartitionsResult = client.alterPartitionReassignments(Map(
|
||||||
|
|
|
@ -2058,8 +2058,10 @@ public class ReplicationControlManager {
|
||||||
ControllerResult<AlterPartitionReassignmentsResponseData>
|
ControllerResult<AlterPartitionReassignmentsResponseData>
|
||||||
alterPartitionReassignments(AlterPartitionReassignmentsRequestData request) {
|
alterPartitionReassignments(AlterPartitionReassignmentsRequestData request) {
|
||||||
List<ApiMessageAndVersion> records = BoundedList.newArrayBacked(MAX_RECORDS_PER_USER_OP);
|
List<ApiMessageAndVersion> records = BoundedList.newArrayBacked(MAX_RECORDS_PER_USER_OP);
|
||||||
|
boolean allowRFChange = request.allowReplicationFactorChange();
|
||||||
AlterPartitionReassignmentsResponseData result =
|
AlterPartitionReassignmentsResponseData result =
|
||||||
new AlterPartitionReassignmentsResponseData().setErrorMessage(null);
|
new AlterPartitionReassignmentsResponseData().setErrorMessage(null)
|
||||||
|
.setAllowReplicationFactorChange(allowRFChange);
|
||||||
int successfulAlterations = 0, totalAlterations = 0;
|
int successfulAlterations = 0, totalAlterations = 0;
|
||||||
for (ReassignableTopic topic : request.topics()) {
|
for (ReassignableTopic topic : request.topics()) {
|
||||||
ReassignableTopicResponse topicResponse = new ReassignableTopicResponse().
|
ReassignableTopicResponse topicResponse = new ReassignableTopicResponse().
|
||||||
|
@ -2067,7 +2069,7 @@ public class ReplicationControlManager {
|
||||||
for (ReassignablePartition partition : topic.partitions()) {
|
for (ReassignablePartition partition : topic.partitions()) {
|
||||||
ApiError error = ApiError.NONE;
|
ApiError error = ApiError.NONE;
|
||||||
try {
|
try {
|
||||||
alterPartitionReassignment(topic.name(), partition, records);
|
alterPartitionReassignment(topic.name(), partition, records, allowRFChange);
|
||||||
successfulAlterations++;
|
successfulAlterations++;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
log.info("Unable to alter partition reassignment for " +
|
log.info("Unable to alter partition reassignment for " +
|
||||||
|
@ -2090,7 +2092,8 @@ public class ReplicationControlManager {
|
||||||
|
|
||||||
void alterPartitionReassignment(String topicName,
|
void alterPartitionReassignment(String topicName,
|
||||||
ReassignablePartition target,
|
ReassignablePartition target,
|
||||||
List<ApiMessageAndVersion> records) {
|
List<ApiMessageAndVersion> records,
|
||||||
|
boolean allowRFChange) {
|
||||||
Uuid topicId = topicsByName.get(topicName);
|
Uuid topicId = topicsByName.get(topicName);
|
||||||
if (topicId == null) {
|
if (topicId == null) {
|
||||||
throw new UnknownTopicOrPartitionException("Unable to find a topic " +
|
throw new UnknownTopicOrPartitionException("Unable to find a topic " +
|
||||||
|
@ -2111,7 +2114,7 @@ public class ReplicationControlManager {
|
||||||
if (target.replicas() == null) {
|
if (target.replicas() == null) {
|
||||||
record = cancelPartitionReassignment(topicName, tp, part);
|
record = cancelPartitionReassignment(topicName, tp, part);
|
||||||
} else {
|
} else {
|
||||||
record = changePartitionReassignment(tp, part, target);
|
record = changePartitionReassignment(tp, part, target, allowRFChange);
|
||||||
}
|
}
|
||||||
record.ifPresent(records::add);
|
record.ifPresent(records::add);
|
||||||
}
|
}
|
||||||
|
@ -2175,18 +2178,23 @@ public class ReplicationControlManager {
|
||||||
* @param tp The topic id and partition id.
|
* @param tp The topic id and partition id.
|
||||||
* @param part The existing partition info.
|
* @param part The existing partition info.
|
||||||
* @param target The target partition info.
|
* @param target The target partition info.
|
||||||
|
* @param allowRFChange Validate if partition replication factor can change. KIP-860
|
||||||
*
|
*
|
||||||
* @return The ChangePartitionRecord for the new partition assignment,
|
* @return The ChangePartitionRecord for the new partition assignment,
|
||||||
* or empty if no change is needed.
|
* or empty if no change is needed.
|
||||||
*/
|
*/
|
||||||
Optional<ApiMessageAndVersion> changePartitionReassignment(TopicIdPartition tp,
|
Optional<ApiMessageAndVersion> changePartitionReassignment(TopicIdPartition tp,
|
||||||
PartitionRegistration part,
|
PartitionRegistration part,
|
||||||
ReassignablePartition target) {
|
ReassignablePartition target,
|
||||||
|
boolean allowRFChange) {
|
||||||
// Check that the requested partition assignment is valid.
|
// Check that the requested partition assignment is valid.
|
||||||
PartitionAssignment currentAssignment = new PartitionAssignment(Replicas.toList(part.replicas), part::directory);
|
PartitionAssignment currentAssignment = new PartitionAssignment(Replicas.toList(part.replicas), part::directory);
|
||||||
PartitionAssignment targetAssignment = new PartitionAssignment(target.replicas(), clusterDescriber);
|
PartitionAssignment targetAssignment = new PartitionAssignment(target.replicas(), clusterDescriber);
|
||||||
|
|
||||||
validateManualPartitionAssignment(targetAssignment, OptionalInt.empty());
|
validateManualPartitionAssignment(targetAssignment, OptionalInt.empty());
|
||||||
|
if (!allowRFChange) {
|
||||||
|
validatePartitionReplicationFactorUnchanged(part, target);
|
||||||
|
}
|
||||||
|
|
||||||
List<Integer> currentReplicas = Replicas.toList(part.replicas);
|
List<Integer> currentReplicas = Replicas.toList(part.replicas);
|
||||||
PartitionReassignmentReplicas reassignment =
|
PartitionReassignmentReplicas reassignment =
|
||||||
|
@ -2406,6 +2414,30 @@ public class ReplicationControlManager {
|
||||||
newPartInfo.elr);
|
newPartInfo.elr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validatePartitionReplicationFactorUnchanged(PartitionRegistration part,
|
||||||
|
ReassignablePartition target) {
|
||||||
|
int currentReassignmentSetSize;
|
||||||
|
if (isReassignmentInProgress(part)) {
|
||||||
|
Set<Integer> set = new HashSet<>();
|
||||||
|
for (int r : part.replicas) {
|
||||||
|
set.add(r);
|
||||||
|
}
|
||||||
|
for (int r : part.addingReplicas) {
|
||||||
|
set.add(r);
|
||||||
|
}
|
||||||
|
for (int r : part.removingReplicas) {
|
||||||
|
set.remove(r);
|
||||||
|
}
|
||||||
|
currentReassignmentSetSize = set.size();
|
||||||
|
} else {
|
||||||
|
currentReassignmentSetSize = part.replicas.length;
|
||||||
|
}
|
||||||
|
if (currentReassignmentSetSize != target.replicas().size()) {
|
||||||
|
throw new InvalidReplicationFactorException("The replication factor is changed from " +
|
||||||
|
currentReassignmentSetSize + " to " + target.replicas().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class IneligibleReplica {
|
private static final class IneligibleReplica {
|
||||||
private final int replicaId;
|
private final int replicaId;
|
||||||
private final String reason;
|
private final String reason;
|
||||||
|
|
|
@ -1922,6 +1922,135 @@ public class ReplicationControlManagerTest {
|
||||||
assertEquals(NONE_REASSIGNING, replication.listPartitionReassignments(null, Long.MAX_VALUE));
|
assertEquals(NONE_REASSIGNING, replication.listPartitionReassignments(null, Long.MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ApiKeyVersionsSource(apiKey = ApiKeys.ALTER_PARTITION)
|
||||||
|
public void testAlterPartitionDisallowReplicationFactorChange(short version) {
|
||||||
|
MetadataVersion metadataVersion = MetadataVersion.latestTesting();
|
||||||
|
ReplicationControlTestContext ctx = new ReplicationControlTestContext.Builder()
|
||||||
|
.setMetadataVersion(metadataVersion)
|
||||||
|
.build();
|
||||||
|
ReplicationControlManager replication = ctx.replicationControl;
|
||||||
|
ctx.registerBrokers(0, 1, 2, 3);
|
||||||
|
ctx.unfenceBrokers(0, 1, 2, 3);
|
||||||
|
ctx.createTestTopic("foo", new int[][] {new int[] {0, 1, 2}, new int[] {0, 1, 2}, new int[] {0, 1, 2}});
|
||||||
|
|
||||||
|
ControllerResult<AlterPartitionReassignmentsResponseData> alterResult =
|
||||||
|
replication.alterPartitionReassignments(
|
||||||
|
new AlterPartitionReassignmentsRequestData().setTopics(singletonList(
|
||||||
|
new ReassignableTopic().setName("foo").setPartitions(asList(
|
||||||
|
new ReassignablePartition().setPartitionIndex(0).
|
||||||
|
setReplicas(asList(1, 2, 3)),
|
||||||
|
new ReassignablePartition().setPartitionIndex(1).
|
||||||
|
setReplicas(asList(0, 1)),
|
||||||
|
new ReassignablePartition().setPartitionIndex(2).
|
||||||
|
setReplicas(asList(0, 1, 2, 3)))))).
|
||||||
|
setAllowReplicationFactorChange(false));
|
||||||
|
assertEquals(new AlterPartitionReassignmentsResponseData().
|
||||||
|
setErrorMessage(null).setAllowReplicationFactorChange(false).setResponses(singletonList(
|
||||||
|
new ReassignableTopicResponse().setName("foo").setPartitions(asList(
|
||||||
|
new ReassignablePartitionResponse().setPartitionIndex(0).
|
||||||
|
setErrorMessage(null),
|
||||||
|
new ReassignablePartitionResponse().setPartitionIndex(1).
|
||||||
|
setErrorCode(INVALID_REPLICATION_FACTOR.code()).
|
||||||
|
setErrorMessage("The replication factor is changed from 3 to 2"),
|
||||||
|
new ReassignablePartitionResponse().setPartitionIndex(2).
|
||||||
|
setErrorCode(INVALID_REPLICATION_FACTOR.code()).
|
||||||
|
setErrorMessage("The replication factor is changed from 3 to 4"))))),
|
||||||
|
alterResult.response());
|
||||||
|
ctx.replay(alterResult.records());
|
||||||
|
ListPartitionReassignmentsResponseData currentReassigning =
|
||||||
|
new ListPartitionReassignmentsResponseData().setErrorMessage(null).
|
||||||
|
setTopics(singletonList(new OngoingTopicReassignment().
|
||||||
|
setName("foo").setPartitions(singletonList(
|
||||||
|
new OngoingPartitionReassignment().setPartitionIndex(0).
|
||||||
|
setRemovingReplicas(singletonList(0)).
|
||||||
|
setAddingReplicas(singletonList(3)).
|
||||||
|
setReplicas(asList(1, 2, 3, 0))))));
|
||||||
|
assertEquals(currentReassigning, replication.listPartitionReassignments(singletonList(
|
||||||
|
new ListPartitionReassignmentsTopics().setName("foo").
|
||||||
|
setPartitionIndexes(asList(0, 1, 2))), Long.MAX_VALUE));
|
||||||
|
|
||||||
|
// test alter replica factor not allow to change when partition reassignment is ongoing
|
||||||
|
ControllerResult<AlterPartitionReassignmentsResponseData> alterReassigningResult =
|
||||||
|
replication.alterPartitionReassignments(
|
||||||
|
new AlterPartitionReassignmentsRequestData().setTopics(singletonList(
|
||||||
|
new ReassignableTopic().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartition().setPartitionIndex(0).setReplicas(asList(0, 1)))))).
|
||||||
|
setAllowReplicationFactorChange(false));
|
||||||
|
assertEquals(new AlterPartitionReassignmentsResponseData().
|
||||||
|
setErrorMessage(null).setAllowReplicationFactorChange(false).setResponses(singletonList(
|
||||||
|
new ReassignableTopicResponse().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartitionResponse().setPartitionIndex(0).
|
||||||
|
setErrorCode(INVALID_REPLICATION_FACTOR.code()).
|
||||||
|
setErrorMessage("The replication factor is changed from 3 to 2"))))),
|
||||||
|
alterReassigningResult.response());
|
||||||
|
|
||||||
|
ControllerResult<AlterPartitionReassignmentsResponseData> alterReassigningResult2 =
|
||||||
|
replication.alterPartitionReassignments(
|
||||||
|
new AlterPartitionReassignmentsRequestData().setTopics(singletonList(
|
||||||
|
new ReassignableTopic().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartition().setPartitionIndex(0).setReplicas(asList(0, 2, 3)))))).
|
||||||
|
setAllowReplicationFactorChange(false));
|
||||||
|
assertEquals(new AlterPartitionReassignmentsResponseData().
|
||||||
|
setErrorMessage(null).setAllowReplicationFactorChange(false).setResponses(singletonList(
|
||||||
|
new ReassignableTopicResponse().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartitionResponse().setPartitionIndex(0).
|
||||||
|
setErrorMessage(null))))),
|
||||||
|
alterReassigningResult2.response());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ApiKeyVersionsSource(apiKey = ApiKeys.ALTER_PARTITION)
|
||||||
|
public void testDisallowReplicationFactorChangeNoEffectWhenCancelAlterPartition(short version) {
|
||||||
|
MetadataVersion metadataVersion = MetadataVersion.latestTesting();
|
||||||
|
ReplicationControlTestContext ctx = new ReplicationControlTestContext.Builder()
|
||||||
|
.setMetadataVersion(metadataVersion)
|
||||||
|
.build();
|
||||||
|
ReplicationControlManager replication = ctx.replicationControl;
|
||||||
|
ctx.registerBrokers(0, 1, 2, 3);
|
||||||
|
ctx.unfenceBrokers(0, 1, 2, 3);
|
||||||
|
ctx.createTestTopic("foo", new int[][] {new int[] {0, 1, 2}}).topicId();
|
||||||
|
|
||||||
|
ControllerResult<AlterPartitionReassignmentsResponseData> alterResult =
|
||||||
|
replication.alterPartitionReassignments(
|
||||||
|
new AlterPartitionReassignmentsRequestData().setTopics(singletonList(
|
||||||
|
new ReassignableTopic().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartition().setPartitionIndex(0).
|
||||||
|
setReplicas(asList(1, 2, 3)))))));
|
||||||
|
assertEquals(new AlterPartitionReassignmentsResponseData().
|
||||||
|
setErrorMessage(null).setResponses(singletonList(
|
||||||
|
new ReassignableTopicResponse().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartitionResponse().setPartitionIndex(0).setErrorMessage(null))))),
|
||||||
|
alterResult.response());
|
||||||
|
ctx.replay(alterResult.records());
|
||||||
|
ListPartitionReassignmentsResponseData currentReassigning =
|
||||||
|
new ListPartitionReassignmentsResponseData().setErrorMessage(null).
|
||||||
|
setTopics(singletonList(new OngoingTopicReassignment().
|
||||||
|
setName("foo").setPartitions(singletonList(
|
||||||
|
new OngoingPartitionReassignment().setPartitionIndex(0).
|
||||||
|
setRemovingReplicas(singletonList(0)).
|
||||||
|
setAddingReplicas(singletonList(3)).
|
||||||
|
setReplicas(asList(1, 2, 3, 0))))));
|
||||||
|
assertEquals(currentReassigning, replication.listPartitionReassignments(singletonList(
|
||||||
|
new ListPartitionReassignmentsTopics().setName("foo").
|
||||||
|
setPartitionIndexes(asList(0, 1, 2))), Long.MAX_VALUE));
|
||||||
|
|
||||||
|
// test replica factor change check takes no effect when partition reassignment is ongoing
|
||||||
|
ControllerResult<AlterPartitionReassignmentsResponseData> cancelResult =
|
||||||
|
replication.alterPartitionReassignments(
|
||||||
|
new AlterPartitionReassignmentsRequestData().setTopics(singletonList(
|
||||||
|
new ReassignableTopic().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartition().setPartitionIndex(0).setReplicas(null))))).
|
||||||
|
setAllowReplicationFactorChange(false));
|
||||||
|
assertEquals(new AlterPartitionReassignmentsResponseData().setAllowReplicationFactorChange(false).setErrorMessage(null).
|
||||||
|
setResponses(singletonList(
|
||||||
|
new ReassignableTopicResponse().setName("foo").setPartitions(singletonList(
|
||||||
|
new ReassignablePartitionResponse().setPartitionIndex(0).setErrorMessage(null))))),
|
||||||
|
cancelResult.response());
|
||||||
|
ctx.replay(cancelResult.records());
|
||||||
|
assertEquals(NONE_REASSIGNING, replication.listPartitionReassignments(null, Long.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ApiKeyVersionsSource(apiKey = ApiKeys.ALTER_PARTITION)
|
@ApiKeyVersionsSource(apiKey = ApiKeys.ALTER_PARTITION)
|
||||||
public void testAlterPartitionShouldRejectFencedBrokers(short version) {
|
public void testAlterPartitionShouldRejectFencedBrokers(short version) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.apache.kafka.admin.BrokerMetadata;
|
||||||
import org.apache.kafka.clients.admin.Admin;
|
import org.apache.kafka.clients.admin.Admin;
|
||||||
import org.apache.kafka.clients.admin.AdminClientConfig;
|
import org.apache.kafka.clients.admin.AdminClientConfig;
|
||||||
import org.apache.kafka.clients.admin.AlterConfigOp;
|
import org.apache.kafka.clients.admin.AlterConfigOp;
|
||||||
|
import org.apache.kafka.clients.admin.AlterPartitionReassignmentsOptions;
|
||||||
import org.apache.kafka.clients.admin.ConfigEntry;
|
import org.apache.kafka.clients.admin.ConfigEntry;
|
||||||
import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult;
|
import org.apache.kafka.clients.admin.DescribeReplicaLogDirsResult;
|
||||||
import org.apache.kafka.clients.admin.NewPartitionReassignment;
|
import org.apache.kafka.clients.admin.NewPartitionReassignment;
|
||||||
|
@ -182,7 +183,8 @@ public class ReassignPartitionsCommand {
|
||||||
opts.options.valueOf(opts.interBrokerThrottleOpt),
|
opts.options.valueOf(opts.interBrokerThrottleOpt),
|
||||||
opts.options.valueOf(opts.replicaAlterLogDirsThrottleOpt),
|
opts.options.valueOf(opts.replicaAlterLogDirsThrottleOpt),
|
||||||
opts.options.valueOf(opts.timeoutOpt),
|
opts.options.valueOf(opts.timeoutOpt),
|
||||||
Time.SYSTEM);
|
Time.SYSTEM,
|
||||||
|
opts.options.has(opts.disallowReplicationFactorChangeOpt));
|
||||||
} else if (opts.options.has(opts.cancelOpt)) {
|
} else if (opts.options.has(opts.cancelOpt)) {
|
||||||
cancelAssignment(adminClient,
|
cancelAssignment(adminClient,
|
||||||
Utils.readFileAsString(opts.options.valueOf(opts.reassignmentJsonFileOpt)),
|
Utils.readFileAsString(opts.options.valueOf(opts.reassignmentJsonFileOpt)),
|
||||||
|
@ -761,7 +763,8 @@ public class ReassignPartitionsCommand {
|
||||||
Long interBrokerThrottle,
|
Long interBrokerThrottle,
|
||||||
Long logDirThrottle,
|
Long logDirThrottle,
|
||||||
Long timeoutMs,
|
Long timeoutMs,
|
||||||
Time time
|
Time time,
|
||||||
|
boolean disallowReplicationFactorChange
|
||||||
) throws ExecutionException, InterruptedException, JsonProcessingException, TerseException {
|
) throws ExecutionException, InterruptedException, JsonProcessingException, TerseException {
|
||||||
Entry<Map<TopicPartition, List<Integer>>, Map<TopicPartitionReplica, String>> t0 = parseExecuteAssignmentArgs(reassignmentJson);
|
Entry<Map<TopicPartition, List<Integer>>, Map<TopicPartitionReplica, String>> t0 = parseExecuteAssignmentArgs(reassignmentJson);
|
||||||
|
|
||||||
|
@ -796,7 +799,7 @@ public class ReassignPartitionsCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the partition reassignments.
|
// Execute the partition reassignments.
|
||||||
Map<TopicPartition, Throwable> errors = alterPartitionReassignments(adminClient, proposedParts);
|
Map<TopicPartition, Throwable> errors = alterPartitionReassignments(adminClient, proposedParts, disallowReplicationFactorChange);
|
||||||
if (!errors.isEmpty()) {
|
if (!errors.isEmpty()) {
|
||||||
throw new TerseException(
|
throw new TerseException(
|
||||||
String.format("Error reassigning partition(s):%n%s",
|
String.format("Error reassigning partition(s):%n%s",
|
||||||
|
@ -943,13 +946,17 @@ public class ReassignPartitionsCommand {
|
||||||
*
|
*
|
||||||
* @param adminClient The admin client object to use.
|
* @param adminClient The admin client object to use.
|
||||||
* @param reassignments A map from topic names to target replica assignments.
|
* @param reassignments A map from topic names to target replica assignments.
|
||||||
|
* @param disallowReplicationFactorChange Disallow replication factor change or not.
|
||||||
* @return A map from partition objects to error strings.
|
* @return A map from partition objects to error strings.
|
||||||
*/
|
*/
|
||||||
static Map<TopicPartition, Throwable> alterPartitionReassignments(Admin adminClient,
|
static Map<TopicPartition, Throwable> alterPartitionReassignments(Admin adminClient,
|
||||||
Map<TopicPartition, List<Integer>> reassignments) throws InterruptedException {
|
Map<TopicPartition, List<Integer>> reassignments,
|
||||||
|
boolean disallowReplicationFactorChange) throws InterruptedException {
|
||||||
Map<TopicPartition, Optional<NewPartitionReassignment>> args = new HashMap<>();
|
Map<TopicPartition, Optional<NewPartitionReassignment>> args = new HashMap<>();
|
||||||
reassignments.forEach((part, replicas) -> args.put(part, Optional.of(new NewPartitionReassignment(replicas))));
|
reassignments.forEach((part, replicas) -> args.put(part, Optional.of(new NewPartitionReassignment(replicas))));
|
||||||
Map<TopicPartition, KafkaFuture<Void>> results = adminClient.alterPartitionReassignments(args).values();
|
AlterPartitionReassignmentsOptions options = new AlterPartitionReassignmentsOptions();
|
||||||
|
options.allowReplicationFactorChange(!disallowReplicationFactorChange);
|
||||||
|
Map<TopicPartition, KafkaFuture<Void>> results = adminClient.alterPartitionReassignments(args, options).values();
|
||||||
Map<TopicPartition, Throwable> errors = new HashMap<>();
|
Map<TopicPartition, Throwable> errors = new HashMap<>();
|
||||||
for (Entry<TopicPartition, KafkaFuture<Void>> e : results.entrySet()) {
|
for (Entry<TopicPartition, KafkaFuture<Void>> e : results.entrySet()) {
|
||||||
try {
|
try {
|
||||||
|
@ -1485,7 +1492,8 @@ public class ReassignPartitionsCommand {
|
||||||
opts.commandConfigOpt,
|
opts.commandConfigOpt,
|
||||||
opts.interBrokerThrottleOpt,
|
opts.interBrokerThrottleOpt,
|
||||||
opts.replicaAlterLogDirsThrottleOpt,
|
opts.replicaAlterLogDirsThrottleOpt,
|
||||||
opts.timeoutOpt
|
opts.timeoutOpt,
|
||||||
|
opts.disallowReplicationFactorChangeOpt
|
||||||
));
|
));
|
||||||
permittedArgs.put(opts.cancelOpt, Arrays.asList(
|
permittedArgs.put(opts.cancelOpt, Arrays.asList(
|
||||||
isBootstrapServer ? opts.bootstrapServerOpt : opts.bootstrapControllerOpt,
|
isBootstrapServer ? opts.bootstrapServerOpt : opts.bootstrapControllerOpt,
|
||||||
|
|
|
@ -42,6 +42,7 @@ public class ReassignPartitionsCommandOptions extends CommandDefaultOptions {
|
||||||
final OptionSpec<Long> timeoutOpt;
|
final OptionSpec<Long> timeoutOpt;
|
||||||
final OptionSpec<?> additionalOpt;
|
final OptionSpec<?> additionalOpt;
|
||||||
final OptionSpec<?> preserveThrottlesOpt;
|
final OptionSpec<?> preserveThrottlesOpt;
|
||||||
|
final OptionSpec<?> disallowReplicationFactorChangeOpt;
|
||||||
|
|
||||||
public ReassignPartitionsCommandOptions(String[] args) {
|
public ReassignPartitionsCommandOptions(String[] args) {
|
||||||
super(args);
|
super(args);
|
||||||
|
@ -115,6 +116,7 @@ public class ReassignPartitionsCommandOptions extends CommandDefaultOptions {
|
||||||
additionalOpt = parser.accepts("additional", "Execute this reassignment in addition to any " +
|
additionalOpt = parser.accepts("additional", "Execute this reassignment in addition to any " +
|
||||||
"other ongoing ones. This option can also be used to change the throttle of an ongoing reassignment.");
|
"other ongoing ones. This option can also be used to change the throttle of an ongoing reassignment.");
|
||||||
preserveThrottlesOpt = parser.accepts("preserve-throttles", "Do not modify broker or topic throttles.");
|
preserveThrottlesOpt = parser.accepts("preserve-throttles", "Do not modify broker or topic throttles.");
|
||||||
|
disallowReplicationFactorChangeOpt = parser.accepts("disallow-replication-factor-change", "Denies the ability to change a partition's replication factor as part of this reassignment through adding validation against it.");
|
||||||
|
|
||||||
options = parser.parse(args);
|
options = parser.parse(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,7 +249,7 @@ public class ReplicationQuotasTestRig {
|
||||||
|
|
||||||
ReassignPartitionsCommand.executeAssignment(adminClient, false,
|
ReassignPartitionsCommand.executeAssignment(adminClient, false,
|
||||||
ReassignPartitionsCommand.formatAsReassignmentJson(newAssignment, Collections.emptyMap()),
|
ReassignPartitionsCommand.formatAsReassignmentJson(newAssignment, Collections.emptyMap()),
|
||||||
config.throttle, -1L, 10000L, Time.SYSTEM);
|
config.throttle, -1L, 10000L, Time.SYSTEM, false);
|
||||||
|
|
||||||
//Await completion
|
//Await completion
|
||||||
waitForReassignmentToComplete();
|
waitForReassignmentToComplete();
|
||||||
|
|
|
@ -93,6 +93,7 @@ import static org.apache.kafka.tools.reassign.ReassignPartitionsCommand.verifyAs
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@ClusterTestDefaults(brokers = 5, disksPerBroker = 3, serverProperties = {
|
@ClusterTestDefaults(brokers = 5, disksPerBroker = 3, serverProperties = {
|
||||||
|
@ -432,6 +433,23 @@ public class ReassignPartitionsCommandTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ClusterTest
|
||||||
|
public void testDisallowReplicationFactorChange() {
|
||||||
|
createTopics();
|
||||||
|
String assignment = "{\"version\":1,\"partitions\":" +
|
||||||
|
"[{\"topic\":\"foo\",\"partition\":0,\"replicas\":[0,1],\"log_dirs\":[\"any\",\"any\"]}," +
|
||||||
|
"{\"topic\":\"foo\",\"partition\":1,\"replicas\":[0,1,2,3],\"log_dirs\":[\"any\",\"any\",\"any\",\"any\"]}," +
|
||||||
|
"{\"topic\":\"bar\",\"partition\":0,\"replicas\":[3],\"log_dirs\":[\"any\"]}" +
|
||||||
|
"]}";
|
||||||
|
try (Admin admin = clusterInstance.admin()) {
|
||||||
|
assertEquals("Error reassigning partition(s):\n" +
|
||||||
|
"bar-0: The replication factor is changed from 3 to 1\n" +
|
||||||
|
"foo-0: The replication factor is changed from 3 to 2\n" +
|
||||||
|
"foo-1: The replication factor is changed from 3 to 4",
|
||||||
|
assertThrows(TerseException.class, () -> executeAssignment(admin, false, assignment, -1L, -1L, 10000L, Time.SYSTEM, true)).getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createTopics() {
|
private void createTopics() {
|
||||||
try (Admin admin = Admin.create(Collections.singletonMap(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, clusterInstance.bootstrapServers()))) {
|
try (Admin admin = Admin.create(Collections.singletonMap(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, clusterInstance.bootstrapServers()))) {
|
||||||
Map<Integer, List<Integer>> fooReplicasAssignments = new HashMap<>();
|
Map<Integer, List<Integer>> fooReplicasAssignments = new HashMap<>();
|
||||||
|
@ -654,7 +672,7 @@ public class ReassignPartitionsCommandTest {
|
||||||
Long replicaAlterLogDirsThrottle) throws RuntimeException {
|
Long replicaAlterLogDirsThrottle) throws RuntimeException {
|
||||||
try (Admin admin = Admin.create(Collections.singletonMap(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, clusterInstance.bootstrapServers()))) {
|
try (Admin admin = Admin.create(Collections.singletonMap(CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG, clusterInstance.bootstrapServers()))) {
|
||||||
executeAssignment(admin, additional, reassignmentJson,
|
executeAssignment(admin, additional, reassignmentJson,
|
||||||
interBrokerThrottle, replicaAlterLogDirsThrottle, 10000L, Time.SYSTEM);
|
interBrokerThrottle, replicaAlterLogDirsThrottle, 10000L, Time.SYSTEM, false);
|
||||||
} catch (ExecutionException | InterruptedException | JsonProcessingException | TerseException e) {
|
} catch (ExecutionException | InterruptedException | JsonProcessingException | TerseException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,7 @@ public class ReassignPartitionsUnitTest {
|
||||||
reassignments.put(new TopicPartition("foo", 0), asList(0, 1, 3));
|
reassignments.put(new TopicPartition("foo", 0), asList(0, 1, 3));
|
||||||
reassignments.put(new TopicPartition("quux", 0), asList(1, 2, 3));
|
reassignments.put(new TopicPartition("quux", 0), asList(1, 2, 3));
|
||||||
|
|
||||||
Map<TopicPartition, Throwable> reassignmentResult = alterPartitionReassignments(adminClient, reassignments);
|
Map<TopicPartition, Throwable> reassignmentResult = alterPartitionReassignments(adminClient, reassignments, false);
|
||||||
|
|
||||||
assertEquals(1, reassignmentResult.size());
|
assertEquals(1, reassignmentResult.size());
|
||||||
assertEquals(UnknownTopicOrPartitionException.class, reassignmentResult.get(new TopicPartition("quux", 0)).getClass());
|
assertEquals(UnknownTopicOrPartitionException.class, reassignmentResult.get(new TopicPartition("quux", 0)).getClass());
|
||||||
|
@ -606,7 +606,7 @@ public class ReassignPartitionsUnitTest {
|
||||||
"{\"version\":1,\"partitions\":" +
|
"{\"version\":1,\"partitions\":" +
|
||||||
"[{\"topic\":\"foo\",\"partition\":0,\"replicas\":[0,1],\"log_dirs\":[\"any\",\"any\"]}," +
|
"[{\"topic\":\"foo\",\"partition\":0,\"replicas\":[0,1],\"log_dirs\":[\"any\",\"any\"]}," +
|
||||||
"{\"topic\":\"quux\",\"partition\":0,\"replicas\":[2,3,4],\"log_dirs\":[\"any\",\"any\",\"any\"]}" +
|
"{\"topic\":\"quux\",\"partition\":0,\"replicas\":[2,3,4],\"log_dirs\":[\"any\",\"any\",\"any\"]}" +
|
||||||
"]}", -1L, -1L, 10000L, Time.SYSTEM), "Expected reassignment with non-existent topic to fail").getCause().getMessage());
|
"]}", -1L, -1L, 10000L, Time.SYSTEM, false), "Expected reassignment with non-existent topic to fail").getCause().getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,7 +619,7 @@ public class ReassignPartitionsUnitTest {
|
||||||
"{\"version\":1,\"partitions\":" +
|
"{\"version\":1,\"partitions\":" +
|
||||||
"[{\"topic\":\"foo\",\"partition\":0,\"replicas\":[0,1],\"log_dirs\":[\"any\",\"any\"]}," +
|
"[{\"topic\":\"foo\",\"partition\":0,\"replicas\":[0,1],\"log_dirs\":[\"any\",\"any\"]}," +
|
||||||
"{\"topic\":\"foo\",\"partition\":1,\"replicas\":[2,3,4],\"log_dirs\":[\"any\",\"any\",\"any\"]}" +
|
"{\"topic\":\"foo\",\"partition\":1,\"replicas\":[2,3,4],\"log_dirs\":[\"any\",\"any\",\"any\"]}" +
|
||||||
"]}", -1L, -1L, 10000L, Time.SYSTEM), "Expected reassignment with non-existent broker id to fail").getMessage());
|
"]}", -1L, -1L, 10000L, Time.SYSTEM, false), "Expected reassignment with non-existent broker id to fail").getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,7 +670,7 @@ public class ReassignPartitionsUnitTest {
|
||||||
reassignments.put(new TopicPartition("foo", 0), asList(0, 1, 4, 2));
|
reassignments.put(new TopicPartition("foo", 0), asList(0, 1, 4, 2));
|
||||||
reassignments.put(new TopicPartition("bar", 0), asList(2, 3));
|
reassignments.put(new TopicPartition("bar", 0), asList(2, 3));
|
||||||
|
|
||||||
Map<TopicPartition, Throwable> reassignmentResult = alterPartitionReassignments(adminClient, reassignments);
|
Map<TopicPartition, Throwable> reassignmentResult = alterPartitionReassignments(adminClient, reassignments, false);
|
||||||
|
|
||||||
assertTrue(reassignmentResult.isEmpty());
|
assertTrue(reassignmentResult.isEmpty());
|
||||||
assertEquals(String.join(System.lineSeparator(),
|
assertEquals(String.join(System.lineSeparator(),
|
||||||
|
@ -762,7 +762,7 @@ public class ReassignPartitionsUnitTest {
|
||||||
try (MockAdminClient adminClient = new MockAdminClient.Builder().numBrokers(4).build()) {
|
try (MockAdminClient adminClient = new MockAdminClient.Builder().numBrokers(4).build()) {
|
||||||
addTopics(adminClient);
|
addTopics(adminClient);
|
||||||
assertStartsWith("Unexpected character",
|
assertStartsWith("Unexpected character",
|
||||||
assertThrows(AdminOperationException.class, () -> executeAssignment(adminClient, false, "{invalid_json", -1L, -1L, 10000L, Time.SYSTEM)).getMessage());
|
assertThrows(AdminOperationException.class, () -> executeAssignment(adminClient, false, "{invalid_json", -1L, -1L, 10000L, Time.SYSTEM, false)).getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue