This is an automated email from the ASF dual-hosted git repository. sergeychugunov pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new 78f1043 IGNITE-13550 Persistence CLEAN command implementation - Fixes #8408. 78f1043 is described below commit 78f1043330e8c92fc330c1e851e6ce8c4f17e739 Author: Sergey Chugunov <sergey.chugu...@gmail.com> AuthorDate: Tue Nov 3 17:15:24 2020 +0300 IGNITE-13550 Persistence CLEAN command implementation - Fixes #8408. Signed-off-by: Sergey Chugunov <sergey.chugu...@gmail.com> --- .../ignite/internal/commandline/CommandList.java | 5 +- .../internal/commandline/PersistenceCommand.java | 290 +++++++++++++++ .../persistence/CleanAndBackupSubcommandArg.java | 45 +++ .../persistence/PersistenceArguments.java | 98 +++++ .../persistence/PersistenceSubcommands.java | 73 ++++ .../apache/ignite/util/GridCommandHandlerTest.java | 366 +++++++++++++++++++ .../internal/maintenance/MaintenanceProcessor.java | 15 +- ...a => CheckCorruptedCacheStoresCleanAction.java} | 49 +-- .../CorruptedPdsMaintenanceCallback.java | 4 +- .../PersistenceCleanAndBackupSettings.java | 71 ++++ .../persistence/PersistenceCleanAndBackupType.java | 41 +++ .../visor/persistence/PersistenceOperation.java | 43 +++ .../visor/persistence/PersistenceTask.java | 401 +++++++++++++++++++++ .../visor/persistence/PersistenceTaskArg.java | 82 +++++ .../visor/persistence/PersistenceTaskResult.java | 123 +++++++ .../apache/ignite/maintenance/MaintenanceTask.java | 4 +- .../main/resources/META-INF/classnames.properties | 22 +- ...ridCommandHandlerClusterByClassTest_help.output | 24 ++ ...andHandlerClusterByClassWithSSLTest_help.output | 24 ++ 19 files changed, 1723 insertions(+), 57 deletions(-) diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java index 2daaf86..e16acaa 100644 --- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java +++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java @@ -89,7 +89,10 @@ public enum CommandList { SYSTEM_VIEW("--system-view", new SystemViewCommand()), /** Command for printing metric values. */ - METRIC("--metric", new MetricCommand()); + METRIC("--metric", new MetricCommand()), + + /** */ + PERSISTENCE("--persistence", new PersistenceCommand()); /** Private values copy so there's no need in cloning it every time. */ private static final CommandList[] VALUES = CommandList.values(); diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/PersistenceCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/PersistenceCommand.java new file mode 100644 index 0000000..d41269a --- /dev/null +++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/PersistenceCommand.java @@ -0,0 +1,290 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.commandline; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Logger; + +import org.apache.ignite.internal.client.GridClient; +import org.apache.ignite.internal.client.GridClientConfiguration; +import org.apache.ignite.internal.client.GridClientNode; +import org.apache.ignite.internal.commandline.argument.CommandArgUtils; +import org.apache.ignite.internal.commandline.persistence.CleanAndBackupSubcommandArg; +import org.apache.ignite.internal.commandline.persistence.PersistenceArguments; +import org.apache.ignite.internal.commandline.persistence.PersistenceSubcommands; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.visor.persistence.PersistenceCleanAndBackupSettings; +import org.apache.ignite.internal.visor.persistence.PersistenceCleanAndBackupType; +import org.apache.ignite.internal.visor.persistence.PersistenceTask; +import org.apache.ignite.internal.visor.persistence.PersistenceTaskArg; +import org.apache.ignite.internal.visor.persistence.PersistenceTaskResult; +import org.apache.ignite.lang.IgniteBiTuple; + +import static org.apache.ignite.internal.commandline.Command.usage; +import static org.apache.ignite.internal.commandline.CommandList.PERSISTENCE; +import static org.apache.ignite.internal.commandline.CommandLogger.INDENT; +import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode; +import static org.apache.ignite.internal.commandline.persistence.CleanAndBackupSubcommandArg.ALL; +import static org.apache.ignite.internal.commandline.persistence.CleanAndBackupSubcommandArg.CACHES; +import static org.apache.ignite.internal.commandline.persistence.CleanAndBackupSubcommandArg.CORRUPTED; +import static org.apache.ignite.internal.commandline.persistence.PersistenceSubcommands.BACKUP; +import static org.apache.ignite.internal.commandline.persistence.PersistenceSubcommands.CLEAN; +import static org.apache.ignite.internal.commandline.persistence.PersistenceSubcommands.INFO; +import static org.apache.ignite.internal.commandline.persistence.PersistenceSubcommands.of; + +/** */ +public class PersistenceCommand implements Command<PersistenceArguments> { + /** */ + private PersistenceArguments cleaningArgs; + + /** {@inheritDoc} */ + @Override public Object execute(GridClientConfiguration clientCfg, Logger logger) throws Exception { + try (GridClient client = Command.startClient(clientCfg)) { + Optional<GridClientNode> firstNodeOpt = client.compute().nodes().stream().findFirst(); + + if (firstNodeOpt.isPresent()) { + UUID uuid = firstNodeOpt.get().nodeId(); + + PersistenceTaskResult res = executeTaskByNameOnNode(client, + PersistenceTask.class.getName(), + convertArguments(cleaningArgs), + uuid, + clientCfg + ); + + printResult(res, logger); + } + else + logger.warning("No nodes found in topology, command won't be executed."); + } + catch (Throwable t) { + logger.severe("Failed to execute persistence command='" + cleaningArgs.subcommand().text() + "'"); + logger.severe(CommandLogger.errorMessage(t)); + + throw t; + } + + return null; + } + + /** + * Prints result of command execution: information about caches or result of clean/backup command. + * + * @param res {@link PersistenceTaskResult} object with results of command execution. + * @param logger {@link Logger} to print output to. + */ + private void printResult(PersistenceTaskResult res, Logger logger) { + if (!res.inMaintenanceMode()) { + logger.warning("Persistence command can be sent only to node in Maintenance Mode."); + + return; + } + //info command + else if (res.cachesInfo() != null) { + logger.info("Persistent caches found on node:"); + + //sort results so corrupted caches occur in the list at the top + res.cachesInfo().entrySet().stream().sorted((ci0, ci1) -> { + IgniteBiTuple<Boolean, Boolean> t0 = ci0.getValue(); + IgniteBiTuple<Boolean, Boolean> t1 = ci1.getValue(); + + boolean corrupted0 = t0.get1() || t0.get2(); + boolean corrupted1 = t1.get1() || t1.get2(); + + if (corrupted0 && corrupted1) + return 0; + else if (!corrupted0 && !corrupted1) + return 0; + else if (corrupted0 && !corrupted1) + return -1; + else + return 1; + }).forEach( + e -> { + IgniteBiTuple<Boolean, Boolean> t = e.getValue(); + + String status; + + if (!t.get1()) + status = "corrupted - WAL disabled globally."; + else if (!t.get1()) + status = "corrupted - WAL disabled locally."; + else + status = "no corruption."; + + logger.info(INDENT + "cache name: " + e.getKey() + ". Status: " + status); + } + ); + } + //clean command + else if (cleaningArgs != null && cleaningArgs.subcommand() == CLEAN) { + logger.info("Maintenance task is " + (!res.maintenanceTaskCompleted() ? "not " : "") + "fixed."); + + List<String> cleanedCaches = res.handledCaches(); + + if (cleanedCaches != null && !cleanedCaches.isEmpty()) { + String cacheDirNames = String.join(", ", cleanedCaches); + + logger.info("Cache directories were cleaned: [" + cacheDirNames + ']'); + } + + List<String> failedToHandleCaches = res.failedCaches(); + + if (failedToHandleCaches != null && !failedToHandleCaches.isEmpty()) { + String failedToHandleCachesStr = String.join(", ", failedToHandleCaches); + + logger.info("Failed to clean following directories: [" + failedToHandleCachesStr + ']'); + } + } + // backup command + else { + List<String> backupCompletedCaches = res.handledCaches(); + + if (backupCompletedCaches != null && !backupCompletedCaches.isEmpty()) { + String cacheDirNames = String.join(", ", backupCompletedCaches); + + logger.info("Cache data files was backed up to the following directories in node's work directory: [" + + cacheDirNames + ']'); + } + + List<String> backupFailedCaches = res.failedCaches(); + + if (backupFailedCaches != null && !backupFailedCaches.isEmpty()) { + String backupFailedCachesStr = String.join(", ", backupFailedCaches); + + logger.info("Failed to backup the following directories in node's work directory: [" + + backupFailedCachesStr + ']'); + } + } + } + + /** {@inheritDoc} */ + @Override public PersistenceArguments arg() { + return cleaningArgs; + } + + /** {@inheritDoc} */ + @Override public void printUsage(Logger logger) { + final String cacheNames = "cache1,cache2,cache3"; + + usage(logger, "Print information about potentially corrupted caches on local node:", + PERSISTENCE); + usage(logger, "The same information is printed when info subcommand is passed:", PERSISTENCE, + INFO.text()); + + usage(logger, "Clean directories of caches with corrupted data files:", PERSISTENCE, CLEAN.text(), + CORRUPTED.argName()); + usage(logger, "Clean directories of all caches:", PERSISTENCE, CLEAN.text(), + ALL.argName()); + usage(logger, "Clean directories of only given caches:", PERSISTENCE, CLEAN.text(), + CACHES.argName(), cacheNames); + + usage(logger, "Backup data files of corrupted caches only:", PERSISTENCE, BACKUP.text(), + CORRUPTED.argName()); + usage(logger, "Backup data files of all caches:", PERSISTENCE, BACKUP.text(), ALL.argName()); + usage(logger, "Backup data files of only given caches:", PERSISTENCE, BACKUP.text(), + CACHES.argName(), cacheNames); + } + + /** {@inheritDoc} */ + @Override public void parseArguments(CommandArgIterator argIter) { + if (!argIter.hasNextSubArg()) { + cleaningArgs = new PersistenceArguments.Builder(INFO).build(); + + return; + } + + PersistenceSubcommands cmd = of(argIter.nextArg("Expected persistence maintenance action")); + + if (cmd == null) + throw new IllegalArgumentException("Expected correct persistence maintenance action"); + + PersistenceArguments.Builder bldr = new PersistenceArguments.Builder(cmd); + + switch (cmd) { + case BACKUP: + case CLEAN: + CleanAndBackupSubcommandArg cleanAndBackupSubcommandArg = CommandArgUtils.of( + argIter.nextArg("Expected one of subcommand arguments"), CleanAndBackupSubcommandArg.class + ); + + if (cleanAndBackupSubcommandArg == null) + throw new IllegalArgumentException("Expected one of subcommand arguments"); + + bldr.withCleanAndBackupSubcommandArg(cleanAndBackupSubcommandArg); + + if (cleanAndBackupSubcommandArg == ALL || cleanAndBackupSubcommandArg == CORRUPTED) + break; + + if (cleanAndBackupSubcommandArg == CACHES) { + Set<String> caches = argIter.nextStringSet("list of cache names"); + + if (F.isEmpty(caches)) + throw new IllegalArgumentException("Empty list of cache names"); + + bldr.withCacheNames(new ArrayList<>(caches)); + } + + break; + } + + cleaningArgs = bldr.build(); + } + + /** {@inheritDoc} */ + @Override public String name() { + return PERSISTENCE.toCommandName(); + } + + /** */ + private PersistenceTaskArg convertArguments(PersistenceArguments args) { + PersistenceCleanAndBackupSettings cleanSettings = convertCleanAndBackupSettings(args); + + PersistenceTaskArg taskArgs = new PersistenceTaskArg(args.subcommand().operation(), cleanSettings); + + return taskArgs; + } + + /** */ + private PersistenceCleanAndBackupSettings convertCleanAndBackupSettings(PersistenceArguments args) { + if (args.subcommand() == INFO) + return null; + + PersistenceCleanAndBackupType type; + + switch (args.cleanArg()) { + case ALL: + type = PersistenceCleanAndBackupType.ALL; + + break; + case CORRUPTED: + type = PersistenceCleanAndBackupType.CORRUPTED; + + break; + + default: + type = PersistenceCleanAndBackupType.CACHES; + } + + return new PersistenceCleanAndBackupSettings(type, args.cachesList()); + } +} diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/CleanAndBackupSubcommandArg.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/CleanAndBackupSubcommandArg.java new file mode 100644 index 0000000..08a0336 --- /dev/null +++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/CleanAndBackupSubcommandArg.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.commandline.persistence; + +import org.apache.ignite.internal.commandline.argument.CommandArg; + +/** + * {@link PersistenceSubcommands#CLEAN} subcommand arguments. + */ +public enum CleanAndBackupSubcommandArg implements CommandArg { + /** Clean all caches data files. */ + ALL("all"), + /** Clean corrupted caches data files. */ + CORRUPTED("corrupted"), + /** Clean only specified caches data files. */ + CACHES("caches"); + + /** */ + private final String name; + + /** */ + CleanAndBackupSubcommandArg(String name) { + this.name = name; + } + + /** {@inheritDoc} */ + @Override public String argName() { + return name; + } +} diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/PersistenceArguments.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/PersistenceArguments.java new file mode 100644 index 0000000..8971680 --- /dev/null +++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/PersistenceArguments.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.commandline.persistence; + +import java.util.List; + +/** + * Arguments of "persistence cleaning" command. + */ +public class PersistenceArguments { + /** */ + private PersistenceSubcommands cmd; + + /** */ + private CleanAndBackupSubcommandArg cleanArg; + + /** */ + private List<String> cachesList; + + /** + * @param cmd + */ + public PersistenceArguments(PersistenceSubcommands cmd, CleanAndBackupSubcommandArg cleanArg, List<String> cachesList) { + this.cmd = cmd; + this.cleanArg = cleanArg; + this.cachesList = cachesList; + } + + /** */ + public PersistenceSubcommands subcommand() { + return cmd; + } + + /** */ + public CleanAndBackupSubcommandArg cleanArg() { + return cleanArg; + } + + /** */ + public List<String> cachesList() { + return cachesList; + } + + /** Builder of {@link PersistenceArguments}. */ + public static class Builder { + /** */ + private PersistenceSubcommands subCmd; + + /** */ + private CleanAndBackupSubcommandArg cleanSubCmdArg; + + /** */ + private List<String> cacheNames; + + /** + * @param subCmd Subcommand. + */ + public Builder(PersistenceSubcommands subCmd) { + this.subCmd = subCmd; + } + + /** */ + public Builder withCleanAndBackupSubcommandArg(CleanAndBackupSubcommandArg cleanSubCmdArg) { + this.cleanSubCmdArg = cleanSubCmdArg; + + return this; + } + + public Builder withCacheNames(List<String> cacheNames) { + this.cacheNames = cacheNames; + + return this; + } + + public PersistenceArguments build() { + return new PersistenceArguments( + subCmd, + cleanSubCmdArg, + cacheNames + ); + } + } +} diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/PersistenceSubcommands.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/PersistenceSubcommands.java new file mode 100644 index 0000000..d674316 --- /dev/null +++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/persistence/PersistenceSubcommands.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.commandline.persistence; + +import org.apache.ignite.internal.visor.persistence.PersistenceOperation; +import org.jetbrains.annotations.Nullable; + +/** + * + */ +public enum PersistenceSubcommands { + /** Collects information about corrupted caches and cache groups and their file system paths. */ + INFO("info", PersistenceOperation.INFO), + + /** Cleans partition files of corrupted caches and cache groups. */ + CLEAN("clean", PersistenceOperation.CLEAN), + + /** */ + BACKUP("backup", PersistenceOperation.BACKUP); + + /** Subcommand name. */ + private final String name; + + /** Operation this subcommand triggers. */ + private final PersistenceOperation operation; + + /** + * @param name String representation of subcommand. + * @param operation Operation this command triggers. + */ + PersistenceSubcommands(String name, PersistenceOperation operation) { + this.name = name; + this.operation = operation; + } + + /** + * @param strRep String representation of subcommand. + * @return Subcommand for its string representation. + */ + public static @Nullable PersistenceSubcommands of(String strRep) { + for (PersistenceSubcommands cmd : values()) { + if (cmd.text().equals(strRep)) + return cmd; + } + + return null; + } + + /** */ + public String text() { + return name; + } + + /** */ + public PersistenceOperation operation() { + return operation; + } +} diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java index 5970ae4..5557b5e 100644 --- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java +++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java @@ -23,6 +23,7 @@ import java.io.RandomAccessFile; import java.io.Serializable; import java.lang.reflect.Field; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -33,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; @@ -41,6 +43,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -65,6 +68,7 @@ import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.GridJobExecuteResponse; import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.internal.IgniteInternalFuture; +import org.apache.ignite.internal.IgniteNodeAttributes; import org.apache.ignite.internal.TestRecordingCommunicationSpi; import org.apache.ignite.internal.client.GridClientFactory; import org.apache.ignite.internal.client.impl.GridClientImpl; @@ -84,6 +88,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtTxFini import org.apache.ignite.internal.processors.cache.distributed.near.GridNearLockResponse; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxFinishRequest; import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal; +import org.apache.ignite.internal.processors.cache.persistence.CheckpointState; import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager; import org.apache.ignite.internal.processors.cache.persistence.db.IgniteCacheGroupsWithRestartsTest; import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.dumpprocessors.ToFileDumpProcessor; @@ -114,6 +119,7 @@ import org.apache.ignite.transactions.Transaction; import org.apache.ignite.transactions.TransactionRollbackException; import org.apache.ignite.transactions.TransactionTimeoutException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.junit.Test; import static java.io.File.separatorChar; @@ -141,6 +147,7 @@ import static org.apache.ignite.internal.processors.cache.persistence.snapshot.I import static org.apache.ignite.internal.processors.cache.verify.IdleVerifyUtility.GRID_NOT_IDLE_MSG; import static org.apache.ignite.internal.processors.diagnostic.DiagnosticProcessor.DEFAULT_TARGET_FOLDER; import static org.apache.ignite.testframework.GridTestUtils.assertContains; +import static org.apache.ignite.testframework.GridTestUtils.assertThrows; import static org.apache.ignite.testframework.GridTestUtils.runAsync; import static org.apache.ignite.testframework.GridTestUtils.waitForCondition; import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC; @@ -248,6 +255,365 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb assertTrue("Still opened clients: " + new ArrayList<>(clnts.values()), clntsBefore.equals(clntsAfter2)); } + private CacheConfiguration cacheConfiguration(String cacheName) { + CacheConfiguration ccfg = new CacheConfiguration(cacheName) + .setAtomicityMode(TRANSACTIONAL) + .setAffinity(new RendezvousAffinityFunction(false, 32)) + .setBackups(1); + + return ccfg; + } + + /** + * Starts cluster of two nodes and prepares situation of corrupted PDS on node2 + * so it enters maintenance mode on restart. + * + * @param cachesToStart Configurations of caches that should be started in cluster. + * @param cacheToCorrupt Function determining should cache with given name be corrupted or not. + */ + private File startGridAndPutNodeToMaintenance(CacheConfiguration[] cachesToStart, + @Nullable Function<String, Boolean> cacheToCorrupt) throws Exception { + assert cachesToStart != null && cachesToStart.length > 0; + + IgniteEx ig0 = startGrid(0); + IgniteEx ig1 = startGrid(1); + + String ig1Folder = ig1.context().pdsFolderResolver().resolveFolders().folderName(); + File dbDir = U.resolveWorkDirectory(ig1.configuration().getWorkDirectory(), "db", false); + + File ig1LfsDir = new File(dbDir, ig1Folder); + + ig0.cluster().baselineAutoAdjustEnabled(false); + ig0.cluster().state(ACTIVE); + + IgniteCache dfltCache = ig0.getOrCreateCache(cachesToStart[0]); + + if (cachesToStart.length > 1) { + for (int i = 1; i < cachesToStart.length; i++) + ig0.getOrCreateCache(cachesToStart[i]); + } + + for (int k = 0; k < 1000; k++) + dfltCache.put(k, k); + + GridCacheDatabaseSharedManager dbMrg0 = (GridCacheDatabaseSharedManager) ig0.context().cache().context().database(); + GridCacheDatabaseSharedManager dbMrg1 = (GridCacheDatabaseSharedManager) ig1.context().cache().context().database(); + + dbMrg0.forceCheckpoint("cp").futureFor(CheckpointState.FINISHED).get(); + dbMrg1.forceCheckpoint("cp").futureFor(CheckpointState.FINISHED).get(); + + Arrays.stream(cachesToStart) + .map(ccfg -> ccfg.getName()) + .filter(name -> cacheToCorrupt.apply(name)) + .forEach(name -> ig0.cluster().disableWal(name)); + + for (int k = 1000; k < 2000; k++) + dfltCache.put(k, k); + + stopGrid(1); + + File[] cpMarkers = new File(ig1LfsDir, "cp").listFiles(); + + for (File cpMark : cpMarkers) { + if (cpMark.getName().contains("-END")) + cpMark.delete(); + } + + assertThrows(log, () -> startGrid(1), Exception.class, null); + + return ig1LfsDir; + } + + /** + * Test verifies persistence clean command with explicit list of caches to be cleaned. + * + * @throws Exception If failed. + */ + @Test + public void testPersistenceCleanSpecifiedCachesCommand() throws Exception { + String cacheName0 = DEFAULT_CACHE_NAME + "0"; + String cacheName1 = DEFAULT_CACHE_NAME + "1"; + String cacheName2 = DEFAULT_CACHE_NAME + "2"; + String cacheName3 = DEFAULT_CACHE_NAME + "3"; + + String nonExistingCacheName = DEFAULT_CACHE_NAME + "4"; + + File mntcNodeWorkDir = startGridAndPutNodeToMaintenance( + new CacheConfiguration[]{ + cacheConfiguration(cacheName0), + cacheConfiguration(cacheName1), + cacheConfiguration(cacheName2), + cacheConfiguration(cacheName3) + }, + s -> !s.equals(cacheName3)); + + IgniteEx ig1 = startGrid(1); + + String port = ig1.localNode().attribute(IgniteNodeAttributes.ATTR_REST_TCP_PORT).toString(); + + assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--persistence", "clean", "caches", + nonExistingCacheName, + "--host", "localhost", "--port", port)); + + assertEquals(EXIT_CODE_OK, execute("--persistence", "clean", "caches", + cacheName0 + "," + cacheName1, + "--host", "localhost", "--port", port)); + + boolean cleanedEmpty = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(f -> f.getName().contains(cacheName0) || f.getName().contains(cacheName1)) + .map(f -> f.listFiles().length == 1) + .reduce(true, (t, u) -> t && u); + + assertTrue(cleanedEmpty); + + boolean nonCleanedNonEmpty = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(f -> f.getName().contains(cacheName2) || f.getName().contains(cacheName3)) + .map(f -> f.listFiles().length > 1) + .reduce(true, (t, u) -> t && u); + + assertTrue(nonCleanedNonEmpty); + + stopGrid(1); + + ig1 = startGrid(1); + + assertTrue(ig1.context().maintenanceRegistry().isMaintenanceMode()); + + assertEquals(EXIT_CODE_OK, execute("--persistence", "clean", "caches", + cacheName2, + "--host", "localhost", "--port", port)); + + stopGrid(1); + + ig1 = startGrid(1); + + assertFalse(ig1.context().maintenanceRegistry().isMaintenanceMode()); + } + + /** + * Test verifies persistence clean command cleaning only corrupted caches and not touching others. + * + * @throws Exception If failed. + */ + @Test + public void testPersistenceCleanCorruptedCachesCommand() throws Exception { + String cacheName0 = DEFAULT_CACHE_NAME + "0"; + String cacheName1 = DEFAULT_CACHE_NAME + "1"; + String cacheName2 = DEFAULT_CACHE_NAME + "2"; + String cacheName3 = DEFAULT_CACHE_NAME + "3"; + + File mntcNodeWorkDir = startGridAndPutNodeToMaintenance( + new CacheConfiguration[]{ + cacheConfiguration(cacheName0), + cacheConfiguration(cacheName1), + cacheConfiguration(cacheName2), + cacheConfiguration(cacheName3) + }, + s -> !s.equals(cacheName3)); + + IgniteEx ig1 = startGrid(1); + + String port = ig1.localNode().attribute(IgniteNodeAttributes.ATTR_REST_TCP_PORT).toString(); + + assertEquals(EXIT_CODE_OK, execute("--persistence", "clean", "corrupted", + "--host", "localhost", "--port", port)); + + boolean cleanedEmpty = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(f -> + f.getName().contains(cacheName0) + || f.getName().contains(cacheName1) + || f.getName().contains(cacheName2) + ) + .map(f -> f.listFiles().length == 1) + .reduce(true, (t, u) -> t && u); + + assertTrue(cleanedEmpty); + + stopGrid(1); + + ig1 = startGrid(1); + + assertFalse(ig1.context().maintenanceRegistry().isMaintenanceMode()); + } + + /** + * Test verifies persistence clean all command that cleans all cache directories. + * + * @throws Exception + */ + @Test + public void testPersistenceCleanAllCachesCommand() throws Exception { + String cacheName0 = DEFAULT_CACHE_NAME + "0"; + String cacheName1 = DEFAULT_CACHE_NAME + "1"; + + File mntcNodeWorkDir = startGridAndPutNodeToMaintenance( + new CacheConfiguration[]{ + cacheConfiguration(cacheName0), + cacheConfiguration(cacheName1) + }, + s -> s.equals(cacheName0)); + + IgniteEx ig1 = startGrid(1); + + String port = ig1.localNode().attribute(IgniteNodeAttributes.ATTR_REST_TCP_PORT).toString(); + + assertEquals(EXIT_CODE_OK, execute("--persistence", "clean", "all", + "--host", "localhost", "--port", port)); + + boolean allEmpty = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(File::isDirectory) + .filter(f -> f.getName().startsWith("cache-")) + .map(f -> f.listFiles().length == 1) + .reduce(true, (t, u) -> t && u); + + assertTrue(allEmpty); + + stopGrid(1); + + ig1 = startGrid(1); + + assertFalse(ig1.context().maintenanceRegistry().isMaintenanceMode()); + } + + /** + * Test verifies that persistence backup command to backup all caches backs up all cache directories. + * + * @throws Exception If failed. + */ + @Test + public void testPersistenceBackupAllCachesCommand() throws Exception { + String cacheName0 = DEFAULT_CACHE_NAME + "0"; + String cacheName1 = DEFAULT_CACHE_NAME + "1"; + + File mntcNodeWorkDir = startGridAndPutNodeToMaintenance( + new CacheConfiguration[]{ + cacheConfiguration(cacheName0), + cacheConfiguration(cacheName1) + }, + s -> s.equals(cacheName0)); + + IgniteEx ig1 = startGrid(1); + + String port = ig1.localNode().attribute(IgniteNodeAttributes.ATTR_REST_TCP_PORT).toString(); + + assertEquals(EXIT_CODE_OK, execute("--persistence", "backup", "all", + "--host", "localhost", "--port", port)); + + Set<String> backedUpCacheDirs = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(File::isDirectory) + .filter(f -> f.getName().startsWith("backup_")) + .map(f -> f.getName().substring("backup_".length())) + .collect(Collectors.toCollection(TreeSet::new)); + + Set<String> allCacheDirs = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(File::isDirectory) + .filter(f -> f.getName().startsWith("cache-")) + .map(File::getName) + .collect(Collectors.toCollection(TreeSet::new)); + + assertEqualsCollections(backedUpCacheDirs, allCacheDirs); + + checkCacheAndBackupDirsContent(mntcNodeWorkDir); + } + + /** + * Test verifies that persistence backup command copies all corrupted caches content to backup directory + * but does not touch other directories. + * + * @throws Exception If failed. + */ + @Test + public void testPersistenceBackupCorruptedCachesCommand() throws Exception { + String cacheName0 = DEFAULT_CACHE_NAME + "0"; + String cacheName1 = DEFAULT_CACHE_NAME + "1"; + + File mntcNodeWorkDir = startGridAndPutNodeToMaintenance( + new CacheConfiguration[]{ + cacheConfiguration(cacheName0), + cacheConfiguration(cacheName1) + }, + s -> s.equals(cacheName0)); + + IgniteEx ig1 = startGrid(1); + + String port = ig1.localNode().attribute(IgniteNodeAttributes.ATTR_REST_TCP_PORT).toString(); + + assertEquals(EXIT_CODE_OK, execute("--persistence", "backup", "corrupted", + "--host", "localhost", "--port", port)); + + long backedUpCachesCnt = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(File::isDirectory) + .filter(f -> f.getName().startsWith("backup_")) + .filter(f -> f.getName().contains(cacheName0)) + .count(); + + assertEquals(1, backedUpCachesCnt); + + checkCacheAndBackupDirsContent(mntcNodeWorkDir); + } + + /** + * Test verifies that persistence backup command with specified caches copied only content of that caches and + * doesn't touch other directories. + * + * @throws Exception If failed. + */ + @Test + public void testPersistenceBackupSpecifiedCachesCommand() throws Exception { + String cacheName0 = DEFAULT_CACHE_NAME + "0"; + String cacheName1 = DEFAULT_CACHE_NAME + "1"; + String cacheName2 = DEFAULT_CACHE_NAME + "2"; + + String nonExistingCacheName = "nonExistingCache"; + + File mntcNodeWorkDir = startGridAndPutNodeToMaintenance( + new CacheConfiguration[]{ + cacheConfiguration(cacheName0), + cacheConfiguration(cacheName1), + cacheConfiguration(cacheName2) + }, + s -> s.equals(cacheName0) || s.equals(cacheName2)); + + IgniteEx ig1 = startGrid(1); + + String port = ig1.localNode().attribute(IgniteNodeAttributes.ATTR_REST_TCP_PORT).toString(); + + assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--persistence", "backup", "caches", + nonExistingCacheName, + "--host", "localhost", "--port", port)); + + assertEquals(EXIT_CODE_OK, execute("--persistence", "backup", "caches", + cacheName0 + "," + cacheName2, + "--host", "localhost", "--port", port)); + + long backedUpCachesCnt = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(File::isDirectory) + .filter(f -> f.getName().startsWith("backup_")) + .count(); + + assertEquals(2, backedUpCachesCnt); + + checkCacheAndBackupDirsContent(mntcNodeWorkDir); + } + + /** */ + private void checkCacheAndBackupDirsContent(File mntcNodeWorkDir) { + List<File> backupDirs = Arrays.stream(mntcNodeWorkDir.listFiles()) + .filter(File::isDirectory) + .filter(f -> f.getName().startsWith("backup_")) + .collect(Collectors.toList()); + + Path mntcNodeWorkDirPath = mntcNodeWorkDir.toPath(); + + for (File bDir : backupDirs) { + File origCacheDir = mntcNodeWorkDirPath.resolve(bDir.getName().substring("backup_".length())).toFile(); + + assertTrue(origCacheDir.isDirectory()); + + assertEquals(origCacheDir.listFiles().length, bDir.listFiles().length); + } + } + /** * Test enabling/disabling read-only mode works via control.sh * diff --git a/modules/core/src/main/java/org/apache/ignite/internal/maintenance/MaintenanceProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/maintenance/MaintenanceProcessor.java index 8f85ceb..6bc3e8e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/maintenance/MaintenanceProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/maintenance/MaintenanceProcessor.java @@ -162,12 +162,21 @@ public class MaintenanceProcessor extends GridProcessorAdapter implements Mainte ); } - if (!workflowCallbacks.isEmpty()) + if (!workflowCallbacks.isEmpty()) { + if (log.isInfoEnabled()) { + String mntcTasksNames = String.join(", ", workflowCallbacks.keySet()); + + log.info("Node requires maintenance, non-empty set of maintenance tasks is found: [" + + mntcTasksNames + ']'); + } + proceedWithMaintenance(); - else { - if (log.isInfoEnabled()) + } + else if (isMaintenanceMode()) { + if (log.isInfoEnabled()) { log.info("All maintenance tasks are fixed, no need to enter maintenance mode. " + "Restart the node to get it back to normal operations."); + } } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CorruptedPdsMaintenanceCallback.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CheckCorruptedCacheStoresCleanAction.java similarity index 56% copy from modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CorruptedPdsMaintenanceCallback.java copy to modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CheckCorruptedCacheStoresCleanAction.java index 52a8f6f..2073b9b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CorruptedPdsMaintenanceCallback.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CheckCorruptedCacheStoresCleanAction.java @@ -18,62 +18,53 @@ package org.apache.ignite.internal.processors.cache.persistence; import java.io.File; -import java.util.Arrays; -import java.util.List; import org.apache.ignite.maintenance.MaintenanceAction; -import org.apache.ignite.maintenance.MaintenanceWorkflowCallback; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.CACHE_DATA_FILENAME; -/** - * - */ -public class CorruptedPdsMaintenanceCallback implements MaintenanceWorkflowCallback { +/** */ +public class CheckCorruptedCacheStoresCleanAction implements MaintenanceAction<Boolean> { /** */ - private final File workDir; + public static final String ACTION_NAME = "check_cache_files_cleaned"; /** */ - private final List<String> cacheStoreDirs; + private final File rootStoreDir; - /** - * @param workDir - * @param cacheStoreDirs - */ - public CorruptedPdsMaintenanceCallback(@NotNull File workDir, - @NotNull List<String> cacheStoreDirs) - { - this.workDir = workDir; + /** */ + private final String[] cacheStoreDirs; + + /** */ + public CheckCorruptedCacheStoresCleanAction(File rootStoreDir, String[] cacheStoreDirs) { + this.rootStoreDir = rootStoreDir; this.cacheStoreDirs = cacheStoreDirs; } /** {@inheritDoc} */ - @Override public boolean shouldProceedWithMaintenance() { + @Override public Boolean execute() { for (String cacheStoreDirName : cacheStoreDirs) { - File cacheStoreDir = new File(workDir, cacheStoreDirName); + File cacheStoreDir = new File(rootStoreDir, cacheStoreDirName); - if (cacheStoreDir.exists() - && cacheStoreDir.isDirectory() - && cacheStoreDir.listFiles().length > 0 - ) { + if (cacheStoreDir.exists() && cacheStoreDir.isDirectory()) { for (File f : cacheStoreDir.listFiles()) { if (!f.getName().equals(CACHE_DATA_FILENAME)) - return true; + return Boolean.FALSE; } } } - return false; + return Boolean.TRUE; } /** {@inheritDoc} */ - @Override public List<MaintenanceAction> allActions() { - return Arrays.asList(new CleanCacheStoresMaintenanceAction(workDir, cacheStoreDirs.toArray(new String[0]))); + @Override public @NotNull String name() { + return ACTION_NAME; } /** {@inheritDoc} */ - @Override public MaintenanceAction automaticAction() { - return null; + @Override public @Nullable String description() { + return "Checks if all corrupted data files are cleaned from cache store directories"; } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CorruptedPdsMaintenanceCallback.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CorruptedPdsMaintenanceCallback.java index 52a8f6f..0173bca 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CorruptedPdsMaintenanceCallback.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CorruptedPdsMaintenanceCallback.java @@ -69,7 +69,9 @@ public class CorruptedPdsMaintenanceCallback implements MaintenanceWorkflowCallb /** {@inheritDoc} */ @Override public List<MaintenanceAction> allActions() { - return Arrays.asList(new CleanCacheStoresMaintenanceAction(workDir, cacheStoreDirs.toArray(new String[0]))); + return Arrays.asList( + new CleanCacheStoresMaintenanceAction(workDir, cacheStoreDirs.toArray(new String[0])), + new CheckCorruptedCacheStoresCleanAction(workDir, cacheStoreDirs.toArray(new String[0]))); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceCleanAndBackupSettings.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceCleanAndBackupSettings.java new file mode 100644 index 0000000..a5bf327 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceCleanAndBackupSettings.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.visor.persistence; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.List; + +import org.apache.ignite.internal.dto.IgniteDataTransferObject; +import org.apache.ignite.internal.util.typedef.internal.U; + +/** */ +public class PersistenceCleanAndBackupSettings extends IgniteDataTransferObject { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private PersistenceCleanAndBackupType cleanAndBackupType; + + /** */ + private List<String> cacheNames; + + /** */ + public PersistenceCleanAndBackupSettings() { + // No-op. + } + + /** */ + public PersistenceCleanAndBackupSettings(PersistenceCleanAndBackupType cleanAndBackupType, List<String> cacheNames) { + this.cleanAndBackupType = cleanAndBackupType; + this.cacheNames = cacheNames; + } + + /** {@inheritDoc} */ + @Override protected void writeExternalData(ObjectOutput out) throws IOException { + U.writeEnum(out, cleanAndBackupType); + U.writeCollection(out, cacheNames); + } + + /** {@inheritDoc} */ + @Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException { + cleanAndBackupType = PersistenceCleanAndBackupType.fromOrdinal(in.readByte()); + cacheNames = U.readList(in); + } + + /** */ + public PersistenceCleanAndBackupType cleanAndBackupType() { + return cleanAndBackupType; + } + + /** */ + public List<String> cacheNames() { + return cacheNames; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceCleanAndBackupType.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceCleanAndBackupType.java new file mode 100644 index 0000000..2198851 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceCleanAndBackupType.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.visor.persistence; + +import org.jetbrains.annotations.Nullable; + +/** */ +public enum PersistenceCleanAndBackupType { + /** */ + ALL, + /** */ + CORRUPTED, + /** */ + CACHES; + + /** */ + private static final PersistenceCleanAndBackupType[] VALS = values(); + + /** + * @param ordinal Index of enum value. + * @return Value of {@link PersistenceCleanAndBackupType} enum. + */ + @Nullable public static PersistenceCleanAndBackupType fromOrdinal(int ordinal) { + return ordinal >= 0 && ordinal < VALS.length ? VALS[ordinal] : null; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceOperation.java new file mode 100644 index 0000000..2481af8 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceOperation.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.visor.persistence; + +import org.jetbrains.annotations.Nullable; + +/** Persistence cleaning operations. */ +public enum PersistenceOperation { + /** */ + INFO, + + /** */ + CLEAN, + + /** */ + BACKUP; + + /** */ + private static final PersistenceOperation[] VALS = values(); + + /** + * @param ordinal Index of enum value. + * @return Value of {@link PersistenceOperation} enum. + */ + @Nullable public static PersistenceOperation fromOrdinal(int ordinal) { + return ordinal >= 0 && ordinal < VALS.length ? VALS[ordinal] : null; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTask.java new file mode 100644 index 0000000..1ac23f4 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTask.java @@ -0,0 +1,401 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.visor.persistence; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteException; +import org.apache.ignite.configuration.DataStorageConfiguration; +import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager; +import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor; +import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor; +import org.apache.ignite.internal.processors.cache.GridCacheProcessor; +import org.apache.ignite.internal.processors.cache.persistence.CheckCorruptedCacheStoresCleanAction; +import org.apache.ignite.internal.processors.cache.persistence.CleanCacheStoresMaintenanceAction; +import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager; +import org.apache.ignite.internal.processors.task.GridInternal; +import org.apache.ignite.internal.processors.task.GridVisorManagementTask; +import org.apache.ignite.internal.util.typedef.internal.CU; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.internal.visor.VisorJob; +import org.apache.ignite.internal.visor.VisorOneNodeTask; +import org.apache.ignite.lang.IgniteBiTuple; +import org.apache.ignite.maintenance.MaintenanceAction; +import org.apache.ignite.maintenance.MaintenanceRegistry; +import org.apache.ignite.maintenance.MaintenanceTask; +import org.jetbrains.annotations.Nullable; + +import static org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.CORRUPTED_DATA_FILES_MNTC_TASK_NAME; +import static org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.cacheDirName; + +/** */ +@GridInternal +@GridVisorManagementTask +public class PersistenceTask extends VisorOneNodeTask<PersistenceTaskArg, PersistenceTaskResult> { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private static final String BACKUP_FOLDER_PREFIX = "backup_"; + + @Override protected VisorJob<PersistenceTaskArg, PersistenceTaskResult> job(PersistenceTaskArg arg) { + return new PersistenceJob(arg, debug); + } + + /** */ + private static class PersistenceJob extends VisorJob<PersistenceTaskArg, PersistenceTaskResult> { + /** */ + private static final long serialVersionUID = 0L; + + /** + * Create job with specified argument. + * + * @param arg Job argument. + * @param debug Flag indicating whether debug information should be printed into node log. + */ + protected PersistenceJob(@Nullable PersistenceTaskArg arg, boolean debug) { + super(arg, debug); + } + + /** {@inheritDoc} */ + @Override protected PersistenceTaskResult run(@Nullable PersistenceTaskArg arg) throws IgniteException { + if (!ignite.context().maintenanceRegistry().isMaintenanceMode()) + return new PersistenceTaskResult(false); + + switch (arg.operation()) { + case CLEAN: + return clean(arg); + + case BACKUP: + return backup(arg); + + default: + return info(); + } + } + + /** */ + private PersistenceTaskResult backup(PersistenceTaskArg arg) { + PersistenceCleanAndBackupSettings backupSettings = arg.cleanAndBackupSettings(); + + MaintenanceRegistry mntcReg = ignite.context().maintenanceRegistry(); + MaintenanceTask task = mntcReg.activeMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME); + + File workDir = ((FilePageStoreManager) ignite.context().cache().context().pageStore()).workDir(); + + switch (backupSettings.cleanAndBackupType()) { + case ALL: + return backupAll(workDir); + + case CORRUPTED: + return backupCaches(workDir, corruptedCacheDirectories(task)); + + default: + return backupCaches(workDir, cacheDirectoriesFromCacheNames(backupSettings.cacheNames())); + } + } + + /** */ + private PersistenceTaskResult backupAll(File workDir) { + GridCacheProcessor cacheProc = ignite.context().cache(); + + List<String> allCacheDirs = cacheProc.cacheDescriptors() + .values() + .stream() + .map(desc -> cacheDirName(desc.cacheConfiguration())) + .distinct() + .collect(Collectors.toList()); + + return backupCaches(workDir, allCacheDirs); + } + + /** */ + private PersistenceTaskResult backupCaches(File workDir, List<String> cacheDirs) { + PersistenceTaskResult res = new PersistenceTaskResult(true); + + List<String> backupCompletedCaches = new ArrayList<>(); + List<String> backupFailedCaches = new ArrayList<>(); + + for (String dir : cacheDirs) { + String backupDirName = BACKUP_FOLDER_PREFIX + dir; + + File backupDir = new File(workDir, backupDirName); + + if (!backupDir.exists()) { + try { + U.ensureDirectory(backupDir, backupDirName, null); + + copyCacheFiles(workDir.toPath().resolve(dir).toFile(), backupDir); + + backupCompletedCaches.add(backupDirName); + } catch (IgniteCheckedException | IOException e) { + backupFailedCaches.add(dir); + } + } + } + + res.handledCaches(backupCompletedCaches); + res.failedCaches(backupFailedCaches); + + return res; + } + + /** */ + private void copyCacheFiles(File sourceDir, File backupDir) throws IOException { + for (File f : sourceDir.listFiles()) + Files.copy(f.toPath(), backupDir.toPath().resolve(f.getName()), StandardCopyOption.REPLACE_EXISTING); + } + + /** */ + private PersistenceTaskResult clean(PersistenceTaskArg arg) { + PersistenceTaskResult res = new PersistenceTaskResult(); + + PersistenceCleanAndBackupSettings cleanSettings = arg.cleanAndBackupSettings(); + + GridCacheProcessor cacheProc = ignite.context().cache(); + MaintenanceRegistry mntcReg = ignite.context().maintenanceRegistry(); + + switch (cleanSettings.cleanAndBackupType()) { + case ALL: + return cleanAll(cacheProc, mntcReg); + + case CORRUPTED: + return cleanCorrupted(mntcReg); + + case CACHES: + return cleanCaches(cacheProc, mntcReg, cleanSettings.cacheNames()); + } + + return res; + } + + /** */ + private PersistenceTaskResult cleanCaches( + GridCacheProcessor cacheProc, + MaintenanceRegistry mntcReg, + List<String> cacheNames + ) { + PersistenceTaskResult res = new PersistenceTaskResult(true); + + List<String> cleanedCaches = new ArrayList<>(); + List<String> failedToCleanCaches = new ArrayList<>(); + + DataStorageConfiguration dsCfg = ignite.context().config().getDataStorageConfiguration(); + IgnitePageStoreManager pageStore = cacheProc.context().pageStore(); + + AtomicReference<String> missedCache = new AtomicReference<>(); + + Boolean allExist = cacheNames + .stream() + .map(name -> { + if (cacheProc.cacheDescriptor(name) != null) + return true; + else { + missedCache.set(name); + + return false; + } + }) + .reduce(true, (t, u) -> t && u); + + if (!allExist) + throw new IllegalArgumentException("Cache with name " + missedCache.get() + + " not found, no caches will be cleaned."); + + for (String name : cacheNames) { + DynamicCacheDescriptor cacheDescr = cacheProc.cacheDescriptor(name); + + if (CU.isPersistentCache(cacheDescr.cacheConfiguration(), dsCfg)) { + try { + pageStore.cleanupPersistentSpace(cacheDescr.cacheConfiguration()); + + cleanedCaches.add(cacheDirName(cacheDescr.cacheConfiguration())); + } + catch (IgniteCheckedException e) { + failedToCleanCaches.add(name); + } + } + } + + res.handledCaches(cleanedCaches); + + if (!failedToCleanCaches.isEmpty()) + res.failedCaches(failedToCleanCaches); + + List<MaintenanceAction> actions = mntcReg.actionsForMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME); + + Optional<MaintenanceAction> checkActionOpt = actions.stream().filter(a -> a.name().equals(CheckCorruptedCacheStoresCleanAction.ACTION_NAME)) + .findFirst(); + + if (checkActionOpt.isPresent()) { + MaintenanceAction<Boolean> action = checkActionOpt.get(); + + Boolean mntcTaskCompleted = action.execute(); + + res.maintenanceTaskCompleted(mntcTaskCompleted); + + if (mntcTaskCompleted) + mntcReg.unregisterMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME); + } + + return res; + } + + /** */ + private PersistenceTaskResult cleanAll(GridCacheProcessor cacheProc, MaintenanceRegistry mntcReg) { + PersistenceTaskResult res = new PersistenceTaskResult(true); + + List<String> allCacheDirs = cacheProc.cacheDescriptors() + .values() + .stream() + .map(desc -> cacheDirName(desc.cacheConfiguration())) + .collect(Collectors.toList()); + + try { + cacheProc.cleanupCachesDirectories(); + } catch (IgniteCheckedException e) { + throw U.convertException(e); + } + + mntcReg.unregisterMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME); + + res.maintenanceTaskCompleted(true); + res.handledCaches(allCacheDirs); + + return res; + } + + /** */ + private PersistenceTaskResult cleanCorrupted(MaintenanceRegistry mntcReg) { + PersistenceTaskResult res = new PersistenceTaskResult(true); + + List<MaintenanceAction> actions = mntcReg + .actionsForMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME); + + Optional<MaintenanceAction> cleanCorruptedActionOpt = actions + .stream() + .filter(a -> a.name().equals(CleanCacheStoresMaintenanceAction.ACTION_NAME)) + .findFirst(); + + if (cleanCorruptedActionOpt.isPresent()) { + cleanCorruptedActionOpt.get().execute(); + + MaintenanceTask corruptedTask = mntcReg.activeMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME); + + mntcReg.unregisterMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME); + + res.handledCaches( + corruptedCacheDirectories(corruptedTask) + ); + + res.maintenanceTaskCompleted(true); + } + + return res; + } + + /** */ + private PersistenceTaskResult info() { + PersistenceTaskResult res = new PersistenceTaskResult(true); + + GridCacheProcessor cacheProc = ignite.context().cache(); + DataStorageConfiguration dsCfg = ignite.context().config().getDataStorageConfiguration(); + + List<String> corruptedCacheNames = corruptedCacheDirectories(ignite.context().maintenanceRegistry() + .activeMaintenanceTask(CORRUPTED_DATA_FILES_MNTC_TASK_NAME)); + + Map<String, IgniteBiTuple<Boolean, Boolean>> cachesInfo = new HashMap<>(); + + for (DynamicCacheDescriptor desc : cacheProc.cacheDescriptors().values()) { + if (!CU.isPersistentCache(desc.cacheConfiguration(), dsCfg)) + continue; + + CacheGroupDescriptor grpDesc = desc.groupDescriptor(); + + if (grpDesc != null) { + boolean globalWalEnabled = grpDesc.walEnabled(); + boolean localWalEnabled = true; + + if (globalWalEnabled && corruptedCacheNames.contains(desc.cacheName())) + localWalEnabled = false; + + cachesInfo.put(desc.cacheName(), new IgniteBiTuple<>(globalWalEnabled, localWalEnabled)); + } + } + + res.cachesInfo(cachesInfo); + + return res; + } + + /** */ + private List<String> corruptedCacheDirectories(MaintenanceTask task) { + String params = task.parameters(); + + String[] namesArr = params.split(File.separator); + + return Arrays.asList(namesArr); + } + + /** */ + private List<String> cacheDirectoriesFromCacheNames(List<String> cacheNames) { + GridCacheProcessor cacheProc = ignite.context().cache(); + + DataStorageConfiguration dsCfg = ignite.configuration().getDataStorageConfiguration(); + + AtomicReference<String> missedCache = new AtomicReference<>(); + + Boolean allExist = cacheNames.stream() + .map(s -> { + if (cacheProc.cacheDescriptor(s) != null) + return true; + else { + missedCache.set(s); + + return false; + } + }) + .reduce(true, (u, v) -> u && v); + + if (!allExist) + throw new IllegalArgumentException("Cache with name " + missedCache.get() + + " not found, no caches will be backed up."); + + return cacheNames.stream() + .filter(s -> cacheProc.cacheDescriptor(s) != null) + .filter(s -> + CU.isPersistentCache(cacheProc.cacheDescriptor(s).cacheConfiguration(), dsCfg)) + .map(s -> cacheProc.cacheDescriptor(s).cacheConfiguration()) + .map(FilePageStoreManager::cacheDirName) + .distinct() + .collect(Collectors.toList()); + } + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTaskArg.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTaskArg.java new file mode 100644 index 0000000..c48f936 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTaskArg.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.visor.persistence; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import org.apache.ignite.internal.dto.IgniteDataTransferObject; +import org.apache.ignite.internal.util.typedef.internal.U; + +/** + * + */ +public class PersistenceTaskArg extends IgniteDataTransferObject { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private PersistenceOperation op; + + /** */ + private PersistenceCleanAndBackupSettings cleanAndBackupSettings; + + /** + * Default constructor. + */ + public PersistenceTaskArg() { + // No-op. + } + + /** + * @param op {@link PersistenceOperation} requested for execution. + * @param cleanAndBackupSettings {@link PersistenceCleanAndBackupSettings} specific settings for clean and backup + * commands. + */ + public PersistenceTaskArg(PersistenceOperation op, PersistenceCleanAndBackupSettings cleanAndBackupSettings) { + this.op = op; + this.cleanAndBackupSettings = cleanAndBackupSettings; + } + + /** + * @return {@link PersistenceOperation} operation requested for execution. + */ + public PersistenceOperation operation() { + return op; + } + + /** + * @return {@link PersistenceCleanAndBackupSettings} specific settings for clean and backup commands. + */ + public PersistenceCleanAndBackupSettings cleanAndBackupSettings() { + return cleanAndBackupSettings; + } + + /** {@inheritDoc} */ + @Override protected void writeExternalData(ObjectOutput out) throws IOException { + U.writeEnum(out, op); + out.writeObject(cleanAndBackupSettings); + } + + /** {@inheritDoc} */ + @Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException { + op = PersistenceOperation.fromOrdinal(in.readByte()); + cleanAndBackupSettings = (PersistenceCleanAndBackupSettings) in.readObject(); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTaskResult.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTaskResult.java new file mode 100644 index 0000000..5a0a0fe --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/persistence/PersistenceTaskResult.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.visor.persistence; + +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.List; +import java.util.Map; + +import org.apache.ignite.internal.dto.IgniteDataTransferObject; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.lang.IgniteBiTuple; + +public class PersistenceTaskResult extends IgniteDataTransferObject { + /** */ + private static final long serialVersionUID = 0L; + + /** */ + private boolean inMaintenanceMode; + + /** */ + private boolean maintenanceTaskCompleted; + + /** */ + private List<String> handledCaches; + + /** */ + private List<String> failedToHandleCaches; + + /** */ + private Map<String, IgniteBiTuple<Boolean, Boolean>> cachesInfo; + + /** */ + public PersistenceTaskResult() { + // No-op. + } + + /** + * + */ + public PersistenceTaskResult(boolean inMaintenanceMode) { + this.inMaintenanceMode = inMaintenanceMode; + } + + /** {@inheritDoc} */ + @Override protected void writeExternalData(ObjectOutput out) throws IOException { + out.writeBoolean(inMaintenanceMode); + out.writeBoolean(maintenanceTaskCompleted); + U.writeCollection(out, handledCaches); + U.writeCollection(out, failedToHandleCaches); + U.writeMap(out, cachesInfo); + } + + /** {@inheritDoc} */ + @Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException { + inMaintenanceMode = in.readBoolean(); + maintenanceTaskCompleted = in.readBoolean(); + handledCaches = U.readList(in); + failedToHandleCaches = U.readList(in); + cachesInfo = U.readMap(in); + } + + /** */ + public boolean inMaintenanceMode() { + return inMaintenanceMode; + } + + /** */ + public boolean maintenanceTaskCompleted() { + return maintenanceTaskCompleted; + } + + /** */ + public void maintenanceTaskCompleted(boolean maintenanceTaskCompleted) { + this.maintenanceTaskCompleted = maintenanceTaskCompleted; + } + + /** */ + public List<String> handledCaches() { + return handledCaches; + } + + /** */ + public void handledCaches(List<String> handledCaches) { + this.handledCaches = handledCaches; + } + + /** */ + public List<String> failedCaches() { + return failedToHandleCaches; + } + + /** */ + public void failedCaches(List<String> failedToHandleCaches) { + this.failedToHandleCaches = failedToHandleCaches; + } + + /** */ + public Map<String, IgniteBiTuple<Boolean, Boolean>> cachesInfo() { + return cachesInfo; + } + + /** */ + public void cachesInfo(Map<String, IgniteBiTuple<Boolean, Boolean>> cachesInfo) { + this.cachesInfo = cachesInfo; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/maintenance/MaintenanceTask.java b/modules/core/src/main/java/org/apache/ignite/maintenance/MaintenanceTask.java index 49795f1..c600952 100644 --- a/modules/core/src/main/java/org/apache/ignite/maintenance/MaintenanceTask.java +++ b/modules/core/src/main/java/org/apache/ignite/maintenance/MaintenanceTask.java @@ -24,8 +24,8 @@ import org.jetbrains.annotations.Nullable; /** * Represents request to handle maintenance situation. * - * It can be created automatically or by user request by any component needed maintenance and should be registered - * in Maintenance Registry with the method {@link MaintenanceRegistry#registerMaintenanceTask(MaintenanceTask)}. + * Maintenance request can be created programmatically + * with {@link MaintenanceRegistry#registerMaintenanceTask(MaintenanceTask)} public API call. * * Lifecycle of Maintenance Task is managed by {@link MaintenanceRegistry}. * diff --git a/modules/core/src/main/resources/META-INF/classnames.properties b/modules/core/src/main/resources/META-INF/classnames.properties index deac9f8..92d01dd 100644 --- a/modules/core/src/main/resources/META-INF/classnames.properties +++ b/modules/core/src/main/resources/META-INF/classnames.properties @@ -350,20 +350,7 @@ org.apache.ignite.internal.commandline.cache.argument.IdleVerifyCommandArg org.apache.ignite.internal.commandline.cache.argument.ListCommandArg org.apache.ignite.internal.commandline.cache.argument.PartitionReconciliationCommandArg org.apache.ignite.internal.commandline.cache.argument.ValidateIndexesCommandArg -org.apache.ignite.internal.commandline.cache.check_indexes_inline_size.CheckIndexInlineSizesResult -org.apache.ignite.internal.commandline.cache.check_indexes_inline_size.CheckIndexInlineSizesTask org.apache.ignite.internal.commandline.cache.check_indexes_inline_size.CheckIndexInlineSizesTask$CheckIndexInlineSizesJob -org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionGroup -org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionNode -org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionPartition -org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionTask -org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionTask$CacheDistributionJob -org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionTaskArg -org.apache.ignite.internal.commandline.cache.distribution.CacheDistributionTaskResult -org.apache.ignite.internal.commandline.cache.reset_lost_partitions.CacheResetLostPartitionsTask -org.apache.ignite.internal.commandline.cache.reset_lost_partitions.CacheResetLostPartitionsTask$CacheResetLostPartitionsJob -org.apache.ignite.internal.commandline.cache.reset_lost_partitions.CacheResetLostPartitionsTaskArg -org.apache.ignite.internal.commandline.cache.reset_lost_partitions.CacheResetLostPartitionsTaskResult org.apache.ignite.internal.commandline.diagnostic.DiagnosticSubCommand org.apache.ignite.internal.commandline.diagnostic.PageLocksCommand$PageLocksCommandArg org.apache.ignite.internal.commandline.dr.DrSubCommandsList @@ -373,16 +360,8 @@ org.apache.ignite.internal.commandline.dr.subcommands.DrCacheCommand$SenderGroup org.apache.ignite.internal.commandline.management.ManagementCommandList org.apache.ignite.internal.commandline.management.ManagementURLCommandArg org.apache.ignite.internal.commandline.meta.subcommands.MetadataAbstractSubCommand.VoidDto -org.apache.ignite.internal.commandline.meta.tasks.MetadataListResult -org.apache.ignite.internal.commandline.meta.tasks.MetadataInfoTask -org.apache.ignite.internal.commandline.meta.tasks.MetadataInfoTask.MetadataListJob -org.apache.ignite.internal.commandline.meta.tasks.MetadataMarshalled -org.apache.ignite.internal.commandline.meta.tasks.MetadataRemoveTask org.apache.ignite.internal.commandline.meta.tasks.MetadataRemoveTask$MetadataRemoveJob org.apache.ignite.internal.commandline.meta.tasks.MetadataRemoveTask$DropAllThinSessionsJob -org.apache.ignite.internal.commandline.meta.tasks.MetadataTypeArgs -org.apache.ignite.internal.commandline.meta.tasks.MetadataUpdateTask -org.apache.ignite.internal.commandline.meta.tasks.MetadataUpdateTask.MetadataUpdateJob org.apache.ignite.internal.commandline.property.tasks.PropertiesListResult org.apache.ignite.internal.commandline.property.tasks.PropertiesListTask org.apache.ignite.internal.commandline.property.tasks.PropertyOperationResult @@ -2285,6 +2264,7 @@ org.apache.ignite.internal.visor.query.VisorRunningQuery org.apache.ignite.internal.visor.query.VisorScanQueryTask org.apache.ignite.internal.visor.query.VisorScanQueryTask$VisorScanQueryJob org.apache.ignite.internal.visor.query.VisorScanQueryTaskArg +org.apache.ignite.internal.visor.persistence.PersistenceTaskResult org.apache.ignite.internal.visor.service.VisorCancelServiceTask org.apache.ignite.internal.visor.service.VisorCancelServiceTask$VisorCancelServiceJob org.apache.ignite.internal.visor.service.VisorCancelServiceTaskArg diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output index 2806cd6..60ef386 100644 --- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output +++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output @@ -197,6 +197,30 @@ If the file name isn't specified the output file name is: '<typeId>.bin' name - Name of the metric which value should be printed. If name of the metric registry is specified, value of all its metrics will be printed. node_id - ID of the node to get the metric values from. If not set, random node will be chosen. + Print information about potentially corrupted caches on local node: + control.(sh|bat) --persistence + + The same information is printed when info subcommand is passed: + control.(sh|bat) --persistence info + + Clean directories of caches with corrupted data files: + control.(sh|bat) --persistence clean corrupted + + Clean directories of all caches: + control.(sh|bat) --persistence clean all + + Clean directories of only given caches: + control.(sh|bat) --persistence clean caches cache1,cache2,cache3 + + Backup data files of corrupted caches only: + control.(sh|bat) --persistence backup corrupted + + Backup data files of all caches: + control.(sh|bat) --persistence backup all + + Backup data files of only given caches: + control.(sh|bat) --persistence backup caches cache1,cache2,cache3 + By default commands affecting the cluster require interactive confirmation. Use --yes option to disable it. diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output index 2806cd6..60ef386 100644 --- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output +++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output @@ -197,6 +197,30 @@ If the file name isn't specified the output file name is: '<typeId>.bin' name - Name of the metric which value should be printed. If name of the metric registry is specified, value of all its metrics will be printed. node_id - ID of the node to get the metric values from. If not set, random node will be chosen. + Print information about potentially corrupted caches on local node: + control.(sh|bat) --persistence + + The same information is printed when info subcommand is passed: + control.(sh|bat) --persistence info + + Clean directories of caches with corrupted data files: + control.(sh|bat) --persistence clean corrupted + + Clean directories of all caches: + control.(sh|bat) --persistence clean all + + Clean directories of only given caches: + control.(sh|bat) --persistence clean caches cache1,cache2,cache3 + + Backup data files of corrupted caches only: + control.(sh|bat) --persistence backup corrupted + + Backup data files of all caches: + control.(sh|bat) --persistence backup all + + Backup data files of only given caches: + control.(sh|bat) --persistence backup caches cache1,cache2,cache3 + By default commands affecting the cluster require interactive confirmation. Use --yes option to disable it.