diff --git a/docs/_docs/tools/control-script.adoc b/docs/_docs/tools/control-script.adoc index 843878b6a5f7d..808cbdff438f8 100644 --- a/docs/_docs/tools/control-script.adoc +++ b/docs/_docs/tools/control-script.adoc @@ -1278,7 +1278,7 @@ The `schedule_indexes_rebuild` commands Apache Ignite to rebuild indexes for spe [source, shell] ---- - control.sh|bat --cache schedule_indexes_rebuild --node-id nodeId --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] --group-names groupName1,groupName2,...groupNameN + control.sh|bat --cache schedule_indexes_rebuild --node-ids nodeId1,...nodeIdN|--all-nodes --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] --group-names groupName1,groupName2,...groupNameN ---- Parameters: diff --git a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java index d150f9e75ab8b..bbb68b62e93ee 100644 --- a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java +++ b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java @@ -980,6 +980,52 @@ public void testIndexForceRebuildWrongArgs() { IllegalArgumentException.class, "Unexpected value: --some-other-arg" ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList( + CACHE, "indexes_force_rebuild", + "--node-id", nodeId, + "--node-ids", nodeId + ',' + nodeId, + "--cache-names", "someNames" + )), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id] allowed" + ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList( + CACHE, "indexes_force_rebuild", + "--node-id", nodeId, + "--all-nodes" + )), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id] allowed" + ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList( + CACHE, "indexes_force_rebuild", + "--node-ids", nodeId + ',' + nodeId, + "--all-nodes" + )), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id] allowed" + ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList( + CACHE, "indexes_force_rebuild", + "--node-id", nodeId, + "--node-ids", nodeId + ',' + nodeId, + "--all-nodes" + )), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id] allowed" + ); } /** */ @@ -1037,6 +1083,37 @@ public void testScheduleIndexRebuildWrongArgs() { IllegalArgumentException.class, "Square brackets must contain comma-separated indexes or not be used at all." ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList(CACHE, "schedule_indexes_rebuild", "--node-id", nodeId, "--all-nodes", "--group-names", "a")), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id]" + ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList(CACHE, "schedule_indexes_rebuild", "--node-id", nodeId, "--node-ids", nodeId + ',' + nodeId, + "--group-names", "a")), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id]" + ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList(CACHE, "schedule_indexes_rebuild", "--all-nodes", "--node-ids", nodeId + ',' + nodeId, + "--group-names", "a")), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id]" + ); + + GridTestUtils.assertThrows( + null, + () -> parseArgs(asList(CACHE, "schedule_indexes_rebuild", "--node-id", nodeId, "--all-nodes", + "--node-ids", nodeId + ',' + nodeId, "--group-names", "a")), + IllegalArgumentException.class, + "Only one of [--node-ids, --all-nodes, --node-id]" + ); } /** */ diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java index 806794d73f114..ee269d7c4e7be 100644 --- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java +++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerIndexForceRebuildTest.java @@ -285,37 +285,6 @@ public void testComplexIndexRebuild() throws IgniteInterruptedCheckedException { removeLogListener(grid(LAST_NODE_NUM), lsnr); } - /** - * Checks two arguments in the group are not allowed. - */ - @Test - public void testInvalidArgumentGroups() { - injectTestSystemOut(); - - assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS, "--cache", "indexes_force_rebuild", - "--node-ids", grid(LAST_NODE_NUM).localNode().id().toString() + ',' + grid(0).localNode().id().toString(), - "--node-id", grid(LAST_NODE_NUM).localNode().id().toString(), - "--cache-names", CACHE_NAME_NO_GRP), - "Only one of [--node-ids, --all-nodes, --node-id] allowed"); - - assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS, "--cache", "indexes_force_rebuild", - "--node-ids", grid(LAST_NODE_NUM).localNode().id().toString() + ',' + grid(0).localNode().id().toString(), - "--all-nodes", - "--cache-names", CACHE_NAME_NO_GRP), - "Only one of [--node-ids, --all-nodes, --node-id] allowed"); - - assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS, "--cache", "indexes_force_rebuild", - "--all-nodes", - "--node-id", grid(LAST_NODE_NUM).localNode().id().toString(), - "--cache-names", CACHE_NAME_NO_GRP), - "Only one of [--node-ids, --all-nodes, --node-id] allowed"); - - assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS, "--cache", "indexes_force_rebuild", - "--node-id", grid(LAST_NODE_NUM).localNode().id().toString(), - "--cache-names", CACHE_NAME_NO_GRP, "--group-names", CACHE_NAME_NO_GRP), - "Only one of [--group-names, --cache-names] allowed"); - } - /** * Checks --node-id and --cache-names options, * correctness of utility output and the fact that indexes were actually rebuilt. @@ -730,7 +699,7 @@ private static void validateOutputCacheNamesNotFound(String outputStr, String... assertContains( log, outputStr, - "WARNING: These caches were not found:" + U.nl() + makeStringListWithIndent(cacheNames) + CacheIndexesForceRebuildCommand.PREF_CACHES_NOT_FOUND + U.nl() + makeStringListWithIndent(cacheNames) ); } @@ -744,7 +713,7 @@ private void validateOutputCacheGroupsNotFound(String outputStr, String... cache assertContains( log, outputStr, - "WARNING: These cache groups were not found:" + U.nl() + makeStringListWithIndent(cacheGrps) + CacheIndexesForceRebuildCommand.PREF_GROUPS_NOT_FOUND + U.nl() + makeStringListWithIndent(cacheGrps) ); } @@ -815,7 +784,7 @@ private void validateOutputIndicesRebuildWasStarted(String outputStr, Map gridIdxs) throws Exception { + Collection grids = gridIdxs.stream().map(this::grid).collect(Collectors.toList()); - node = startGrid(LAST_NODE_NUM); + if (gridIdxs.isEmpty()) { + injectTestSystemOut(); - assertTrue(node.context().maintenanceRegistry().isMaintenanceMode()); + assertEquals(EXIT_CODE_OK, execute("--cache", "schedule_indexes_rebuild", "--all-nodes", + "--cache-names", CACHE_NAME_NO_GRP)); - assertTrue(waitForIndexesRebuild(grid(LAST_NODE_NUM))); + String notExistingCacheOutputStr = testOut.toString(); - node.close(); + assertTrue(notExistingCacheOutputStr.contains(PREF_SCHEDULED)); + assertTrue(notExistingCacheOutputStr.contains("of cache '" + CACHE_NAME_NO_GRP + "'")); - node = startGrid(LAST_NODE_NUM); + for (Ignite ig : grids) + assertTrue(notExistingCacheOutputStr.contains(ig.cluster().localNode().id().toString())); - assertFalse(node.context().maintenanceRegistry().isMaintenanceMode()); + gridIdxs = IntStream.range(0, GRIDS_NUM).boxed().collect(Collectors.toList()); + } + else { + String nids = grids.size() == 1 + ? grids.iterator().next().localNode().id().toString() + : grids.stream().map(g -> g.localNode().id().toString()).collect(Collectors.joining(",")); - checkIndexes(CACHE_NAME_NO_GRP); + assertEquals(EXIT_CODE_OK, execute("--cache", "schedule_indexes_rebuild", + grids.size() == 1 ? "--node-id" : "--node-ids", nids, "--cache-names", CACHE_NAME_NO_GRP)); + } + + for (Integer gridIdx : gridIdxs) { + IgniteEx node = grid(gridIdx); + + checkIndexesRebuildScheduled(node, singletonMap(CU.cacheId(CACHE_NAME_NO_GRP), indexes(node, CACHE_NAME_NO_GRP))); + + node.close(); + + node = startGrid(gridIdx); + + assertTrue(node.context().maintenanceRegistry().isMaintenanceMode()); + + assertTrue(waitForIndexesRebuild(node)); + + node.close(); + + node = startGrid(gridIdx); + + assertFalse(node.context().maintenanceRegistry().isMaintenanceMode()); + + checkIndexes(CACHE_NAME_NO_GRP); + } } /** @@ -275,14 +378,25 @@ public void testCorruptedIndexRebuildCacheWithGroupOnAllNodes() throws Exception * @param specifyNodeId If {@code true} then execute rebuild only on one node. */ private void testCorruptedIndexRebuild(boolean withCacheGroup, boolean specifyNodeId) throws Exception { - IgniteEx firstNode = grid(0); + testCorruptedIndexRebuild(withCacheGroup, specifyNodeId ? Collections.singletonList(0) : null); + } + + /** + * Checks that corrupted index is successfully rebuilt by the command. + * + * @param withCacheGroup If {@code true} creates a cache with a cache group. + * @param nodeIdxs Nodes indexes of the nodes to launch on. If {@code Null}, no node ids is passed. If empty, + * '--all-nodes' passed. Otherwise, '--node-id' or '--node-ids' is used. + */ + private void testCorruptedIndexRebuild(boolean withCacheGroup, @Nullable List nodeIdxs) throws Exception { + IgniteEx ig = grid(0); String cacheName = "tmpCache"; try { - createAndFillCache(firstNode, cacheName, withCacheGroup ? "tmpGrp" : null); + createAndFillCache(ig, cacheName, withCacheGroup ? "tmpGrp" : null); - breakSqlIndex(firstNode.cachex(cacheName), 1, null); + breakSqlIndex(ig.cachex(cacheName), 1, null); injectTestSystemOut(); @@ -295,18 +409,28 @@ private void testCorruptedIndexRebuild(boolean withCacheGroup, boolean specifyNo List args = new ArrayList<>(); args.add("--cache"); args.add("schedule_indexes_rebuild"); - if (specifyNodeId) { + + if (F.isEmpty(nodeIdxs)) { + if (nodeIdxs != null) + args.add("--all-nodes"); + + nodeIdxs = IntStream.range(0, GRIDS_NUM).boxed().collect(Collectors.toList()); + } + else if (nodeIdxs.size() == 1) { args.add("--node-id"); - args.add(firstNode.localNode().id().toString()); + args.add(grid(nodeIdxs.get(0)).localNode().id().toString()); + } + else { + args.add("--node-ids"); + args.add(nodeIdxs.stream().map(idx -> grid(idx).localNode().id().toString()).collect(Collectors.joining(","))); } + args.add("--cache-names"); args.add(cacheName); assertEquals(EXIT_CODE_OK, execute(args.toArray(new String[0]))); - int nodeCount = specifyNodeId ? 1 : GRIDS_NUM; - - for (int i = 0; i < nodeCount; i++) { + for (int i : nodeIdxs) { IgniteEx grid = grid(i); checkIndexesRebuildScheduled(grid, singletonMap(CU.cacheId(cacheName), indexes(grid, cacheName))); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java index 76e37070e52b6..93a8890d57057 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/api/CommandUtils.java @@ -497,7 +497,7 @@ public static boolean hasDescribedParameters(Command cmd) { } /** */ - public static Collection nodeOrNull(@Nullable UUID nodeId, Collection nodes) { + public static @Nullable Collection nodeOrNull(@Nullable UUID nodeId, Collection nodes) { return nodeId == null ? null : node(nodeId, nodes); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java index 224c957159f90..56abe7c175d15 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommand.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; import org.apache.ignite.internal.client.GridClientNode; @@ -44,7 +45,7 @@ public class CacheIndexesForceRebuildCommand public static final String PREF_CACHES_NOT_FOUND = "WARNING: These caches were not found:"; /** */ - private static final String PREF_GROUPS_NOT_FOUND = "WARNING: These cache groups were not found:"; + public static final String PREF_GROUPS_NOT_FOUND = "WARNING: These cache groups were not found:"; /** */ public static final String PREF_REBUILD_STARTED = "Indexes rebuild was started for these caches:"; @@ -57,6 +58,9 @@ public class CacheIndexesForceRebuildCommand public static final String PREF_REBUILD_NOT_STARTED = "WARNING: Indexes rebuild was not started for " + "any cache on the following nodes. Check the command input:"; + /** */ + public static final String PREF_SCHEDULED = "Indexes rebuild was scheduled for these caches:"; + /** {@inheritDoc} */ @Override public String description() { return "Triggers rebuild of all indexes for specified caches or cache groups"; @@ -92,7 +96,7 @@ public class CacheIndexesForceRebuildCommand } if (F.isEmpty(res)) - throw new IllegalArgumentException("Please, specify oat least one server node"); + throw new IllegalArgumentException("Please, specify at least one server node"); return res; } @@ -115,12 +119,12 @@ public class CacheIndexesForceRebuildCommand Set notStarted = new HashSet<>(); results.forEach((nodeId, res) -> { - storeCacheResults(notFound, res.notFoundCacheNames(), nodeId); + storeEntryToNodesResults(notFound, res.notFoundCacheNames(), nodeId); - storeCacheResults(rebuilding, res.cachesWithRebuildInProgress(), nodeId); + storeEntryToNodesResults(rebuilding, res.cachesWithRebuildInProgress(), nodeId); if (!F.isEmpty(res.cachesWithStartedRebuild())) - storeCacheResults(started, res.cachesWithStartedRebuild(), nodeId); + storeEntryToNodesResults(started, res.cachesWithStartedRebuild(), nodeId); else notStarted.add(nodeId); }); @@ -188,7 +192,7 @@ private static void printSingleResult( } /** */ - private static void storeCacheResults(Map> to, Collection keys, UUID nodeId) { + static void storeEntryToNodesResults(Map> to, Collection keys, UUID nodeId) { if (F.isEmpty(keys)) return; @@ -212,30 +216,35 @@ private static void printInfos(Collection infos } /** */ - private static void printBlock(SB b, String header, Map> data) { + static void printBlock(SB b, String header, Map> data, BiConsumer cacheInfoPrinter) { printHeader(b, header); data.forEach((cacheInfo, nodes) -> { printEntryNewLine(b); - printCacheInfo(b, cacheInfo); + cacheInfoPrinter.accept(b, cacheInfo); b.a(" on nodes ").a(nodeIdsString(nodes)).a('.'); }); } /** */ - private static void printEntryNewLine(SB b) { + static void printBlock(SB b, String header, Map> data) { + printBlock(b, header, data, CacheIndexesForceRebuildCommand::printCacheInfo); + } + + /** */ + static void printEntryNewLine(SB b) { b.a(U.nl()).a(INDENT); } /** */ - private static String nodeIdsString(Collection nodes) { + static String nodeIdsString(Collection nodes) { return nodes.stream().map(uuid -> '\'' + uuid.toString() + '\'').collect(Collectors.joining(", ")); } /** */ - private static void printHeader(SB b, String header) { + static void printHeader(SB b, String header) { b.a(U.nl()).a(U.nl()).a(header); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommandArg.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommandArg.java index 111e9f24d2204..3eb8a0c837fde 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommandArg.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheIndexesForceRebuildCommandArg.java @@ -34,7 +34,8 @@ public class CacheIndexesForceRebuildCommandArg extends IgniteDataTransferObject private static final long serialVersionUID = 0; /** */ - @Argument(description = "Specify node for indexes rebuild (deprecated. Use --node-ids instead)", example = "nodeId") + @Argument(description = "Specify node for indexes rebuild (deprecated. Use --node-ids or --all-nodes instead)", + example = "nodeId") private UUID nodeId; /** */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java index ccf845786f80f..0b9fe5ffc6d0f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommand.java @@ -18,19 +18,46 @@ package org.apache.ignite.internal.management.cache; import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.apache.ignite.internal.client.GridClientNode; +import org.apache.ignite.internal.management.api.CommandUtils; import org.apache.ignite.internal.management.api.ComputeCommand; +import org.apache.ignite.internal.util.GridStringBuilder; +import org.apache.ignite.internal.util.lang.IgnitePair; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.SB; import static org.apache.ignite.internal.management.api.CommandUtils.INDENT; import static org.apache.ignite.internal.management.api.CommandUtils.nodeOrAll; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.PREF_CACHES_NOT_FOUND; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.PREF_GROUPS_NOT_FOUND; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.PREF_SCHEDULED; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.nodeIdsString; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.printBlock; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.printEntryNewLine; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.printHeader; +import static org.apache.ignite.internal.management.cache.CacheIndexesForceRebuildCommand.storeEntryToNodesResults; /** Index rebuild via the maintenance mode. */ public class CacheScheduleIndexesRebuildCommand implements ComputeCommand { + /** */ + public static final String PREF_INDEXES_NOT_FOUND = "WARNING: These indexes were not found:"; + + /** */ + public static final String PREF_REBUILD_NOT_SCHEDULED = "WARNING: Indexes rebuild was not scheduled for any cache. " + + "Check command input."; + + /** */ + public static final String PREF_REBUILD_NOT_SCHEDULED_MULTI = "WARNING: Indexes rebuild was not scheduled for " + + "any cache on the following nodes. Check command input:"; + /** {@inheritDoc} */ @Override public String description() { return "Schedules rebuild of the indexes for specified caches via the Maintenance Mode. " + @@ -49,34 +76,100 @@ public class CacheScheduleIndexesRebuildCommand /** {@inheritDoc} */ @Override public Collection nodes(Collection nodes, CacheScheduleIndexesRebuildCommandArg arg) { - return nodeOrAll(arg.nodeId(), nodes); + if (arg.allNodes() || F.isEmpty(arg.nodeIds()) && arg.nodeId() == null) + return nodes; + + nodeOrAll(arg.nodeId(), nodes); + + return CommandUtils.nodes(arg.nodeId() == null ? arg.nodeIds() : new UUID[] {arg.nodeId()}, nodes); } /** {@inheritDoc} */ @Override public void printResult( CacheScheduleIndexesRebuildCommandArg arg, - ScheduleIndexRebuildTaskRes res0, + ScheduleIndexRebuildTaskRes results, Consumer printer ) { - res0.results().forEach((nodeId, res) -> { - printMissed(printer, "WARNING: These caches were not found:", res.notFoundCacheNames()); - printMissed(printer, "WARNING: These cache groups were not found:", res.notFoundGroupNames()); + if (arg.nodeId() != null) { + printSingleResult(results, printer); + + return; + } + + Map> missedCaches = new HashMap<>(); + Map> missedGroups = new HashMap<>(); + Map> notFoundIndexes = new HashMap<>(); + Map> scheduled = new HashMap<>(); + Set notScheduled = new HashSet<>(); + + results.results().forEach((nodeId, res) -> { + storeEntryToNodesResults(missedCaches, res.notFoundCacheNames(), nodeId); + + storeEntryToNodesResults(missedGroups, res.notFoundGroupNames(), nodeId); + + storeEntryToNodesResults(notFoundIndexes, extractCacheIndexNames(res.notFoundIndexes()), nodeId); + + if (hasAtLeastOneIndex(res.cacheToIndexes())) + storeEntryToNodesResults(scheduled, extractCacheIndexNames(res.cacheToIndexes()), nodeId); + else + notScheduled.add(nodeId); + }); + + SB b = new SB(); + + if (!F.isEmpty(missedCaches)) + printBlock(b, PREF_CACHES_NOT_FOUND, missedCaches, GridStringBuilder::a); + + if (!F.isEmpty(missedGroups)) + printBlock(b, PREF_GROUPS_NOT_FOUND, missedGroups, GridStringBuilder::a); - if (!F.isEmpty(res.notFoundIndexes()) && hasAtLeastOneIndex(res.notFoundIndexes())) { - String warning = "WARNING: These indexes were not found:"; + if (!F.isEmpty(notFoundIndexes)) + printBlock(b, PREF_INDEXES_NOT_FOUND, notFoundIndexes, GridStringBuilder::a); - printer.accept(warning); + if (!F.isEmpty(notScheduled)) { + printHeader(b, PREF_REBUILD_NOT_SCHEDULED_MULTI); + + printEntryNewLine(b); + + b.a(nodeIdsString(notScheduled)); + } + + if (!F.isEmpty(scheduled)) + printBlock(b, PREF_SCHEDULED, scheduled, GridStringBuilder::a); + + printer.accept(b.toString().trim()); + } + + /** */ + private static Collection extractCacheIndexNames(Map> cacheIndexes) { + return F.flatCollections(cacheIndexes.entrySet().stream().map(cIdxs -> cIdxs.getValue().stream() + .map(idx -> indexAndCacheInfo(cIdxs.getKey(), idx)).collect(Collectors.toList())).collect(Collectors.toList())); + } + + /** */ + private static String indexAndCacheInfo(String cache, String index) { + return '\'' + index + "' (of cache '" + cache + "')"; + } + + /** */ + private static void printSingleResult(ScheduleIndexRebuildTaskRes result, Consumer printer) { + result.results().forEach((nodeId, res) -> { + printMissed(printer, PREF_CACHES_NOT_FOUND, res.notFoundCacheNames()); + printMissed(printer, PREF_GROUPS_NOT_FOUND, res.notFoundGroupNames()); + + if (hasAtLeastOneIndex(res.notFoundIndexes())) { + printer.accept(PREF_INDEXES_NOT_FOUND); printCachesAndIndexes(res.notFoundIndexes(), printer); } if (!F.isEmpty(res.cacheToIndexes()) && hasAtLeastOneIndex(res.cacheToIndexes())) { - printer.accept("Indexes rebuild was scheduled for these caches:"); + printer.accept(PREF_SCHEDULED); printCachesAndIndexes(res.cacheToIndexes(), printer); } else - printer.accept("WARNING: Indexes rebuild was not scheduled for any cache. Check command input."); + printer.accept(PREF_REBUILD_NOT_SCHEDULED); printer.accept(""); }); @@ -89,7 +182,7 @@ public class CacheScheduleIndexesRebuildCommand * @param message Message. * @param missed Missed caches or cache groups' names. */ - private void printMissed(Consumer printer, String message, Set missed) { + private static void printMissed(Consumer printer, String message, Set missed) { if (F.isEmpty(missed)) return; @@ -120,8 +213,23 @@ private static void printCachesAndIndexes(Map> cachesToIndex * @return {@code true} if has at least one index in the map, {@code false} otherwise. */ private static boolean hasAtLeastOneIndex(Map> cacheToIndexes) { - return cacheToIndexes.values().stream() + return !F.isEmpty(cacheToIndexes) && cacheToIndexes.values().stream() .anyMatch(indexes -> !indexes.isEmpty()); } + /** */ + static void storeCacheAndIndexResults(Map, Set> to, Map> values, UUID nodeId) { + if (F.isEmpty(values)) + return; + + values.forEach((cache, indexes) -> indexes.forEach(idx -> + to.compute(new IgnitePair<>(cache, idx), (c0, idxs0) -> { + if (idxs0 == null) + idxs0 = new HashSet<>(); + + idxs0.add(nodeId); + + return idxs0; + }))); + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommandArg.java b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommandArg.java index 1c609ec27c001..94894a5bb3dbe 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommandArg.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/cache/CacheScheduleIndexesRebuildCommandArg.java @@ -36,6 +36,7 @@ import org.apache.ignite.internal.util.typedef.internal.U; /** */ +@ArgumentGroup(value = {"nodeIds", "allNodes", "nodeId"}, onlyOneOf = true, optional = true) @ArgumentGroup(value = {"cacheNames", "groupNames"}, optional = false) public class CacheScheduleIndexesRebuildCommandArg extends IgniteDataTransferObject { /** */ @@ -46,11 +47,22 @@ public class CacheScheduleIndexesRebuildCommandArg extends IgniteDataTransferObj /** */ @Argument( - description = "(Optional) Specify node for indexes rebuild. If not specified, schedules rebuild on all nodes", - example = "nodeId", - optional = true) + description = "(Optional) Specify node for indexes rebuild. If not specified, schedules rebuild on all nodes " + + "(deprecated. Use --node-ids or --all-nodes instead)", + example = "nodeId") private UUID nodeId; + /** */ + @Argument( + description = "Comma-separated list of nodes ids to schedule index rebuild on", + example = "nodeId1,...nodeIdN" + ) + private UUID[] nodeIds; + + /** Flag to launch index rebuild on all nodes. */ + @Argument(description = "Rebuild index on all nodes") + private boolean allNodes; + /** */ @Argument(description = "Comma-separated list of cache names with optionally specified indexes. " + "If indexes are not specified then all indexes of the cache will be scheduled for the rebuild operation. " + @@ -73,6 +85,8 @@ public class CacheScheduleIndexesRebuildCommandArg extends IgniteDataTransferObj U.writeString(out, cacheNames); U.writeArray(out, groupNames); U.writeMap(out, cacheToIndexes); + U.writeArray(out, nodeIds); + out.writeBoolean(allNodes); } /** {@inheritDoc} */ @@ -81,6 +95,8 @@ public class CacheScheduleIndexesRebuildCommandArg extends IgniteDataTransferObj cacheNames = U.readString(in); groupNames = U.readArray(in, String.class); cacheToIndexes = U.readMap(in); + nodeIds = U.readArray(in, UUID.class); + allNodes = in.readBoolean(); } /** */ @@ -128,6 +144,26 @@ public void nodeId(UUID nodeId) { this.nodeId = nodeId; } + /** */ + public UUID[] nodeIds() { + return nodeIds; + } + + /** */ + public void allNodes(boolean allNodes) { + this.allNodes = allNodes; + } + + /** */ + public boolean allNodes() { + return allNodes; + } + + /** */ + public void nodeIds(UUID[] nodeIds) { + this.nodeIds = nodeIds; + } + /** */ public String cacheNames() { return cacheNames; diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output index 3b7b9111815f0..2fc5118361841 100644 --- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output +++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_cache_help.output @@ -100,7 +100,7 @@ Arguments: --cache help --yes control.(sh|bat) --cache indexes_force_rebuild --node-id nodeId|--node-ids nodeId1,...nodeIdN|--all-nodes --cache-names cacheName1,...cacheNameN|--group-names groupName1,...groupNameN Parameters: - --node-id nodeId - Specify node for indexes rebuild (deprecated. Use --node-ids instead). + --node-id nodeId - Specify node for indexes rebuild (deprecated. Use --node-ids or --all-nodes instead). --node-ids nodeId1,...nodeIdN - Comma-separated list of nodes ids to run index rebuild on. --all-nodes - Rebuild index on all nodes. --cache-names cacheName1,...cacheNameN - Comma-separated list of cache names for which indexes should be rebuilt. @@ -114,10 +114,12 @@ Arguments: --cache help --yes --all-caches - applies operation to all user caches. Schedules rebuild of the indexes for specified caches via the Maintenance Mode. Schedules rebuild of specified caches and cache-groups: - control.(sh|bat) --cache schedule_indexes_rebuild [--node-id nodeId] --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] --group-names groupName1,groupName2,...groupNameN + control.(sh|bat) --cache schedule_indexes_rebuild [--node-id nodeId|--node-ids nodeId1,...nodeIdN|--all-nodes] --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] --group-names groupName1,groupName2,...groupNameN Parameters: - --node-id nodeId - (Optional) Specify node for indexes rebuild. If not specified, schedules rebuild on all nodes. + --node-id nodeId - (Optional) Specify node for indexes rebuild. If not specified, schedules rebuild on all nodes (deprecated. Use --node-ids or --all-nodes instead). + --node-ids nodeId1,...nodeIdN - Comma-separated list of nodes ids to schedule index rebuild on. + --all-nodes - Rebuild index on all nodes. --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] - Comma-separated list of cache names with optionally specified indexes. If indexes are not specified then all indexes of the cache will be scheduled for the rebuild operation. Can be used simultaneously with cache group names. --group-names groupName1,groupName2,...groupNameN - Comma-separated list of cache group names for which indexes should be scheduled for the rebuild. Can be used simultaneously with cache names. diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output index 3b7b9111815f0..2fc5118361841 100644 --- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output +++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_cache_help.output @@ -100,7 +100,7 @@ Arguments: --cache help --yes control.(sh|bat) --cache indexes_force_rebuild --node-id nodeId|--node-ids nodeId1,...nodeIdN|--all-nodes --cache-names cacheName1,...cacheNameN|--group-names groupName1,...groupNameN Parameters: - --node-id nodeId - Specify node for indexes rebuild (deprecated. Use --node-ids instead). + --node-id nodeId - Specify node for indexes rebuild (deprecated. Use --node-ids or --all-nodes instead). --node-ids nodeId1,...nodeIdN - Comma-separated list of nodes ids to run index rebuild on. --all-nodes - Rebuild index on all nodes. --cache-names cacheName1,...cacheNameN - Comma-separated list of cache names for which indexes should be rebuilt. @@ -114,10 +114,12 @@ Arguments: --cache help --yes --all-caches - applies operation to all user caches. Schedules rebuild of the indexes for specified caches via the Maintenance Mode. Schedules rebuild of specified caches and cache-groups: - control.(sh|bat) --cache schedule_indexes_rebuild [--node-id nodeId] --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] --group-names groupName1,groupName2,...groupNameN + control.(sh|bat) --cache schedule_indexes_rebuild [--node-id nodeId|--node-ids nodeId1,...nodeIdN|--all-nodes] --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] --group-names groupName1,groupName2,...groupNameN Parameters: - --node-id nodeId - (Optional) Specify node for indexes rebuild. If not specified, schedules rebuild on all nodes. + --node-id nodeId - (Optional) Specify node for indexes rebuild. If not specified, schedules rebuild on all nodes (deprecated. Use --node-ids or --all-nodes instead). + --node-ids nodeId1,...nodeIdN - Comma-separated list of nodes ids to schedule index rebuild on. + --all-nodes - Rebuild index on all nodes. --cache-names cacheName[index1,...indexN],cacheName2,cacheName3[index1] - Comma-separated list of cache names with optionally specified indexes. If indexes are not specified then all indexes of the cache will be scheduled for the rebuild operation. Can be used simultaneously with cache group names. --group-names groupName1,groupName2,...groupNameN - Comma-separated list of cache group names for which indexes should be scheduled for the rebuild. Can be used simultaneously with cache names.