You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by na...@apache.org on 2022/06/01 12:59:45 UTC
[ignite] branch master updated: IGNITE-14913 Added cache metrics command for Control Script (#9694)
This is an automated email from the ASF dual-hosted git repository.
namelchev 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 9003d09e29e IGNITE-14913 Added cache metrics command for Control Script (#9694)
9003d09e29e is described below
commit 9003d09e29e2465e0766be73f0837bc56ae64a24
Author: Ilya Shishkov <sh...@gmail.com>
AuthorDate: Wed Jun 1 15:59:34 2022 +0300
IGNITE-14913 Added cache metrics command for Control Script (#9694)
---
docs/_docs/tools/control-script.adoc | 29 +++
.../commandline/cache/CacheCommandList.java | 7 +-
.../internal/commandline/cache/CacheMetrics.java | 133 ++++++++++
.../commandline/cache/CacheSubcommands.java | 7 +-
.../testsuites/IgniteControlUtilityTestSuite.java | 2 +
.../ignite/util/CacheMetricsCommandTest.java | 278 +++++++++++++++++++++
.../visor/cache/metrics/CacheMetricsOperation.java | 53 ++++
.../visor/cache/metrics/VisorCacheMetricsTask.java | 114 +++++++++
.../cache/metrics/VisorCacheMetricsTaskArg.java | 84 +++++++
.../cache/metrics/VisorCacheMetricsTaskResult.java | 83 ++++++
.../main/resources/META-INF/classnames.properties | 5 +
...mandHandlerClusterByClassTest_cache_help.output | 7 +
...dlerClusterByClassWithSSLTest_cache_help.output | 7 +
13 files changed, 807 insertions(+), 2 deletions(-)
diff --git a/docs/_docs/tools/control-script.adoc b/docs/_docs/tools/control-script.adoc
index 599bf841de1..d1fe4732356 100644
--- a/docs/_docs/tools/control-script.adoc
+++ b/docs/_docs/tools/control-script.adoc
@@ -1081,3 +1081,32 @@ tab:Window[]
control.bat --enable-experimental --consistency status
----
--
+
+== Manage cache metrics collection
+
+The command provides an ability to enable, disable or show status of cache metrics collection.
+
+[source, shell]
+----
+control.sh|bat --cache metrics enable|disable|status --caches cache1[,...,cacheN]|--all-caches
+----
+
+Parameters:
+
+[cols="1,3",opts="header"]
+|===
+| Parameter | Description
+| `--caches cache1[,...,cacheN]`| Specifies a comma-separated list of cache names to which operation should be applied.
+| `--all-caches` | Applies operation to all user caches.
+|===
+
+Examples:
+[source, shell]
+----
+# Show metrics statuses for all caches:
+control.sh|bat --cache metrics status --all-caches
+
+# Enable metrics collection for cache-1 and cache-2:
+control.sh|bat --cache metrics enable --caches cache-2,cache-1
+----
+
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommandList.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommandList.java
index d11ddcbfbf6..5eed85e63c8 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommandList.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheCommandList.java
@@ -87,7 +87,12 @@ public enum CacheCommandList {
/**
* Index force rebuild.
*/
- INDEX_FORCE_REBUILD("indexes_force_rebuild", new CacheIndexesForceRebuild());
+ INDEX_FORCE_REBUILD("indexes_force_rebuild", new CacheIndexesForceRebuild()),
+
+ /**
+ * Enable, disable or show status for cache metrics.
+ */
+ METRICS("metrics", new CacheMetrics());
/** Enumerated values. */
private static final CacheCommandList[] VALS = values();
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheMetrics.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheMetrics.java
new file mode 100644
index 00000000000..602ac23eedd
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheMetrics.java
@@ -0,0 +1,133 @@
+/*
+ * 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.cache;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.logging.Logger;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.Command;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.TaskExecutor;
+import org.apache.ignite.internal.commandline.systemview.SystemViewCommand;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.visor.cache.metrics.CacheMetricsOperation;
+import org.apache.ignite.internal.visor.cache.metrics.VisorCacheMetricsTask;
+import org.apache.ignite.internal.visor.cache.metrics.VisorCacheMetricsTaskArg;
+import org.apache.ignite.internal.visor.cache.metrics.VisorCacheMetricsTaskResult;
+
+import static java.util.Arrays.asList;
+import static org.apache.ignite.internal.commandline.CommandLogger.optional;
+import static org.apache.ignite.internal.commandline.CommandLogger.or;
+import static org.apache.ignite.internal.commandline.cache.CacheSubcommands.METRICS;
+import static org.apache.ignite.internal.visor.cache.metrics.CacheMetricsOperation.DISABLE;
+import static org.apache.ignite.internal.visor.cache.metrics.CacheMetricsOperation.ENABLE;
+import static org.apache.ignite.internal.visor.cache.metrics.CacheMetricsOperation.STATUS;
+import static org.apache.ignite.internal.visor.systemview.VisorSystemViewTask.SimpleType.STRING;
+
+/**
+ * Cache sub-command for a cache metrics collection management. It provides an ability to enable, disable or show status.
+ */
+public class CacheMetrics extends AbstractCommand<VisorCacheMetricsTaskArg> {
+ /** Argument for applying metrics command operation to explicitly specified caches. */
+ public static final String CACHES_ARGUMENT = "--caches";
+
+ /** Argument for applying metrics command operation to all user caches. */
+ public static final String ALL_CACHES_ARGUMENT = "--all-caches";
+
+ /** Incorrect metrics operation message. */
+ public static final String INCORRECT_METRICS_OPERATION_MESSAGE = "Expected correct metrics command operation.";
+
+ /** Incorrect cache argument message. */
+ public static final String INCORRECT_CACHE_ARGUMENT_MESSAGE =
+ String.format("Expected one of these arguments: '%s' or '%s'. Multiple arguments are not allowed.",
+ CACHES_ARGUMENT, ALL_CACHES_ARGUMENT);
+
+ /** Expected caches list message. */
+ public static final String EXPECTED_CACHES_LIST_MESSAGE = "comma-separated list of cache names.";
+
+ /** Task argument. */
+ private VisorCacheMetricsTaskArg arg;
+
+ /** {@inheritDoc} */
+ @Override public Object execute(GridClientConfiguration clientCfg, Logger log) throws Exception {
+ try (GridClient client = Command.startClient(clientCfg)) {
+ VisorCacheMetricsTaskResult res = TaskExecutor.executeTaskByNameOnNode(client,
+ VisorCacheMetricsTask.class.getName(), arg, null, clientCfg);
+
+ List<List<?>> values = new ArrayList<>();
+
+ for (Map.Entry<String, Boolean> e : res.result().entrySet())
+ values.add(asList(e.getKey(), e.getValue() ? "enabled" : "disabled"));
+
+ SystemViewCommand.printTable(asList("Cache Name", "Metrics Status"), asList(STRING, STRING), values, log);
+
+ return null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ String desc = "Manages user cache metrics collection: enables, disables it or shows status.";
+
+ String cachesArgDesc = CACHES_ARGUMENT + " cache1" + optional(",...,cacheN");
+
+ Map<String, String> paramsDesc = F.asMap(
+ cachesArgDesc, "specifies a comma-separated list of cache names to which operation should be applied.",
+ ALL_CACHES_ARGUMENT, "applies operation to all user caches.");
+
+ usageCache(log, METRICS, desc, paramsDesc, or(ENABLE, DISABLE, STATUS), or(cachesArgDesc, ALL_CACHES_ARGUMENT));
+ }
+
+ /** {@inheritDoc} */
+ @Override public VisorCacheMetricsTaskArg arg() {
+ return arg;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void parseArguments(CommandArgIterator argIter) {
+ CacheMetricsOperation op = CacheMetricsOperation.of(argIter.nextArg(INCORRECT_METRICS_OPERATION_MESSAGE));
+
+ if (op == null)
+ throw new IllegalArgumentException(INCORRECT_METRICS_OPERATION_MESSAGE);
+
+ Set<String> cacheNames;
+
+ String arg = argIter.nextArg(INCORRECT_CACHE_ARGUMENT_MESSAGE);
+
+ if (CACHES_ARGUMENT.equals(arg))
+ cacheNames = new TreeSet<>(argIter.nextStringSet(EXPECTED_CACHES_LIST_MESSAGE));
+ else if (ALL_CACHES_ARGUMENT.equals(arg))
+ cacheNames = Collections.emptySet();
+ else
+ throw new IllegalArgumentException(INCORRECT_CACHE_ARGUMENT_MESSAGE);
+
+ this.arg = new VisorCacheMetricsTaskArg(op, cacheNames);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return METRICS.text().toUpperCase();
+ }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java
index 770e8ead20f..1565dafc95e 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/cache/CacheSubcommands.java
@@ -96,7 +96,12 @@ public enum CacheSubcommands {
/**
* Destroy caches.
*/
- DESTROY("destroy", null, new CacheDestroy());
+ DESTROY("destroy", null, new CacheDestroy()),
+
+ /**
+ * Enable / disable cache metrics collection or show metrics collection status.
+ */
+ METRICS("metrics", null, new CacheMetrics());
/** Enumerated values. */
private static final CacheSubcommands[] VALS = values();
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java
index f020609d046..02206c76b8b 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java
@@ -22,6 +22,7 @@ import org.apache.ignite.events.BaselineEventsRemoteTest;
import org.apache.ignite.internal.commandline.CommandHandlerParsingTest;
import org.apache.ignite.internal.commandline.indexreader.IgniteIndexReaderTest;
import org.apache.ignite.internal.processors.security.GridCommandHandlerSslWithSecurityTest;
+import org.apache.ignite.util.CacheMetricsCommandTest;
import org.apache.ignite.util.GridCommandHandlerBrokenIndexTest;
import org.apache.ignite.util.GridCommandHandlerCheckIndexesInlineSizeTest;
import org.apache.ignite.util.GridCommandHandlerClusterByClassTest;
@@ -103,6 +104,7 @@ import org.junit.runners.Suite;
SystemViewCommandTest.class,
MetricCommandTest.class,
PerformanceStatisticsCommandTest.class,
+ CacheMetricsCommandTest.class,
IgniteIndexReaderTest.class
})
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/CacheMetricsCommandTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/CacheMetricsCommandTest.java
new file mode 100644
index 00000000000..ad7d99ebe30
--- /dev/null
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/CacheMetricsCommandTest.java
@@ -0,0 +1,278 @@
+/*
+ * 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.util;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.commandline.cache.CacheMetrics;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.internal.visor.cache.metrics.CacheMetricsOperation;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+
+import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_INVALID_ARGUMENTS;
+import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
+import static org.apache.ignite.internal.commandline.CommandList.CACHE;
+import static org.apache.ignite.internal.commandline.cache.CacheMetrics.ALL_CACHES_ARGUMENT;
+import static org.apache.ignite.internal.commandline.cache.CacheMetrics.CACHES_ARGUMENT;
+import static org.apache.ignite.internal.commandline.cache.CacheMetrics.EXPECTED_CACHES_LIST_MESSAGE;
+import static org.apache.ignite.internal.commandline.cache.CacheMetrics.INCORRECT_CACHE_ARGUMENT_MESSAGE;
+import static org.apache.ignite.internal.commandline.cache.CacheMetrics.INCORRECT_METRICS_OPERATION_MESSAGE;
+import static org.apache.ignite.internal.commandline.cache.CacheSubcommands.METRICS;
+import static org.apache.ignite.internal.util.lang.GridFunc.asMap;
+
+/**
+ * Test for {@link CacheMetrics} command.
+ */
+public class CacheMetricsCommandTest extends GridCommandHandlerAbstractTest {
+ /** Enable operation. */
+ private static final String ENABLE = CacheMetricsOperation.ENABLE.toString();
+
+ /** Disable operation. */
+ private static final String DISABLE = CacheMetricsOperation.DISABLE.toString();
+
+ /** Status operation. */
+ private static final String STATUS = CacheMetricsOperation.STATUS.toString();
+
+ /** Cache one. */
+ private static final String CACHE_ONE = "cache-1";
+
+ /** Cache two. */
+ private static final String CACHE_TWO = "cache-2";
+
+ /** {@inheritDoc} */
+ @Override public void beforeTest() throws Exception {
+ super.beforeTest();
+
+ injectTestSystemOut();
+ persistenceEnable(false);
+ autoConfirmation = false;
+
+ startGrids(2);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+
+ stopAllGrids();
+ }
+
+ /**
+ * Tests metrics enable / disable operations.
+ */
+ @Test
+ public void testEnableDisable() {
+ // Test empty cluster
+ checkExecutionSuccess(Collections.emptyMap(), ENABLE, ALL_CACHES_ARGUMENT);
+
+ createCachesWithMetrics(asMap(CACHE_ONE, false, CACHE_TWO, true));
+
+ checkExecutionSuccess(asMap(CACHE_ONE, true), ENABLE, CACHES_ARGUMENT, CACHE_ONE);
+ checkExecutionSuccess(asMap(CACHE_TWO, false), DISABLE, CACHES_ARGUMENT, CACHE_TWO);
+
+ // Cache list with duplicates
+ String cacheNames = String.join(",", CACHE_TWO, CACHE_ONE, CACHE_ONE, CACHE_TWO);
+
+ checkExecutionSuccess(asMap(CACHE_ONE, false, CACHE_TWO, false), DISABLE, CACHES_ARGUMENT, cacheNames);
+
+ checkExecutionSuccess(asMap(CACHE_ONE, true, CACHE_TWO, true), ENABLE, ALL_CACHES_ARGUMENT);
+ }
+
+ /**
+ * Tests metrics status operation.
+ */
+ @Test
+ public void testStatus() {
+ // Test empty cluster
+ checkExecutionSuccess(Collections.emptyMap(), STATUS, ALL_CACHES_ARGUMENT);
+
+ createCachesWithMetrics(asMap(CACHE_ONE, false, CACHE_TWO, true));
+
+ checkExecutionSuccess(asMap(CACHE_ONE, false), STATUS, CACHES_ARGUMENT, CACHE_ONE);
+ checkExecutionSuccess(asMap(CACHE_TWO, true), STATUS, CACHES_ARGUMENT, CACHE_TWO);
+
+ // Cache list with duplicates
+ String cacheNames = String.join(",", CACHE_TWO, CACHE_ONE, CACHE_ONE, CACHE_TWO);
+
+ checkExecutionSuccess(asMap(CACHE_ONE, false, CACHE_TWO, true), STATUS, CACHES_ARGUMENT, cacheNames);
+
+ checkExecutionSuccess(asMap(CACHE_ONE, false, CACHE_TWO, true), STATUS, ALL_CACHES_ARGUMENT);
+ }
+
+ /**
+ * Tests metrics operations for a non-existing cache.
+ */
+ @Test
+ public void testNotFoundCache() {
+ createCachesWithMetrics(asMap(CACHE_ONE, false));
+
+ String descriptorsNotFoundMsg = "One or more cache descriptors not found [caches=[" + CACHE_ONE + ", " +
+ CACHE_TWO + ']';
+
+ checkExecutionError(descriptorsNotFoundMsg, ENABLE, CACHES_ARGUMENT, CACHE_ONE + ',' + CACHE_TWO);
+
+ // Check that metrics statuses was not changed
+ checkClusterMetrics(asMap(CACHE_ONE, false));
+
+ checkExecutionError("Cache does not exist: " + CACHE_TWO, STATUS, CACHES_ARGUMENT, CACHE_ONE + ',' + CACHE_TWO);
+ }
+
+ /** */
+ @Test
+ public void testInvalidArguments() {
+ String checkArgs = "Check arguments. ";
+
+ // Check when no operation passed
+ checkInvalidArguments(checkArgs + INCORRECT_METRICS_OPERATION_MESSAGE);
+
+ // Check when unknown operation passed
+ checkInvalidArguments(checkArgs + INCORRECT_METRICS_OPERATION_MESSAGE, "bad-command");
+
+ // Check when no --caches/--all-caches arguments passed
+ checkInvalidArguments(checkArgs + INCORRECT_CACHE_ARGUMENT_MESSAGE, ENABLE);
+ checkInvalidArguments(checkArgs + INCORRECT_CACHE_ARGUMENT_MESSAGE, STATUS);
+
+ String invalidCacheListFullMsg = "Check arguments. Expected " + EXPECTED_CACHES_LIST_MESSAGE;
+
+ // Check when --caches argument passed without list of caches
+ checkInvalidArguments(invalidCacheListFullMsg, ENABLE, CACHES_ARGUMENT);
+ checkInvalidArguments(invalidCacheListFullMsg, STATUS, CACHES_ARGUMENT);
+
+ String incorrectCacheArgFullMsg = checkArgs + INCORRECT_CACHE_ARGUMENT_MESSAGE;
+
+ // Check when unknown argument is passed after metric operation
+ checkInvalidArguments(incorrectCacheArgFullMsg, ENABLE, "--arg");
+ checkInvalidArguments(incorrectCacheArgFullMsg, STATUS, "--arg");
+
+ String unexpectedCacheArgMsg = "Unexpected argument of --cache subcommand: ";
+
+ // Check when extra argument passed after correct command
+ checkInvalidArguments(unexpectedCacheArgMsg + ALL_CACHES_ARGUMENT, ENABLE, CACHES_ARGUMENT, CACHE_ONE,
+ ALL_CACHES_ARGUMENT);
+ checkInvalidArguments(unexpectedCacheArgMsg + CACHES_ARGUMENT, STATUS, ALL_CACHES_ARGUMENT, CACHES_ARGUMENT,
+ CACHE_ONE);
+
+ // Check when after --caches argument extra argument is passed instead of list of caches
+ checkInvalidArguments(unexpectedCacheArgMsg + ALL_CACHES_ARGUMENT, ENABLE, CACHES_ARGUMENT, ALL_CACHES_ARGUMENT);
+ }
+
+ /**
+ * Check execution of metric operation and parse resulting table.
+ *
+ * @param expStatuses Expected table entries in command output.
+ * @param args Command arguments.
+ */
+ private void checkExecutionSuccess(Map<String, Boolean> expStatuses, String... args) {
+ exec(EXIT_CODE_OK, args);
+
+ checkOutput(expStatuses, testOut.toString());
+
+ checkClusterMetrics(expStatuses);
+ }
+
+ /**
+ * @param expExitCode Expected exit code.
+ * @param args Command arguments.
+ */
+ private void exec(int expExitCode, String... args) {
+ String[] fullArgs = F.concat(new String[] {CACHE.text(), METRICS.text()}, args);
+
+ int exitCode = execute(fullArgs);
+ assertEquals("Unexpected exit code", expExitCode, exitCode);
+ }
+
+ /** */
+ private void checkInvalidArguments(String expOut, String... args) {
+ checkExecutionAndOutput(EXIT_CODE_INVALID_ARGUMENTS, expOut, args);
+ }
+
+ /** */
+ private void checkExecutionError(String expOut, String... args) {
+ checkExecutionAndOutput(EXIT_CODE_UNEXPECTED_ERROR, expOut, args);
+ }
+
+ /**
+ * Check command execution and results.
+ *
+ * @param expExitCode Expected exit code.
+ * @param expOut Expected command output.
+ * @param args Command arguments.
+ */
+ private void checkExecutionAndOutput(int expExitCode, String expOut, String... args) {
+ exec(expExitCode, args);
+
+ GridTestUtils.assertContains(log, testOut.toString(), expOut);
+ }
+
+ /**
+ * @param expStatuses Expected metrics statuses.
+ * @param testOutStr Test output.
+ */
+ private void checkOutput(Map<String, Boolean> expStatuses, String testOutStr) {
+ for (Map.Entry<String, Boolean> entry : expStatuses.entrySet()) {
+ String cacheName = entry.getKey();
+ String metricsStatus = entry.getValue() ? "enabled" : "disabled";
+
+ Matcher cacheStatusMatcher = Pattern.compile(cacheName + "\\s+" + metricsStatus).matcher(testOutStr);
+
+ String msg = String.format("Unexpected count of table entries for metrics: [cacheName=%s, metricsStatus=%s]",
+ cacheName, metricsStatus);
+
+ int cnt = 0;
+
+ while (cacheStatusMatcher.find())
+ cnt++;
+
+ assertEquals(msg, 1, cnt);
+ }
+ }
+
+ /**
+ * @param metricsStatuses Metrics statuses.
+ */
+ private void createCachesWithMetrics(Map<String, Boolean> metricsStatuses) {
+ for (Map.Entry<String, Boolean> nameAndState : metricsStatuses.entrySet()) {
+ grid(0).getOrCreateCache(new CacheConfiguration<>()
+ .setName(nameAndState.getKey())
+ .setStatisticsEnabled(nameAndState.getValue()));
+ }
+
+ checkClusterMetrics(metricsStatuses);
+ }
+
+ /**
+ * @param expStatuses Expected cache metrics statuses.
+ */
+ private void checkClusterMetrics(Map<String, Boolean> expStatuses) {
+ for (Ignite ignite : G.allGrids()) {
+ for (String cacheName : expStatuses.keySet()) {
+ Boolean cacheMetricsEnabled = ignite.cache(cacheName).metrics().isStatisticsEnabled();
+
+ assertEquals("Unexpected metrics mode for cache: " + cacheName, expStatuses.get(cacheName),
+ cacheMetricsEnabled);
+ }
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/CacheMetricsOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/CacheMetricsOperation.java
new file mode 100644
index 00000000000..133e454fca8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/CacheMetricsOperation.java
@@ -0,0 +1,53 @@
+/*
+ * 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.cache.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Enum for cache metrics command operations.
+ */
+public enum CacheMetricsOperation {
+ /** Enable operation. */
+ ENABLE,
+
+ /** Disable operation. */
+ DISABLE,
+
+ /** Status operation. */
+ STATUS;
+
+ /**
+ * @param strRep String representation of operation.
+ *
+ * @return Operation corresponding to the specified string representation.
+ */
+ public static @Nullable CacheMetricsOperation of(String strRep) {
+ for (CacheMetricsOperation op : values()) {
+ if (op.name().equalsIgnoreCase(strRep))
+ return op;
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return super.toString().toLowerCase();
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTask.java
new file mode 100644
index 00000000000..5071ded10e6
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTask.java
@@ -0,0 +1,114 @@
+/*
+ * 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.cache.metrics;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.processors.task.GridVisorManagementTask;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorOneNodeTask;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.visor.cache.metrics.CacheMetricsOperation.ENABLE;
+
+/**
+ * Task for a cache metrics command.
+ */
+@GridInternal
+@GridVisorManagementTask
+public class VisorCacheMetricsTask extends VisorOneNodeTask<VisorCacheMetricsTaskArg, VisorCacheMetricsTaskResult> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override protected VisorCacheMetricsJob job(VisorCacheMetricsTaskArg arg) {
+ return new VisorCacheMetricsJob(arg, false);
+ }
+
+ /**
+ * Job returns {@link Map} with names of processed caches paired with corresponding metrics collection statuses or
+ * exception, caught during execution of job.
+ * Results are passed into instance of wrapper class {@link VisorCacheMetricsTaskResult}.
+ */
+ private static class VisorCacheMetricsJob extends VisorJob<VisorCacheMetricsTaskArg, VisorCacheMetricsTaskResult> {
+ /** Serial version uid. */
+ 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 VisorCacheMetricsJob(@Nullable VisorCacheMetricsTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected VisorCacheMetricsTaskResult run(@Nullable VisorCacheMetricsTaskArg arg)
+ throws IgniteException {
+ if (arg != null) {
+ Collection<String> cacheNames = F.isEmpty(arg.cacheNames()) ? ignite.cacheNames() : arg.cacheNames();
+
+ try {
+ switch (arg.operation()) {
+ case ENABLE:
+ case DISABLE:
+ ignite.cluster().enableStatistics(cacheNames, ENABLE == arg.operation());
+
+ return new VisorCacheMetricsTaskResult(cacheMetricsStatus(cacheNames));
+
+ case STATUS:
+ return new VisorCacheMetricsTaskResult(cacheMetricsStatus(cacheNames));
+
+ default:
+ throw new IllegalStateException("Unexpected value: " + arg.operation());
+ }
+ }
+ catch (Exception e) {
+ return new VisorCacheMetricsTaskResult(e);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param cacheNames Cache names.
+ */
+ private Map<String, Boolean> cacheMetricsStatus(Collection<String> cacheNames) {
+ Map<String, Boolean> cacheMetricsStatus = new TreeMap<>();
+
+ for (String cacheName : cacheNames) {
+ IgniteInternalCache<?, ?> cachex = ignite.cachex(cacheName);
+
+ if (cachex != null)
+ cacheMetricsStatus.put(cacheName, cachex.clusterMetrics().isStatisticsEnabled());
+ else
+ throw new IgniteException("Cache does not exist: " + cacheName);
+ }
+
+ return cacheMetricsStatus;
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTaskArg.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTaskArg.java
new file mode 100644
index 00000000000..c6574e40532
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTaskArg.java
@@ -0,0 +1,84 @@
+/*
+ * 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.cache.metrics;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Collections;
+import java.util.Set;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Task argument for {@link VisorCacheMetricsTask}.
+ */
+public class VisorCacheMetricsTaskArg extends IgniteDataTransferObject {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** Metrics command operation. */
+ private CacheMetricsOperation op;
+
+ /** Caches which will be processed. If not set, operation will affect all caches. */
+ private Set<String> cacheNames;
+
+ /**
+ * Default constructor.
+ */
+ public VisorCacheMetricsTaskArg() {
+ // No-op.
+ }
+
+ /**
+ * @param op Metrics command operation.
+ * @param cacheNames Names of the caches, which should be processed.
+ */
+ public VisorCacheMetricsTaskArg(CacheMetricsOperation op, Set<String> cacheNames) {
+ this.op = op;
+ this.cacheNames = cacheNames == null ? null : Collections.unmodifiableSet(cacheNames);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ U.writeEnum(out, op);
+ U.writeCollection(out, cacheNames);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException,
+ ClassNotFoundException {
+ op = U.readEnum(in, CacheMetricsOperation.class);
+ cacheNames = U.readSet(in);
+ }
+
+ /**
+ * @return Metrics command operation.
+ */
+ public CacheMetricsOperation operation() {
+ return op;
+ }
+
+ /**
+ * @return Caches which will be processed. If not set, operation will affect all caches.
+ */
+ public Set<String> cacheNames() {
+ return Collections.unmodifiableSet(cacheNames);
+ }
+}
+
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTaskResult.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTaskResult.java
new file mode 100644
index 00000000000..e232f7048f2
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/metrics/VisorCacheMetricsTaskResult.java
@@ -0,0 +1,83 @@
+/*
+ * 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.cache.metrics;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Collections;
+import java.util.Map;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Result wrapper for {@link VisorCacheMetricsTask}.
+ */
+public class VisorCacheMetricsTaskResult extends IgniteDataTransferObject {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** Task result. */
+ private Map<String, Boolean> result;
+
+ /** Task execution error. */
+ private Exception error;
+
+ /**
+ * Default constructor.
+ */
+ public VisorCacheMetricsTaskResult() {
+ // No-op.
+ }
+
+ /**
+ * @param result Task execution result.
+ */
+ public VisorCacheMetricsTaskResult(Map<String, Boolean> result) {
+ this.result = Collections.unmodifiableMap(result);
+ }
+
+ /**
+ * @param error Task execution error.
+ */
+ public VisorCacheMetricsTaskResult(Exception error) {
+ this.error = error;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ U.writeMap(out, result);
+ out.writeObject(error);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte protoVer, ObjectInput in) throws IOException, ClassNotFoundException {
+ result = U.readMap(in);
+ error = (Exception)in.readObject();
+ }
+
+ /**
+ * Get task result or task execution error.
+ */
+ public Map<String, Boolean> result() throws Exception {
+ if (error != null)
+ throw error;
+
+ return Collections.unmodifiableMap(result);
+ }
+}
diff --git a/modules/core/src/main/resources/META-INF/classnames.properties b/modules/core/src/main/resources/META-INF/classnames.properties
index 266910ea138..5aeb89669a7 100644
--- a/modules/core/src/main/resources/META-INF/classnames.properties
+++ b/modules/core/src/main/resources/META-INF/classnames.properties
@@ -2051,6 +2051,11 @@ org.apache.ignite.internal.visor.cache.VisorCacheMetrics
org.apache.ignite.internal.visor.cache.VisorCacheMetricsCollectorTask
org.apache.ignite.internal.visor.cache.VisorCacheMetricsCollectorTask$VisorCacheMetricsCollectorJob
org.apache.ignite.internal.visor.cache.VisorCacheMetricsCollectorTaskArg
+org.apache.ignite.internal.visor.cache.metrics.CacheMetricsOperation
+org.apache.ignite.internal.visor.cache.metrics.VisorCacheMetricsTask
+org.apache.ignite.internal.visor.cache.metrics.VisorCacheMetricsTask$VisorCacheMetricsJob
+org.apache.ignite.internal.visor.cache.metrics.VisorCacheMetricsTaskArg
+org.apache.ignite.internal.visor.cache.metrics.VisorCacheMetricsTaskResult
org.apache.ignite.internal.visor.cache.VisorCacheModifyTask
org.apache.ignite.internal.visor.cache.VisorCacheModifyTask$VisorCacheModifyJob
org.apache.ignite.internal.visor.cache.VisorCacheModifyTaskArg
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 d175fcce598..585370b4963 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
@@ -83,6 +83,13 @@ Arguments: --cache help --yes
--cache-names - Comma-separated list of cache names for which indexes should be rebuilt.
--group-names - Comma-separated list of cache group names for which indexes should be rebuilt.
+ --cache metrics enable|disable|status --caches cache1[,...,cacheN]|--all-caches
+ Manages user cache metrics collection: enables, disables it or shows status.
+
+ Parameters:
+ --caches cache1[,...,cacheN] - specifies a comma-separated list of cache names to which operation should be applied.
+ --all-caches - applies operation to all user caches.
+
Command [CACHE] finished with code: 0
Control utility has completed execution at: <!any!>
Execution time: <!any!>
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 d175fcce598..585370b4963 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
@@ -83,6 +83,13 @@ Arguments: --cache help --yes
--cache-names - Comma-separated list of cache names for which indexes should be rebuilt.
--group-names - Comma-separated list of cache group names for which indexes should be rebuilt.
+ --cache metrics enable|disable|status --caches cache1[,...,cacheN]|--all-caches
+ Manages user cache metrics collection: enables, disables it or shows status.
+
+ Parameters:
+ --caches cache1[,...,cacheN] - specifies a comma-separated list of cache names to which operation should be applied.
+ --all-caches - applies operation to all user caches.
+
Command [CACHE] finished with code: 0
Control utility has completed execution at: <!any!>
Execution time: <!any!>