This is an automated email from the ASF dual-hosted git repository. sanpwc pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push: new 938e09d0306 IGNITE-25987 Add public CLI API for partition restart with cleanup functionality (#6513) 938e09d0306 is described below commit 938e09d0306be3f0d87d88f1714f55102e102840 Author: Cyrill <cyrill.si...@gmail.com> AuthorDate: Tue Sep 2 10:00:36 2025 +0300 IGNITE-25987 Add public CLI API for partition restart with cleanup functionality (#6513) --- .../restart/ItRestartPartitionsTest.java | 71 +++++++++++++++++++++- .../recovery/restart/RestartPartitionsCall.java | 12 +++- .../restart/RestartPartitionsCallInput.java | 22 ++++++- .../ignite/internal/cli/commands/Options.java | 5 ++ .../partitions/restart/RestartPartitionsMixin.java | 10 +++ .../partitions/restart/RestartPartitionsTest.java | 64 +++++++++++++++++++ 6 files changed, 179 insertions(+), 5 deletions(-) diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/ItRestartPartitionsTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/ItRestartPartitionsTest.java index 487abd5975f..f795455d85d 100644 --- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/ItRestartPartitionsTest.java +++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/ItRestartPartitionsTest.java @@ -23,12 +23,14 @@ import static org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_ import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_NODE_NAMES_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_PARTITION_IDS_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_TABLE_NAME_OPTION; +import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_WITH_CLEANUP_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_ZONE_NAME_OPTION; import static org.apache.ignite.lang.util.IgniteNameUtils.canonicalName; import org.apache.ignite.Ignite; import org.apache.ignite.internal.cli.CliIntegrationTest; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIf; @@ -44,10 +46,11 @@ public abstract class ItRestartPartitionsTest extends CliIntegrationTest { @BeforeAll public void createTables() { - sql(String.format("CREATE ZONE \"%s\" storage profiles ['%s']", ZONE, DEFAULT_AIPERSIST_PROFILE_NAME)); + sql(String.format("CREATE ZONE \"%s\" (REPLICAS %s) storage profiles ['%s']", ZONE, 3, DEFAULT_AIPERSIST_PROFILE_NAME)); sql(String.format("CREATE TABLE PUBLIC.\"%s\" (id INT PRIMARY KEY, val INT) ZONE \"%s\"", TABLE_NAME, ZONE)); } + @Disabled("https://issues.apache.org/jira/browse/IGNITE-26337") @Test public void testRestartAllPartitions() { execute(CLUSTER_URL_OPTION, NODE_URL, @@ -161,4 +164,70 @@ public abstract class ItRestartPartitionsTest extends CliIntegrationTest { DEFAULT_PARTITION_COUNT )); } + + @Disabled("https://issues.apache.org/jira/browse/IGNITE-26337") + @Test + public void testRestartAllPartitionsWithCleanup() { + execute(CLUSTER_URL_OPTION, NODE_URL, + RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME, + RECOVERY_ZONE_NAME_OPTION, ZONE, + RECOVERY_WITH_CLEANUP_OPTION + ); + + assertErrOutputIsEmpty(); + assertOutputContains("Successfully restarted partitions."); + } + + @Disabled("https://issues.apache.org/jira/browse/IGNITE-26337") + @Test + public void testRestartSpecifiedPartitionsWithCleanup() { + execute(CLUSTER_URL_OPTION, NODE_URL, + RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME, + RECOVERY_ZONE_NAME_OPTION, ZONE, + RECOVERY_PARTITION_IDS_OPTION, "1,2", + RECOVERY_WITH_CLEANUP_OPTION + ); + + assertErrOutputIsEmpty(); + assertOutputContains("Successfully restarted partitions."); + } + + @Disabled("https://issues.apache.org/jira/browse/IGNITE-26337") + @Test + public void testRestartPartitionsByNodesWithCleanup() { + String nodeNames = CLUSTER.runningNodes() + .limit(initialNodes() - 1) + .map(Ignite::name) + .collect(joining(",")); + + execute(CLUSTER_URL_OPTION, NODE_URL, + RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME, + RECOVERY_ZONE_NAME_OPTION, ZONE, + RECOVERY_PARTITION_IDS_OPTION, "1,2", + RECOVERY_NODE_NAMES_OPTION, nodeNames, + RECOVERY_WITH_CLEANUP_OPTION + ); + + assertErrOutputIsEmpty(); + assertOutputContains("Successfully restarted partitions."); + } + + @Test + public void testRestartPartitionsByNodesWithCleanupNoExecutorInNodes() { + String nodeNames = CLUSTER.runningNodes() + .skip(1) + .map(Ignite::name) + .collect(joining(",")); + + execute(CLUSTER_URL_OPTION, NODE_URL, + RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME, + RECOVERY_ZONE_NAME_OPTION, ZONE, + RECOVERY_PARTITION_IDS_OPTION, "1,2", + RECOVERY_NODE_NAMES_OPTION, nodeNames, + RECOVERY_WITH_CLEANUP_OPTION + ); + + assertErrOutputIsEmpty(); + assertOutputContains("Successfully restarted partitions."); + } } diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCall.java index 5c0b1f82b23..c45b98a5016 100644 --- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCall.java +++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCall.java @@ -50,7 +50,11 @@ public class RestartPartitionsCall implements Call<RestartPartitionsCallInput, S command.setNodeNames(input.nodeNames()); command.setZoneName(input.zoneName()); - client.restartZonePartitions(command); + if (input.withCleanup()) { + client.restartZonePartitionsWithCleanup(command); + } else { + client.restartZonePartitions(command); + } } else { RestartPartitionsRequest command = new RestartPartitionsRequest(); @@ -59,7 +63,11 @@ public class RestartPartitionsCall implements Call<RestartPartitionsCallInput, S command.setTableName(input.tableName()); command.setZoneName(input.zoneName()); - client.restartPartitions(command); + if (input.withCleanup()) { + client.restartPartitionsWithCleanup(command); + } else { + client.restartPartitions(command); + } } return DefaultCallOutput.success( diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCallInput.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCallInput.java index e541bef10c5..495a87fad70 100644 --- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCallInput.java +++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/restart/RestartPartitionsCallInput.java @@ -34,6 +34,8 @@ public class RestartPartitionsCallInput implements CallInput { private final List<Integer> partitionIds; + private final boolean withCleanup; + /** Cluster url. */ public String clusterUrl() { return clusterUrl; @@ -59,18 +61,25 @@ public class RestartPartitionsCallInput implements CallInput { return nodeNames; } + /** Whether to restart partitions with cleanup. */ + public boolean withCleanup() { + return withCleanup; + } + private RestartPartitionsCallInput( String clusterUrl, String zoneName, String tableName, @Nullable List<Integer> partitionIds, - @Nullable List<String> nodeNames + @Nullable List<String> nodeNames, + boolean withCleanup ) { this.clusterUrl = clusterUrl; this.zoneName = zoneName; this.tableName = tableName; this.partitionIds = partitionIds == null ? List.of() : List.copyOf(partitionIds); this.nodeNames = nodeNames == null ? List.of() : List.copyOf(nodeNames); + this.withCleanup = withCleanup; } /** Returns {@link RestartPartitionsCallInput} with specified arguments. */ @@ -80,6 +89,7 @@ public class RestartPartitionsCallInput implements CallInput { .tableName(restartArgs.tableName()) .partitionIds(restartArgs.partitionIds()) .nodeNames(restartArgs.nodeNames()) + .withCleanup(restartArgs.withCleanup()) .clusterUrl(clusterUrl) .build(); } @@ -107,6 +117,8 @@ public class RestartPartitionsCallInput implements CallInput { @Nullable private List<String> nodeNames; + private boolean withCleanup; + /** Set cluster URL. */ RestartPartitionsCallInputBuilder clusterUrl(String clusterUrl) { this.clusterUrl = clusterUrl; @@ -137,9 +149,15 @@ public class RestartPartitionsCallInput implements CallInput { return this; } + /** Set whether to restart partitions with cleanup. */ + RestartPartitionsCallInputBuilder withCleanup(boolean withCleanup) { + this.withCleanup = withCleanup; + return this; + } + /** Build {@link RestartPartitionsCallInput}. */ RestartPartitionsCallInput build() { - return new RestartPartitionsCallInput(clusterUrl, zoneName, tableName, partitionIds, nodeNames); + return new RestartPartitionsCallInput(clusterUrl, zoneName, tableName, partitionIds, nodeNames, withCleanup); } } } diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java index e225a3e536c..a64ebf215fc 100644 --- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java +++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java @@ -307,6 +307,11 @@ public enum Options { + "'--cluster-management-group node1, node2' " + "to specify more than one node) that will host the Cluster Management Group."; + public static final String RECOVERY_WITH_CLEANUP_OPTION = "--with-cleanup"; + + public static final String RECOVERY_WITH_CLEANUP_OPTION_DESC = "Restarts partitions, preceded by a storage cleanup. " + + "This will remove all data from the partition storages before restart."; + /** Old cluster endpoint URL option long name. */ public static final String RECOVERY_OLD_CLUSTER_URL_OPTION = "--old-cluster-url"; diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsMixin.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsMixin.java index 6527418d881..5ec51ebc23f 100644 --- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsMixin.java +++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsMixin.java @@ -23,6 +23,8 @@ import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_PARTITION_IDS_OPTION_DESC; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_TABLE_NAME_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_TABLE_NAME_OPTION_DESC; +import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_WITH_CLEANUP_OPTION; +import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_WITH_CLEANUP_OPTION_DESC; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_ZONE_NAME_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_ZONE_NAME_OPTION_DESC; @@ -44,6 +46,9 @@ public class RestartPartitionsMixin { @Option(names = RECOVERY_TABLE_NAME_OPTION, description = RECOVERY_TABLE_NAME_OPTION_DESC, required = true) private String tableName; + @Option(names = RECOVERY_WITH_CLEANUP_OPTION, description = RECOVERY_WITH_CLEANUP_OPTION_DESC) + private boolean withCleanup; + /** Returns name of the zone to restart partitions of. */ public String zoneName() { return zoneName; @@ -65,4 +70,9 @@ public class RestartPartitionsMixin { public List<String> nodeNames() { return nodeNames; } + + /** Returns whether to restart partitions with cleanup. */ + public boolean withCleanup() { + return withCleanup; + } } diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsTest.java index bf69f75af2c..cc5a9f2d689 100644 --- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsTest.java +++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/partitions/restart/RestartPartitionsTest.java @@ -21,6 +21,7 @@ import static org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_ import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_NODE_NAMES_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_PARTITION_IDS_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_TABLE_NAME_OPTION; +import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_WITH_CLEANUP_OPTION; import static org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_ZONE_NAME_OPTION; import static org.apache.ignite.internal.lang.IgniteSystemProperties.colocationEnabled; import static org.mockserver.matchers.MatchType.ONLY_MATCHING_FIELDS; @@ -38,10 +39,12 @@ import org.mockserver.model.MediaType; /** Unit tests for {@link RestartPartitionsCommand}. */ public class RestartPartitionsTest extends IgniteCliInterfaceTestBase { private static String PARTITIONS_RESTART_ENDPOINT; + private static String PARTITIONS_RESTART_ENDPOINT_WITH_CLEANUP; @BeforeAll public static void beforeAll() { PARTITIONS_RESTART_ENDPOINT = "partitions/restart"; + PARTITIONS_RESTART_ENDPOINT_WITH_CLEANUP = "partitions/restartWithCleanup"; } @Test @@ -127,6 +130,67 @@ public class RestartPartitionsTest extends IgniteCliInterfaceTestBase { assertOutputIs("Successfully restarted partitions."); } + @Test + @DisplayName("Restart all partitions with cleanup") + void restartAllPartitionsWithCleanup() { + String expectedSentContent; + + if (colocationEnabled()) { + expectedSentContent = "{" + + " \"zoneName\" : \"zone_NAME\"" + + "}"; + + } else { + expectedSentContent = "{" + + " \"tableName\" : \"table_NAME\"," + + " \"zoneName\" : \"zone_NAME\"" + + "}"; + } + + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/recovery/" + PARTITIONS_RESTART_ENDPOINT_WITH_CLEANUP) + .withBody(json(expectedSentContent)) + .withContentType(MediaType.APPLICATION_JSON_UTF_8) + ) + .respond(response(null)); + + execute(CLUSTER_URL_OPTION, mockUrl, + RECOVERY_TABLE_NAME_OPTION, "table_NAME", + RECOVERY_ZONE_NAME_OPTION, "zone_NAME", + RECOVERY_WITH_CLEANUP_OPTION + ); + + assertErrOutputIsEmpty(); + assertOutputIs("Successfully restarted partitions."); + } + + @Test + @DisplayName("Restart specified partitions with cleanup") + void restartSpecifiedPartitionsWithCleanup() { + String expectedSentContent = "{\"partitionIds\" : [1,2]}"; + + clientAndServer + .when(request() + .withMethod("POST") + .withPath("/management/v1/recovery/" + PARTITIONS_RESTART_ENDPOINT_WITH_CLEANUP) + .withBody(json(expectedSentContent, ONLY_MATCHING_FIELDS)) + .withContentType(MediaType.APPLICATION_JSON_UTF_8) + ) + .respond(response(null)); + + execute(CLUSTER_URL_OPTION, mockUrl, + RECOVERY_TABLE_NAME_OPTION, "table_NAME", + RECOVERY_ZONE_NAME_OPTION, "zone_NAME", + RECOVERY_PARTITION_IDS_OPTION, "1,2", + RECOVERY_WITH_CLEANUP_OPTION + ); + + assertErrOutputIsEmpty(); + assertOutputIs("Successfully restarted partitions."); + } + @Override protected void execute(String... args) { String[] fullArgs = ArrayUtils.concat(new String[] {"recovery", "partitions", "restart"}, args);