This is an automated email from the ASF dual-hosted git repository.

alexpl 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 4e517b9402b IGNITE-26510 Add max value aggregated interval metric - 
Fixes #12371.
4e517b9402b is described below

commit 4e517b9402b86b855142648a06650c14ad01c8a1
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Thu Oct 2 20:06:48 2025 +0300

    IGNITE-26510 Add max value aggregated interval metric - Fixes #12371.
    
    Signed-off-by: Aleksey Plekhanov <[email protected]>
---
 .../benchmarks/jol/GridMetricsJolBenchmark.java    |   2 +-
 .../org/apache/ignite/util/MetricCommandTest.java  |  26 ++-
 .../internal/management/metric/MetricCommand.java  |   3 +-
 .../metric/MetricConfigureMaxValueCommand.java     |  48 ++++
 .../metric/MetricConfigureMaxValueCommandArg.java  |  59 +++++
 .../internal/management/metric/MetricTask.java     |   5 +
 .../processors/metric/GridMetricManager.java       |  72 ++++--
 .../processors/metric/MetricRegistryImpl.java      |  45 +++-
 .../metric/impl/AbstractIntervalMetric.java        | 243 +++++++++++++++++++++
 .../processors/metric/impl/HitRateMetric.java      | 196 ++---------------
 .../processors/metric/impl/MaxValueMetric.java     |  85 +++++++
 .../ignite/internal/metric/JmxExporterSpiTest.java |   2 +-
 .../internal/metric/MetricsConfigurationTest.java  |  76 ++++++-
 .../ignite/internal/metric/MetricsSelfTest.java    |  90 +++++++-
 .../tcp/GridTcpCommunicationSpiConfigSelfTest.java |   2 +-
 .../ignite/testframework/GridSpiTestContext.java   |   2 +-
 ...ridCommandHandlerClusterByClassTest_help.output |   8 +
 ...andHandlerClusterByClassWithSSLTest_help.output |   8 +
 18 files changed, 761 insertions(+), 211 deletions(-)

diff --git 
a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java
 
b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java
index 29195814092..cf996f1a7cb 100644
--- 
a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java
+++ 
b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jol/GridMetricsJolBenchmark.java
@@ -111,7 +111,7 @@ public class GridMetricsJolBenchmark {
      * Calculates and prints the size of metric registry of {@code TOTAL} size;
      */
     private static void measureMetricRegistry() {
-        MetricRegistryImpl mreg = new MetricRegistryImpl("test", name -> null, 
name -> null, null);
+        MetricRegistryImpl mreg = new MetricRegistryImpl("test", name -> null, 
name -> null, name -> null, null);
 
         for (int i = 0; i < BOOLEAN_CNT; i++)
             mreg.booleanMetric(BOOLEAN_METRIC + i, null);
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java
index 7450bad6783..8af01ebcb5b 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/util/MetricCommandTest.java
@@ -27,6 +27,7 @@ import 
org.apache.ignite.internal.management.metric.MetricCommand;
 import org.apache.ignite.internal.processors.metric.MetricRegistryImpl;
 import org.apache.ignite.internal.processors.metric.impl.HistogramMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
+import org.apache.ignite.internal.processors.metric.impl.MaxValueMetric;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.junit.Test;
@@ -54,6 +55,9 @@ public class MetricCommandTest extends 
GridCommandHandlerClusterByClassAbstractT
     /** */
     private static final String CONFIGURE_HITRATE = "--configure-hitrate";
 
+    /** */
+    private static final String CONFIGURE_MAXVAL = "--configure-max-value";
+
     /** Test node with 0 index. */
     private IgniteEx ignite0;
 
@@ -219,11 +223,29 @@ public class MetricCommandTest extends 
GridCommandHandlerClusterByClassAbstractT
 
         HitRateMetric hitrate = mreg.hitRateMetric("hitrate", null, 500, 5);
 
-        assertEquals(500, hitrate.rateTimeInterval());
+        assertEquals(500, hitrate.timeInterval());
 
         executeCommand(EXIT_CODE_OK, CMD_METRIC, CONFIGURE_HITRATE, 
hitrate.name(), "100");
 
-        assertEquals(100, hitrate.rateTimeInterval());
+        assertEquals(100, hitrate.timeInterval());
+    }
+
+    /** Tests configuration of MaxValue metric. */
+    @Test
+    public void testConfigureMaxValue() {
+        String mregName = "configure-registry";
+
+        ignite0.context().metric().remove(mregName);
+
+        MetricRegistryImpl mreg = 
ignite0.context().metric().registry(mregName);
+
+        MaxValueMetric maxVal = mreg.maxValueMetric("maxval", null, 500, 5);
+
+        assertEquals(500, maxVal.timeInterval());
+
+        executeCommand(EXIT_CODE_OK, CMD_METRIC, CONFIGURE_MAXVAL, 
maxVal.name(), "100");
+
+        assertEquals(100, maxVal.timeInterval());
     }
 
     /** */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java
index f9ddc76eeee..3709a0764d0 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricCommand.java
@@ -41,7 +41,8 @@ public class MetricCommand extends 
CommandRegistryImpl<MetricCommandArg, Map<Str
     public MetricCommand() {
         super(
             new MetricConfigureHistogramCommand(),
-            new MetricConfigureHitrateCommand()
+            new MetricConfigureHitrateCommand(),
+            new MetricConfigureMaxValueCommand()
         );
     }
 
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureMaxValueCommand.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureMaxValueCommand.java
new file mode 100644
index 00000000000..8f59274650b
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureMaxValueCommand.java
@@ -0,0 +1,48 @@
+/*
+ * 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.management.metric;
+
+import java.util.Collection;
+import java.util.Map;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.management.api.ComputeCommand;
+
+import static 
org.apache.ignite.internal.management.api.CommandUtils.nodeOrNull;
+
+/** */
+public class MetricConfigureMaxValueCommand implements 
ComputeCommand<MetricCommandArg, Map<String, ?>> {
+    /** {@inheritDoc} */
+    @Override public String description() {
+        return "Configure MaxValue metric";
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<MetricConfigureMaxValueCommandArg> argClass() {
+        return MetricConfigureMaxValueCommandArg.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<MetricTask> taskClass() {
+        return MetricTask.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<ClusterNode> nodes(Collection<ClusterNode> 
nodes, MetricCommandArg arg) {
+        return nodeOrNull(arg.nodeId(), nodes);
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureMaxValueCommandArg.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureMaxValueCommandArg.java
new file mode 100644
index 00000000000..09ae7ee95a0
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricConfigureMaxValueCommandArg.java
@@ -0,0 +1,59 @@
+/*
+ * 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.management.metric;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.internal.management.api.Argument;
+import org.apache.ignite.internal.management.api.Positional;
+
+/** */
+public class MetricConfigureMaxValueCommandArg extends MetricCommandArg {
+    /** */
+    private static final long serialVersionUID = 0;
+
+    /** */
+    @Argument(description = "Time interval of the metric", example = 
"newTimeInterval")
+    @Positional
+    private long newTimeInterval;
+
+    /** {@inheritDoc} */
+    @Override protected void writeExternalData(ObjectOutput out) throws 
IOException {
+        super.writeExternalData(out);
+
+        out.writeLong(newTimeInterval);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void readExternalData(ObjectInput in) throws 
IOException, ClassNotFoundException {
+        super.readExternalData(in);
+
+        newTimeInterval = in.readLong();
+    }
+
+    /** */
+    public long newTimeInterval() {
+        return newTimeInterval;
+    }
+
+    /** */
+    public void newTimeInterval(long interval) {
+        this.newTimeInterval = interval;
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricTask.java
index cd3e66b5c8c..fe6e002d6d7 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricTask.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/metric/MetricTask.java
@@ -81,6 +81,11 @@ public class MetricTask extends 
VisorOneNodeTask<MetricCommandArg, Map<String, ?
                 else if (arg instanceof MetricConfigureHitrateCommandArg) {
                     mmgr.configureHitRate(arg.name(), 
((MetricConfigureHitrateCommandArg)arg).newRateTimeInterval());
 
+                    return null;
+                }
+                else if (arg instanceof MetricConfigureMaxValueCommandArg) {
+                    mmgr.configureMaxValueMetric(arg.name(), 
((MetricConfigureMaxValueCommandArg)arg).newTimeInterval());
+
                     return null;
                 }
             }
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
index 43c69ef84e6..3474e3d3dfa 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/GridMetricManager.java
@@ -41,9 +41,11 @@ import 
org.apache.ignite.internal.managers.GridManagerAdapter;
 import 
org.apache.ignite.internal.processors.metastorage.DistributedMetaStorage;
 import 
org.apache.ignite.internal.processors.metastorage.DistributedMetastorageLifecycleListener;
 import 
org.apache.ignite.internal.processors.metastorage.ReadableDistributedMetaStorage;
+import 
org.apache.ignite.internal.processors.metric.impl.AbstractIntervalMetric;
 import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric;
 import org.apache.ignite.internal.processors.metric.impl.DoubleMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
+import org.apache.ignite.internal.processors.metric.impl.MaxValueMetric;
 import org.apache.ignite.internal.processors.metric.impl.MetricUtils;
 import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor;
 import org.apache.ignite.internal.util.future.GridCompoundFuture;
@@ -159,6 +161,9 @@ public class GridMetricManager extends 
GridManagerAdapter<MetricExporterSpi> imp
     /** Prefix for {@link HitRateMetric} configuration property name. */
     public static final String HITRATE_CFG_PREFIX = metricName("metrics", 
"hitrate");
 
+    /** Prefix for {@link MaxValueMetric} configuration property name. */
+    public static final String MAXVAL_CFG_PREFIX = metricName("metrics", 
"maxval");
+
     /** Prefix for {@link HistogramMetric} configuration property name. */
     public static final String HISTOGRAM_CFG_PREFIX = metricName("metrics", 
"histogram");
 
@@ -278,9 +283,12 @@ public class GridMetricManager extends 
GridManagerAdapter<MetricExporterSpi> imp
                     roMetastorage = metastorage;
 
                     try {
-                        metastorage.iterate(HITRATE_CFG_PREFIX, (name, val) -> 
onHitRateConfigChanged(
+                        metastorage.iterate(HITRATE_CFG_PREFIX, (name, val) -> 
onIntervalMetricConfigChanged(
                             name.substring(HITRATE_CFG_PREFIX.length() + 1), 
(Long)val));
 
+                        metastorage.iterate(MAXVAL_CFG_PREFIX, (name, val) -> 
onIntervalMetricConfigChanged(
+                            name.substring(MAXVAL_CFG_PREFIX.length() + 1), 
(Long)val));
+
                         metastorage.iterate(HISTOGRAM_CFG_PREFIX, (name, val) 
-> onHistogramConfigChanged(
                             name.substring(HISTOGRAM_CFG_PREFIX.length() + 1), 
(long[])val));
                     }
@@ -289,9 +297,13 @@ public class GridMetricManager extends 
GridManagerAdapter<MetricExporterSpi> imp
                     }
 
                     metastorage.listen(n -> n.startsWith(HITRATE_CFG_PREFIX),
-                        (name, oldVal, newVal) -> onHitRateConfigChanged(
+                        (name, oldVal, newVal) -> 
onIntervalMetricConfigChanged(
                             name.substring(HITRATE_CFG_PREFIX.length() + 1), 
(Long)newVal));
 
+                    metastorage.listen(n -> n.startsWith(MAXVAL_CFG_PREFIX),
+                        (name, oldVal, newVal) -> 
onIntervalMetricConfigChanged(
+                            name.substring(MAXVAL_CFG_PREFIX.length() + 1), 
(Long)newVal));
+
                     metastorage.listen(n -> n.startsWith(HISTOGRAM_CFG_PREFIX),
                         (name, oldVal, newVal) -> onHistogramConfigChanged(
                             name.substring(HISTOGRAM_CFG_PREFIX.length() + 1), 
(long[])newVal));
@@ -324,6 +336,7 @@ public class GridMetricManager extends 
GridManagerAdapter<MetricExporterSpi> imp
         return (MetricRegistryImpl)registries.computeIfAbsent(name, n -> {
             MetricRegistryImpl mreg = new MetricRegistryImpl(name,
                 custom ? null : mname -> 
readFromMetastorage(metricName(HITRATE_CFG_PREFIX, mname)),
+                custom ? null : mname -> 
readFromMetastorage(metricName(MAXVAL_CFG_PREFIX, mname)),
                 custom ? null : mname -> 
readFromMetastorage(metricName(HISTOGRAM_CFG_PREFIX, mname)),
                 log);
 
@@ -405,6 +418,8 @@ public class GridMetricManager extends 
GridManagerAdapter<MetricExporterSpi> imp
                 for (Metric m : mreg) {
                     if (m instanceof HitRateMetric)
                         
opsFut.add(metastorage0.removeAsync(metricName(HITRATE_CFG_PREFIX, m.name())));
+                    else if (m instanceof MaxValueMetric)
+                        
opsFut.add(metastorage0.removeAsync(metricName(MAXVAL_CFG_PREFIX, m.name())));
                     else if (m instanceof HistogramMetric)
                         
opsFut.add(metastorage0.removeAsync(metricName(HISTOGRAM_CFG_PREFIX, 
m.name())));
                 }
@@ -437,16 +452,47 @@ public class GridMetricManager extends 
GridManagerAdapter<MetricExporterSpi> imp
      * @see HitRateMetric#reset(long, int)
      */
     public void configureHitRate(String name, long rateTimeInterval) throws 
IgniteCheckedException {
+        configureIntervalMetric(name, HitRateMetric.class, HITRATE_CFG_PREFIX, 
rateTimeInterval);
+    }
+
+    /**
+     * Change {@link AbstractIntervalMetric} configuration if it exists.
+     *
+     * @param name Metric name.
+     * @param interval New time interval.
+     * @throws IgniteCheckedException If write of configuration failed.
+     * @see AbstractIntervalMetric#reset(long, int)
+     */
+    public void configureMaxValueMetric(String name, long interval) throws 
IgniteCheckedException {
+        configureIntervalMetric(name, MaxValueMetric.class, MAXVAL_CFG_PREFIX, 
interval);
+    }
+
+    /**
+     * Change {@link AbstractIntervalMetric} configuration if it exists.
+     *
+     * @param name Metric name.
+     * @param cls Metric class.
+     * @param cfgPrefix Metric config prefix.
+     * @param interval New time interval.
+     * @throws IgniteCheckedException If write of configuration failed.
+     * @see AbstractIntervalMetric#reset(long, int)
+     */
+    private void configureIntervalMetric(
+        String name,
+        Class<? extends AbstractIntervalMetric> cls,
+        String cfgPrefix,
+        long interval
+    ) throws IgniteCheckedException {
         A.notNullOrEmpty(name, "name");
-        A.ensure(rateTimeInterval > 0, "rateTimeInterval should be positive");
+        A.ensure(interval > 0, "interval should be positive");
         A.notNull(metastorage, "Metastorage not ready. Node not started?");
 
         if (ctx.isStopping())
             throw new NodeStoppingException("Operation has been cancelled 
(node is stopping)");
 
-        ensureMetricRegistered(name, HitRateMetric.class);
+        ensureMetricRegistered(name, cls);
 
-        metastorage.write(metricName(HITRATE_CFG_PREFIX, name), 
rateTimeInterval);
+        metastorage.write(metricName(cfgPrefix, name), interval);
     }
 
     /**
@@ -470,24 +516,24 @@ public class GridMetricManager extends 
GridManagerAdapter<MetricExporterSpi> imp
     }
 
     /**
-     * Change {@link HitRateMetric} instance configuration.
+     * Change {@link AbstractIntervalMetric} instance configuration.
      *
      * @param name Metric name.
-     * @param rateTimeInterval New rateTimeInterval.
-     * @see HitRateMetric#reset(long)
+     * @param interval New interval.
+     * @see AbstractIntervalMetric#reset(long)
      */
-    private void onHitRateConfigChanged(String name, @Nullable Long 
rateTimeInterval) {
-        if (rateTimeInterval == null)
+    private void onIntervalMetricConfigChanged(String name, @Nullable Long 
interval) {
+        if (interval == null)
             return;
 
-        A.ensure(rateTimeInterval > 0, "rateTimeInterval should be positive");
+        A.ensure(interval > 0, "interval should be positive");
 
-        HitRateMetric m = find(name, HitRateMetric.class);
+        AbstractIntervalMetric m = find(name, AbstractIntervalMetric.class);
 
         if (m == null)
             return;
 
-        m.reset(rateTimeInterval);
+        m.reset(interval);
     }
 
     /**
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistryImpl.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistryImpl.java
index 227f7901136..d4fe5729ef2 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistryImpl.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/MetricRegistryImpl.java
@@ -39,6 +39,7 @@ import 
org.apache.ignite.internal.processors.metric.impl.IntMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.LongAdderMetric;
 import 
org.apache.ignite.internal.processors.metric.impl.LongAdderWithDelegateMetric;
 import org.apache.ignite.internal.processors.metric.impl.LongGauge;
+import org.apache.ignite.internal.processors.metric.impl.MaxValueMetric;
 import org.apache.ignite.internal.processors.metric.impl.ObjectGauge;
 import org.apache.ignite.internal.processors.metric.impl.ObjectMetricImpl;
 import org.apache.ignite.internal.util.typedef.internal.LT;
@@ -47,7 +48,7 @@ import org.apache.ignite.spi.metric.Metric;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-import static 
org.apache.ignite.internal.processors.metric.impl.HitRateMetric.DFLT_SIZE;
+import static 
org.apache.ignite.internal.processors.metric.impl.AbstractIntervalMetric.DFLT_SIZE;
 import static 
org.apache.ignite.internal.processors.metric.impl.MetricUtils.customMetric;
 import static 
org.apache.ignite.internal.processors.metric.impl.MetricUtils.fromFullName;
 import static 
org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
@@ -57,10 +58,10 @@ import static 
org.apache.ignite.internal.processors.metric.impl.MetricUtils.metr
  */
 public class MetricRegistryImpl implements MetricRegistry {
     /** Registry name. */
-    private String regName;
+    private final String regName;
 
     /** Logger. */
-    private IgniteLogger log;
+    private final IgniteLogger log;
 
     /** Registered metrics. */
     private final ConcurrentHashMap<String, Metric> metrics = new 
ConcurrentHashMap<>();
@@ -68,20 +69,30 @@ public class MetricRegistryImpl implements MetricRegistry {
     /** HitRate config provider. */
     private final Function<String, Long> hitRateCfgProvider;
 
+    /** MaxValue metric config provider. */
+    private final Function<String, Long> maxValCfgProvider;
+
     /** Histogram config provider. */
     private final Function<String, long[]> histogramCfgProvider;
 
     /**
      * @param regName Registry name.
      * @param hitRateCfgProvider HitRate config provider.
+     * @param maxValCfgProvider MaxVal config provider.
      * @param histogramCfgProvider Histogram config provider.
      * @param log Logger.
      */
-    public MetricRegistryImpl(String regName, Function<String, Long> 
hitRateCfgProvider,
-        Function<String, long[]> histogramCfgProvider, IgniteLogger log) {
+    public MetricRegistryImpl(
+        String regName,
+        Function<String, Long> hitRateCfgProvider,
+        Function<String, Long> maxValCfgProvider,
+        Function<String, long[]> histogramCfgProvider,
+        IgniteLogger log
+    ) {
         this.regName = regName;
         this.log = log;
         this.hitRateCfgProvider = hitRateCfgProvider;
+        this.maxValCfgProvider = maxValCfgProvider;
         this.histogramCfgProvider = histogramCfgProvider;
     }
 
@@ -234,6 +245,21 @@ public class MetricRegistryImpl implements MetricRegistry {
         return addMetric(name, new HitRateMetric(metricName(regName, name), 
desc, rateTimeInterval, size));
     }
 
+    /**
+     * Creates and register metric for max value during certain time interval.
+     *
+     * It will accumulates approximate max value statistics.
+     * Calculates max value in last timeInterval milliseconds.
+     *
+     * @param timeInterval Time interval.
+     * @param size Array size for underlying calculations (buckets count).
+     * @return {@link MaxValueMetric}
+     * @see MaxValueMetric
+     */
+    public MaxValueMetric maxValueMetric(String name, @Nullable String desc, 
long timeInterval, int size) {
+        return addMetric(name, new MaxValueMetric(metricName(regName, name), 
desc, timeInterval, size));
+    }
+
     /**
      * Creates and register named gauge.
      * Returned instance are thread safe.
@@ -305,6 +331,15 @@ public class MetricRegistryImpl implements MetricRegistry {
             if (cfgRateTimeInterval != null)
                 ((HitRateMetric)metric).reset(cfgRateTimeInterval, DFLT_SIZE);
         }
+        else if (metric instanceof MaxValueMetric) {
+            if (maxValCfgProvider == null)
+                return;
+
+            Long cfgTimeInterval = maxValCfgProvider.apply(metric.name());
+
+            if (cfgTimeInterval != null)
+                ((MaxValueMetric)metric).reset(cfgTimeInterval, DFLT_SIZE);
+        }
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/AbstractIntervalMetric.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/AbstractIntervalMetric.java
new file mode 100644
index 00000000000..ea7a58fd766
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/AbstractIntervalMetric.java
@@ -0,0 +1,243 @@
+/*
+* 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.processors.metric.impl;
+
+import java.util.concurrent.atomic.AtomicLongArray;
+import org.apache.ignite.internal.processors.metric.AbstractMetric;
+import org.apache.ignite.internal.util.typedef.internal.A;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.metric.LongMetric;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Accumulates approximate values statistics.
+ * Calculates accumulated value in last {@code timeInterval} milliseconds.
+ * Algorithm is based on circular array of {@code size} values, each is 
responsible for last corresponding time
+ * subinterval of {@code timeInterval}/{@code size} milliseconds. Resulting 
value is accumulated for all subintervals.
+ *
+ * Implementation is nonblocking and protected from values loss.
+ * Maximum relative error is 1/{@code size}.
+ * Maximum value per interval is {@code 2^55 - 1}.
+ */
+public abstract class AbstractIntervalMetric extends AbstractMetric implements 
LongMetric {
+    /** Default values array size (number of buckets). */
+    public static final int DFLT_SIZE = 10;
+
+    /** Metric instance. */
+    protected volatile AbstractIntervalMetricImpl cntr;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     * @param timeInterval Time interval in milliseconds.
+     * @param size Values array size.
+     */
+    protected AbstractIntervalMetric(String name, @Nullable String desc, long 
timeInterval, int size) {
+        super(name, desc);
+
+        reset(timeInterval, size);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void reset() {
+        AbstractIntervalMetricImpl cntr0 = cntr;
+
+        reset(cntr0.timeInterval, cntr0.size);
+    }
+
+    /**
+     * Resets metric with the new parametes.
+     *
+     * @param timeInterval New rate time interval.
+     */
+    public void reset(long timeInterval) {
+        reset(timeInterval, DFLT_SIZE);
+    }
+
+    /**
+     * Resets metric with the new parameters.
+     *
+     * @param timeInterval New time interval.
+     * @param size New values array size.
+     */
+    public void reset(long timeInterval, int size) {
+        cntr = createImpl(timeInterval, size);
+    }
+
+    /** */
+    protected abstract AbstractIntervalMetricImpl createImpl(long 
timeInterval, int size);
+
+    /** {@inheritDoc} */
+    @Override public long value() {
+        return cntr.value();
+    }
+
+    /** @return Time interval in milliseconds. */
+    public long timeInterval() {
+        return cntr.timeInterval;
+    }
+
+    /**
+     * Actual metric.
+     */
+    protected abstract static class AbstractIntervalMetricImpl {
+        /** Bits that store actual hit count. */
+        private static final int TAG_OFFSET = 56;
+
+        /** Tag mask. */
+        protected static final long TAG_MASK = -1L << TAG_OFFSET;
+
+        /** Useful part mask. */
+        protected static final long NO_TAG_MASK = ~TAG_MASK;
+
+        /** Time interval when values are counted to calculate accumulated 
value, in milliseconds. */
+        private final long timeInterval;
+
+        /** Counters array size. */
+        private final int size;
+
+        /** Last value times. */
+        private final AtomicLongArray lastValTimes;
+
+        /** Tagged values. */
+        protected final AtomicLongArray taggedVals;
+
+        /**
+         * @param timeInterval Time interval.
+         * @param size Number of buckets.
+         */
+        protected AbstractIntervalMetricImpl(long timeInterval, int size) {
+            A.ensure(timeInterval > 0, "timeInterval should be positive");
+
+            A.ensure(size > 1, "Minimum value for size is 2");
+
+            this.timeInterval = timeInterval;
+
+            this.size = size;
+
+            taggedVals = new AtomicLongArray(size);
+
+            lastValTimes = new AtomicLongArray(size);
+        }
+
+        /**
+         * Adds val to the metric.
+         *
+         * @param val Value.
+         */
+        public void update(long val) {
+            long curTs = U.currentTimeMillis();
+
+            int curPos = position(curTs);
+
+            clearIfObsolete(curTs, curPos);
+
+            lastValTimes.set(curPos, curTs);
+
+            // Order is important. Value won't be cleared by concurrent 
#clearIfObsolete.
+            accumulateBucket(curPos, val);
+        }
+
+        /**
+         * @return Accumulated value in last {@link #timeInterval} 
milliseconds.
+         */
+        public long value() {
+            long curTs = U.currentTimeMillis();
+
+            long res = 0;
+
+            for (int i = 0; i < size; i++) {
+                clearIfObsolete(curTs, i);
+
+                res = accumulate(res, untag(taggedVals.get(i)));
+            }
+
+            return res;
+        }
+
+        /**
+         * Folds value into a result.
+         *
+         * @return Accumulated value.
+         */
+        protected abstract long accumulate(long res, long val);
+
+        /**
+         * Folds value into a bucket.
+         */
+        protected abstract void accumulateBucket(int bucket, long val);
+
+        /**
+         * @param curTs Current timestamp.
+         * @param i Index.
+         */
+        private void clearIfObsolete(long curTs, int i) {
+            long cur = taggedVals.get(i);
+
+            byte curTag = getTag(cur);
+
+            long lastTs = lastValTimes.get(i);
+
+            if (isObsolete(curTs, lastTs)) {
+                if (taggedVals.compareAndSet(i, cur, 
taggedLongZero(++curTag))) // ABA problem prevention.
+                    lastValTimes.set(i, curTs);
+                // If CAS failed, counter is reset by another thread.
+            }
+        }
+
+        /**
+         * @param curTs Current timestamp.
+         * @param lastValTime Last value timestamp.
+         * @return True, is last value time was too long ago.
+         */
+        private boolean isObsolete(long curTs, long lastValTime) {
+            return curTs - lastValTime > timeInterval * (size - 1) / size;
+        }
+
+        /**
+         * @param time Timestamp.
+         * @return Index of bucket for given timestamp.
+         */
+        private int position(long time) {
+            return (int)((time % timeInterval * size) / timeInterval);
+        }
+
+        /**
+         * @param tag Tag byte.
+         * @return 0L with given tag byte.
+         */
+        private static long taggedLongZero(byte tag) {
+            return ((long)tag << TAG_OFFSET);
+        }
+
+        /**
+         * @param l Tagged long.
+         * @return Long without tag byte.
+         */
+        private static long untag(long l) {
+            return l & NO_TAG_MASK;
+        }
+
+        /**
+         * @param taggedLong Tagged long.
+         * @return Tag byte.
+         */
+        private static byte getTag(long taggedLong) {
+            return (byte)(taggedLong >> TAG_OFFSET);
+        }
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java
index 5ac88a94c96..d4da705c4fd 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/HitRateMetric.java
@@ -16,30 +16,15 @@
 */
 package org.apache.ignite.internal.processors.metric.impl;
 
-import java.util.concurrent.atomic.AtomicLongArray;
-import org.apache.ignite.internal.processors.metric.AbstractMetric;
-import org.apache.ignite.internal.util.typedef.internal.A;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.spi.metric.LongMetric;
 import org.jetbrains.annotations.Nullable;
 
 /**
  * Accumulates approximate hit rate statistics.
  * Calculates number of hits in last {@code rateTimeInterval} milliseconds.
- * Algorithm is based on circular array of {@code size} hit counters, each is 
responsible for last corresponding time
- * interval of {@code rateTimeInterval}/{@code size} milliseconds. Resulting 
number of hits is sum of all counters.
  *
- * <p>Implementation is nonblocking and protected from hits loss.
- * Maximum relative error is 1/{@code size}.
- * 2^55 - 1 hits per interval can be accumulated without numeric overflow.
+ * @see AbstractIntervalMetric for implementation details.
  */
-public class HitRateMetric extends AbstractMetric implements LongMetric {
-    /** Default counters array size. */
-    public static final int DFLT_SIZE = 10;
-
-    /** Metric instance. */
-    private volatile HitRateMetricImpl cntr;
-
+public class HitRateMetric extends AbstractIntervalMetric {
     /**
      * @param name Name.
      * @param desc Description.
@@ -47,35 +32,12 @@ public class HitRateMetric extends AbstractMetric 
implements LongMetric {
      * @param size Counters array size.
      */
     public HitRateMetric(String name, @Nullable String desc, long 
rateTimeInterval, int size) {
-        super(name, desc);
-
-        cntr = new HitRateMetricImpl(rateTimeInterval, size);
+        super(name, desc, rateTimeInterval, size);
     }
 
     /** {@inheritDoc} */
-    @Override public void reset() {
-        HitRateMetricImpl cntr0 = cntr;
-
-        cntr = new HitRateMetricImpl(cntr0.rateTimeInterval, cntr0.size);
-    }
-
-    /**
-     * Resets metric with the new parametes.
-     *
-     * @param rateTimeInterval New rate time interval.
-     */
-    public void reset(long rateTimeInterval) {
-        reset(rateTimeInterval, DFLT_SIZE);
-    }
-
-    /**
-     * Resets metric with the new parameters.
-     *
-     * @param rateTimeInterval New rate time interval.
-     * @param size New counters array size.
-     */
-    public void reset(long rateTimeInterval, int size) {
-        cntr = new HitRateMetricImpl(rateTimeInterval, size);
+    @Override protected AbstractIntervalMetricImpl createImpl(long 
timeInterval, int size) {
+        return new HitRateMetricImpl(timeInterval, size);
     }
 
     /**
@@ -84,7 +46,7 @@ public class HitRateMetric extends AbstractMetric implements 
LongMetric {
      * @param x Value to be added.
      */
     public void add(long x) {
-        cntr.add(x);
+        cntr.update(x);
     }
 
     /** Adds 1 to the metric. */
@@ -92,150 +54,26 @@ public class HitRateMetric extends AbstractMetric 
implements LongMetric {
         add(1);
     }
 
-    /** {@inheritDoc} */
-    @Override public long value() {
-        return cntr.value();
-    }
-
-    /** @return Rate time interval in milliseconds. */
-    public long rateTimeInterval() {
-        return cntr.rateTimeInterval;
-    }
-
     /**
      * Actual metric.
-     *
-     * Separated class required to
      */
-    private static class HitRateMetricImpl {
-        /** Bits that store actual hit count. */
-        private static final int TAG_OFFSET = 56;
-
-        /** Useful part mask. */
-        private static final long NO_TAG_MASK = ~(-1L << TAG_OFFSET);
-
-        /** Time interval when hits are counted to calculate rate, in 
milliseconds. */
-        private final long rateTimeInterval;
-
-        /** Counters array size. */
-        private final int size;
-
-        /** Tagged counters. */
-        private final AtomicLongArray taggedCounters;
-
-        /** Last hit times. */
-        private final AtomicLongArray lastHitTimes;
-
-        /**
-         * @param rateTimeInterval Rate time interval.
-         * @param size Number of counters.
-         */
-        public HitRateMetricImpl(long rateTimeInterval, int size) {
-            A.ensure(rateTimeInterval > 0, "rateTimeInterval should be 
positive");
-
-            A.ensure(size > 1, "Minimum value for size is 2");
-
-            this.rateTimeInterval = rateTimeInterval;
-
-            this.size = size;
-
-            taggedCounters = new AtomicLongArray(size);
-
-            lastHitTimes = new AtomicLongArray(size);
-        }
-
+    private static class HitRateMetricImpl extends AbstractIntervalMetricImpl {
         /**
-         * Adds hits to the metric.
-         *
-         * @param hits Number of hits.
+         * @param timeInterval Time interval.
+         * @param size Buckets count.
          */
-        public void add(long hits) {
-            long curTs = U.currentTimeMillis();
-
-            int curPos = position(curTs);
-
-            clearIfObsolete(curTs, curPos);
-
-            lastHitTimes.set(curPos, curTs);
-
-            // Order is important. Hit won't be cleared by concurrent 
#clearIfObsolete.
-            taggedCounters.addAndGet(curPos, hits);
-        }
-
-        /**
-         * @return Total number of hits in last {@link #rateTimeInterval} 
milliseconds.
-         */
-        public long value() {
-            long curTs = U.currentTimeMillis();
-
-            long sum = 0;
-
-            for (int i = 0; i < size; i++) {
-                clearIfObsolete(curTs, i);
-
-                sum += untag(taggedCounters.get(i));
-            }
-
-            return sum;
+        public HitRateMetricImpl(long timeInterval, int size) {
+            super(timeInterval, size);
         }
 
-        /**
-         * @param curTs Current timestamp.
-         * @param i Index.
-         */
-        private void clearIfObsolete(long curTs, int i) {
-            long cur = taggedCounters.get(i);
-
-            byte curTag = getTag(cur);
-
-            long lastTs = lastHitTimes.get(i);
-
-            if (isObsolete(curTs, lastTs)) {
-                if (taggedCounters.compareAndSet(i, cur, 
taggedLongZero(++curTag))) // ABA problem prevention.
-                    lastHitTimes.set(i, curTs);
-                // If CAS failed, counter is reset by another thread.
-            }
+        /** {@inheritDoc} */
+        @Override protected long accumulate(long res, long val) {
+            return res + val;
         }
 
-        /**
-         * @param curTs Current timestamp.
-         * @param lastHitTime Last hit timestamp.
-         * @return True, is last hit time was too long ago.
-         */
-        private boolean isObsolete(long curTs, long lastHitTime) {
-            return curTs - lastHitTime > rateTimeInterval * (size - 1) / size;
-        }
-
-        /**
-         * @param time Timestamp.
-         * @return Index of counter for given timestamp.
-         */
-        private int position(long time) {
-            return (int)((time % rateTimeInterval * size) / rateTimeInterval);
-        }
-
-        /**
-         * @param tag Tag byte.
-         * @return 0L with given tag byte.
-         */
-        private static long taggedLongZero(byte tag) {
-            return ((long)tag << TAG_OFFSET);
-        }
-
-        /**
-         * @param l Tagged long.
-         * @return Long without tag byte.
-         */
-        private static long untag(long l) {
-            return l & NO_TAG_MASK;
-        }
-
-        /**
-         * @param taggedLong Tagged long.
-         * @return Tag byte.
-         */
-        private static byte getTag(long taggedLong) {
-            return (byte)(taggedLong >> TAG_OFFSET);
+        /** {@inheritDoc} */
+        @Override protected void accumulateBucket(int bucket, long val) {
+            taggedVals.addAndGet(bucket, val);
         }
     }
 }
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/MaxValueMetric.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/MaxValueMetric.java
new file mode 100644
index 00000000000..0ad4e31d5fe
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/metric/impl/MaxValueMetric.java
@@ -0,0 +1,85 @@
+/*
+* 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.processors.metric.impl;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Accumulates approximate maximum value statistics.
+ * Calculates maximum value in last {@code timeInterval} milliseconds.
+ *
+ * @see AbstractIntervalMetric for implementation details.
+ */
+public class MaxValueMetric extends AbstractIntervalMetric {
+    /**
+     * @param name Name.
+     * @param desc Description.
+     * @param timeInterval Time interval in milliseconds.
+     * @param size Values array size (number of buckets).
+     */
+    public MaxValueMetric(String name, @Nullable String desc, long 
timeInterval, int size) {
+        super(name, desc, timeInterval, size);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected AbstractIntervalMetricImpl createImpl(long 
timeInterval, int size) {
+        return new MaxValueMetricImpl(timeInterval, size);
+    }
+
+    /**
+     * Accumulate x value to the metric.
+     *
+     * @param x Value to be accumulate.
+     */
+    public void update(long x) {
+        cntr.update(x);
+    }
+
+    /**
+     * Actual metric.
+     */
+    private static class MaxValueMetricImpl extends AbstractIntervalMetricImpl 
{
+        /**
+         * @param timeInterval Time interval.
+         * @param size Buckets count.
+         */
+        public MaxValueMetricImpl(long timeInterval, int size) {
+            super(timeInterval, size);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected long accumulate(long res, long val) {
+            return Math.max(res, val);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void accumulateBucket(int bucket, long val) {
+            long oldVal;
+            long val0;
+
+            do {
+                oldVal = taggedVals.get(bucket);
+
+                val0 = (val & NO_TAG_MASK) | (oldVal & TAG_MASK);
+
+                if (val0 <= oldVal)
+                    return;
+            }
+            while (!taggedVals.compareAndSet(bucket, oldVal, val0));
+        }
+    }
+}
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
index ed21575c046..d01649ea29a 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/metric/JmxExporterSpiTest.java
@@ -607,7 +607,7 @@ public class JmxExporterSpiTest extends 
AbstractExporterSpiTest {
     /** */
     @Test
     public void testHistogramSearchByName() throws Exception {
-        MetricRegistryImpl mreg = new MetricRegistryImpl("test", name -> null, 
name -> null, null);
+        MetricRegistryImpl mreg = new MetricRegistryImpl("test", name -> null, 
name -> null, name -> null, null);
 
         createTestHistogram(mreg);
 
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsConfigurationTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsConfigurationTest.java
index 7892f43f41f..9041b4bb8b2 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsConfigurationTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsConfigurationTest.java
@@ -17,15 +17,19 @@
 
 package org.apache.ignite.internal.metric;
 
+import java.util.Collections;
+import javax.management.DynamicMBean;
 import org.apache.ignite.cluster.ClusterState;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.management.api.CommandMBean;
 import org.apache.ignite.internal.processors.metric.MetricRegistryImpl;
 import org.apache.ignite.internal.processors.metric.MetricsMxBeanImpl;
 import org.apache.ignite.internal.processors.metric.impl.HistogramMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
+import org.apache.ignite.internal.processors.metric.impl.MaxValueMetric;
 import 
org.apache.ignite.internal.processors.metric.impl.PeriodicHistogramMetricImpl;
 import org.apache.ignite.mxbean.MetricsMxBean;
 import org.apache.ignite.spi.metric.HistogramMetric;
@@ -37,6 +41,7 @@ import static 
org.apache.ignite.internal.binary.BinaryUtils.arrayEq;
 import static 
org.apache.ignite.internal.processors.cache.transactions.TransactionMetricsAdapter.METRIC_SYSTEM_TIME_HISTOGRAM;
 import static 
org.apache.ignite.internal.processors.metric.GridMetricManager.HISTOGRAM_CFG_PREFIX;
 import static 
org.apache.ignite.internal.processors.metric.GridMetricManager.HITRATE_CFG_PREFIX;
+import static 
org.apache.ignite.internal.processors.metric.GridMetricManager.MAXVAL_CFG_PREFIX;
 import static 
org.apache.ignite.internal.processors.metric.GridMetricManager.TX_METRICS;
 import static 
org.apache.ignite.internal.processors.metric.impl.MetricUtils.cacheMetricsRegistryName;
 import static 
org.apache.ignite.internal.processors.metric.impl.MetricUtils.metricName;
@@ -54,6 +59,9 @@ public class MetricsConfigurationTest extends 
GridCommonAbstractTest {
     /** Test hitrate metric name. */
     public static final String HITRATE_NAME = "hitrate";
 
+    /** Test maxval metric name. */
+    public static final String MAXVAL_NAME = "maxval";
+
     /** Test histogram metric name. */
     public static final String HISTOGRAM_NAME = "histogram";
 
@@ -115,7 +123,57 @@ public class MetricsConfigurationTest extends 
GridCommonAbstractTest {
             HitRateMetric allocationRate = 
g.context().metric().registry(metricName("io.dataregion.default"))
                 .findMetric("AllocationRate");
 
-            assertEquals(5000, allocationRate.rateTimeInterval());
+            assertEquals(5000, allocationRate.timeInterval());
+        }
+    }
+
+    /** Tests configuration of {@link MaxValueMetric}. */
+    @Test
+    public void testMaxValueMetricConfiguration() throws Exception {
+        try (IgniteEx g = startGrid(0)) {
+            MetricRegistryImpl mreg = g.context().metric().registry(TEST_REG);
+            MaxValueMetric metric = mreg.maxValueMetric(MAXVAL_NAME, "test", 
10000, 5);
+            String metricName = metricName(TEST_REG, MAXVAL_NAME);
+
+            // Empty name.
+            assertThrowsWithCause(
+                () -> configureMaxValueMetric(g, null, 1),
+                NullPointerException.class);
+
+            // Wrong interval value.
+            assertThrowsWithCause(
+                () -> configureMaxValueMetric(g, metricName, 0),
+                IllegalArgumentException.class);
+
+            assertThrowsWithCause(
+                () -> configureMaxValueMetric(g, metricName, -1),
+                IllegalArgumentException.class);
+
+            configureMaxValueMetric(g, metricName, 5000);
+
+            assertEquals(5000, metric.timeInterval());
+        }
+    }
+
+    /** */
+    private void configureMaxValueMetric(IgniteEx ignite, String metricName, 
int interval) {
+        DynamicMBean mbean = getMxBean(
+            ignite.context().igniteInstanceName(),
+            "management",
+            Collections.singletonList("Metric"),
+            "ConfigureMaxValue",
+            DynamicMBean.class
+        );
+
+        try {
+            mbean.invoke(
+                CommandMBean.INVOKE,
+                new Object[] {metricName, Integer.toString(interval), null},
+                new String[] {String.class.getName(), String.class.getName(), 
String.class.getName()}
+            );
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 
@@ -242,28 +300,38 @@ public class MetricsConfigurationTest extends 
GridCommonAbstractTest {
             MetricRegistryImpl mreg = g0.context().metric().registry(TEST_REG);
 
             mreg.hitRateMetric(HITRATE_NAME, "test", 10000, 5);
+            mreg.maxValueMetric(MAXVAL_NAME, "test", 10000, 5);
             mreg.histogram(HISTOGRAM_NAME, new long[] {250, 500}, "test");
 
             metricsBean(g0).configureHistogramMetric(metricName(TEST_REG, 
HISTOGRAM_NAME), BOUNDS);
             metricsBean(g0).configureHitRateMetric(metricName(TEST_REG, 
HITRATE_NAME), 1000);
+            configureMaxValueMetric(g0, metricName(TEST_REG, MAXVAL_NAME), 
1000);
         }, (g0, g1) -> {
             MetricRegistryImpl mreg = g0.context().metric().registry(TEST_REG);
 
             HitRateMetric hitRate = mreg.hitRateMetric(HITRATE_NAME, "test", 
10000, 5);
+            MaxValueMetric maxVal = mreg.maxValueMetric(MAXVAL_NAME, "test", 
10000, 5);
             HistogramMetricImpl histogram = mreg.histogram(HISTOGRAM_NAME, new 
long[] {250, 500}, "test");
 
-            assertEquals(1000, hitRate.rateTimeInterval());
+            assertEquals(1000, hitRate.timeInterval());
+            assertEquals(1000, maxVal.timeInterval());
             assertArrayEquals(BOUNDS, histogram.bounds());
 
             assertEquals((Long)1000L,
                 
g0.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, 
TEST_REG, HITRATE_NAME)));
 
+            assertEquals((Long)1000L,
+                
g0.context().distributedMetastorage().read(metricName(MAXVAL_CFG_PREFIX, 
TEST_REG, MAXVAL_NAME)));
+
             assertArrayEquals(BOUNDS,
                 
g0.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, 
TEST_REG, HISTOGRAM_NAME)));
 
             assertEquals((Long)1000L,
                 
g1.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, 
TEST_REG, HITRATE_NAME)));
 
+            assertEquals((Long)1000L,
+                
g1.context().distributedMetastorage().read(metricName(MAXVAL_CFG_PREFIX, 
TEST_REG, MAXVAL_NAME)));
+
             assertArrayEquals(BOUNDS,
                 
g1.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, 
TEST_REG, HISTOGRAM_NAME)));
 
@@ -271,11 +339,15 @@ public class MetricsConfigurationTest extends 
GridCommonAbstractTest {
 
             assertNull(
                 
g0.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, 
TEST_REG, HITRATE_NAME)));
+            assertNull(
+                
g0.context().distributedMetastorage().read(metricName(MAXVAL_CFG_PREFIX, 
TEST_REG, MAXVAL_NAME)));
             assertNull(
                 
g0.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, 
TEST_REG, HISTOGRAM_NAME)));
 
             assertNull(
                 
g1.context().distributedMetastorage().read(metricName(HITRATE_CFG_PREFIX, 
TEST_REG, HITRATE_NAME)));
+            assertNull(
+                
g1.context().distributedMetastorage().read(metricName(MAXVAL_CFG_PREFIX, 
TEST_REG, MAXVAL_NAME)));
             assertNull(
                 
g1.context().distributedMetastorage().read(metricName(HISTOGRAM_CFG_PREFIX, 
TEST_REG, HISTOGRAM_NAME)));
         });
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java
index bff7e75fe8d..17d6ac88efd 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/metric/MetricsSelfTest.java
@@ -23,6 +23,8 @@ import java.util.List;
 import java.util.Set;
 import java.util.Spliterators;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.StreamSupport;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteInternalFuture;
@@ -34,6 +36,8 @@ import 
org.apache.ignite.internal.processors.metric.impl.HistogramMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
 import org.apache.ignite.internal.processors.metric.impl.IntMetricImpl;
 import org.apache.ignite.internal.processors.metric.impl.LongAdderMetric;
+import org.apache.ignite.internal.processors.metric.impl.MaxValueMetric;
+import org.apache.ignite.internal.util.GridTestClockTimer;
 import org.apache.ignite.internal.util.typedef.T2;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.IgniteSpiException;
@@ -70,7 +74,7 @@ public class MetricsSelfTest extends GridCommonAbstractTest {
     /** */
     @Before
     public void setUp() throws Exception {
-        mreg = new MetricRegistryImpl("group", name -> null, name -> null, 
null);
+        mreg = new MetricRegistryImpl("group", name -> null, name -> null, 
name -> null, null);
     }
 
     /** */
@@ -293,7 +297,7 @@ public class MetricsSelfTest extends GridCommonAbstractTest 
{
     /** */
     @Test
     public void testGetMetrics() throws Exception {
-        MetricRegistryImpl mreg = new MetricRegistryImpl("group", name -> 
null, name -> null, null);
+        MetricRegistryImpl mreg = new MetricRegistryImpl("group", name -> 
null, name -> null, name -> null, null);
 
         mreg.longMetric("test1", "");
         mreg.longMetric("test2", "");
@@ -314,7 +318,7 @@ public class MetricsSelfTest extends GridCommonAbstractTest 
{
     /** */
     @Test
     public void testRemove() throws Exception {
-        MetricRegistryImpl mreg = new MetricRegistryImpl("group", name -> 
null, name -> null, null);
+        MetricRegistryImpl mreg = new MetricRegistryImpl("group", name -> 
null, name -> null, name -> null, null);
 
         AtomicLongMetric cntr = mreg.longMetric("my.name", null);
         AtomicLongMetric cntr2 = mreg.longMetric("my.name.x", null);
@@ -354,13 +358,89 @@ public class MetricsSelfTest extends 
GridCommonAbstractTest {
 
         assertEquals(0, metric.value());
 
-        assertEquals(rateTimeInterval, metric.rateTimeInterval());
+        assertEquals(rateTimeInterval, metric.timeInterval());
 
         metric.reset(rateTimeInterval * 2, 10);
 
-        assertEquals(rateTimeInterval * 2, metric.rateTimeInterval());
+        assertEquals(rateTimeInterval * 2, metric.timeInterval());
     }
 
+    /** */
+    @Test
+    public void testMaxValueMetric() {
+        try {
+            long timeInterval = 500;
+
+            MaxValueMetric metric = mreg.maxValueMetric("testMaxVal", null, 
timeInterval, 5);
+
+            assertEquals(0, metric.value());
+
+            long startTs = U.currentTimeMillis();
+
+            updateMaxValMetric(metric, startTs + 40, 5);
+            updateMaxValMetric(metric, startTs + 50, 10);
+            updateMaxValMetric(metric, startTs + 60, 5);
+
+            assertEquals(10, metric.value());
+
+            updateMaxValMetric(metric, startTs + 140, 10);
+            updateMaxValMetric(metric, startTs + 150, 20);
+            updateMaxValMetric(metric, startTs + 160, 10);
+
+            assertEquals(20, metric.value());
+
+            updateMaxValMetric(metric, startTs + 240, 10);
+            updateMaxValMetric(metric, startTs + 250, 15);
+            updateMaxValMetric(metric, startTs + 260, 10);
+
+            assertEquals(20, metric.value());
+
+            GridTestClockTimer.timeSupplier(() -> startTs + 650);
+            GridTestClockTimer.update();
+
+            assertEquals(15, metric.value());
+
+            GridTestClockTimer.timeSupplier(() -> startTs + 750);
+            GridTestClockTimer.update();
+
+            assertEquals(0, metric.value());
+        }
+        finally {
+            
GridTestClockTimer.timeSupplier(GridTestClockTimer.DFLT_TIME_SUPPLIER);
+        }
+    }
+
+    /** */
+    @Test
+    public void testMaxValueMetricMultithreaded() throws Exception {
+        long timeInterval = 500;
+        long maxVal = 100;
+        AtomicInteger threadId = new AtomicInteger();
+
+        MaxValueMetric metric = mreg.maxValueMetric("testMaxVal", null, 
timeInterval, 500);
+
+        assertEquals(0, metric.value());
+
+        long startTs = U.currentTimeMillis();
+
+        GridTestUtils.runMultiThreaded(() -> {
+            int id = threadId.getAndIncrement();
+
+            while (U.currentTimeMillis() < startTs + timeInterval)
+                metric.update(id == 0 ? maxVal : 
ThreadLocalRandom.current().nextLong(maxVal));
+        }, 10, "metric-update");
+
+        assertEquals(maxVal, metric.value());
+    }
+
+    /** */
+    private void updateMaxValMetric(MaxValueMetric metric, long ts, long val) {
+        GridTestClockTimer.timeSupplier(() -> ts);
+        GridTestClockTimer.update();
+        metric.update(val);
+    }
+
+
     /** */
     @Test
     public void testHistogramNames() throws Exception {
diff --git 
a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/GridTcpCommunicationSpiConfigSelfTest.java
 
b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/GridTcpCommunicationSpiConfigSelfTest.java
index b1b26e3924b..d4d63ac7e66 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/GridTcpCommunicationSpiConfigSelfTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/GridTcpCommunicationSpiConfigSelfTest.java
@@ -194,7 +194,7 @@ public class GridTcpCommunicationSpiConfigSelfTest extends 
GridSpiAbstractConfig
                 // No-op.
             }
 
-            return new MetricRegistryImpl(name, null, null, new NullLogger());
+            return new MetricRegistryImpl(name, null, null, null, new 
NullLogger());
         });
 
         TcpCommunicationSpi receiverSpi = initializeSpi(receiverCtx, 
receiverNode, listeningLog, true);
diff --git 
a/modules/core/src/test/java/org/apache/ignite/testframework/GridSpiTestContext.java
 
b/modules/core/src/test/java/org/apache/ignite/testframework/GridSpiTestContext.java
index 56852ea365a..7f5d876e21f 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/testframework/GridSpiTestContext.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/testframework/GridSpiTestContext.java
@@ -620,7 +620,7 @@ public class GridSpiTestContext implements IgniteSpiContext 
{
         if (metricsRegistryProducer != null)
             return metricsRegistryProducer.apply(name);
 
-        return new MetricRegistryImpl(name, null, null, new NullLogger());
+        return new MetricRegistryImpl(name, null, null, null, new 
NullLogger());
     }
 
     /** {@inheritDoc} */
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 939c60c0609..662227a1938 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
@@ -382,6 +382,14 @@ If the file name isn't specified the output file name is: 
'<typeId>.bin':
       newRateTimeInterval  - Rate time interval of hitrate.
       --node-id node_id    - Node id.
 
+  Configure MaxValue metric:
+    control.(sh|bat) --metric --configure-max-value name newTimeInterval 
[--node-id node_id]
+
+    Parameters:
+      name               - Name of the metric.
+      newTimeInterval    - Time interval of the metric.
+      --node-id node_id  - Node id.
+
   Print information about potentially corrupted caches on local node:
     control.(sh|bat) --persistence
 
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 2dd6bb73815..4ca9e56b1ea 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
@@ -382,6 +382,14 @@ If the file name isn't specified the output file name is: 
'<typeId>.bin':
       newRateTimeInterval  - Rate time interval of hitrate.
       --node-id node_id    - Node id.
 
+  Configure MaxValue metric:
+    control.(sh|bat) --metric --configure-max-value name newTimeInterval 
[--node-id node_id]
+
+    Parameters:
+      name               - Name of the metric.
+      newTimeInterval    - Time interval of the metric.
+      --node-id node_id  - Node id.
+
   Print information about potentially corrupted caches on local node:
     control.(sh|bat) --persistence
 

Reply via email to