Merge branch 'main' into ignite-17354
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/FilteringIterator.java b/modules/core/src/main/java/org/apache/ignite/internal/util/FilteringIterator.java
similarity index 97%
rename from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/FilteringIterator.java
rename to modules/core/src/main/java/org/apache/ignite/internal/util/FilteringIterator.java
index 1ec865d..0baa62e 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/FilteringIterator.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/FilteringIterator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.util;
+package org.apache.ignite.internal.util;
import java.util.Iterator;
import java.util.NoSuchElementException;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TransformingIterator.java b/modules/core/src/main/java/org/apache/ignite/internal/util/TransformingIterator.java
similarity index 97%
rename from modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TransformingIterator.java
rename to modules/core/src/main/java/org/apache/ignite/internal/util/TransformingIterator.java
index d8efccb..827e62b 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TransformingIterator.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/TransformingIterator.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.sql.engine.util;
+package org.apache.ignite.internal.util;
import java.util.Iterator;
import java.util.function.Function;
diff --git a/modules/metrics/pom.xml b/modules/metrics/pom.xml
new file mode 100644
index 0000000..720e99c
--- /dev/null
+++ b/modules/metrics/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-parent</artifactId>
+ <version>1</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>ignite-metrics</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-core</artifactId>
+ </dependency>
+
+ <!-- 3rd party dependencies -->
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ </dependency>
+
+ <!-- Test dependencies -->
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetric.java
new file mode 100644
index 0000000..bd8623d
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetric.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Abstract metric.
+ */
+public abstract class AbstractMetric implements Metric {
+ /** Metric name. It is local for a particular {@link MetricSet}. */
+ private final String name;
+
+ /** Metric description. */
+ @Nullable
+ private final String desc;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ */
+ public AbstractMetric(String name, @Nullable String desc) {
+ assert name != null;
+ assert !name.isEmpty();
+
+ this.name = name;
+ this.desc = desc;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable
+ public String description() {
+ return desc;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ AbstractMetric metric = (AbstractMetric) o;
+
+ if (!name.equals(metric.name)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return name.hashCode();
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetricSource.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetricSource.java
new file mode 100644
index 0000000..a5081c1
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetricSource.java
@@ -0,0 +1,137 @@
+/*
+ * 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.metrics;
+
+import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+/**
+ * Base class for all metric sources.
+ *
+ * @param <T> Holder type.
+ */
+public abstract class AbstractMetricSource<T extends AbstractMetricSource.Holder<T>> implements MetricSource {
+ /** Holder field updater. */
+ @SuppressWarnings("rawtypes")
+ private static final AtomicReferenceFieldUpdater<AbstractMetricSource, Holder> HOLDER_FIELD_UPD =
+ newUpdater(AbstractMetricSource.class, AbstractMetricSource.Holder.class, "holder");
+
+ /** Metric source name. */
+ private final String name;
+
+ /** Metric instances holder. */
+ private volatile T holder;
+
+ /**
+ * Base constructor for all metric source implementations.
+ *
+ * @param name Metric source name.
+ */
+ protected AbstractMetricSource(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns metric source name.
+ *
+ * @return Metric source name.
+ */
+ @Override public final String name() {
+ return name;
+ }
+
+ /**
+ * Checks whether metrics is enabled (switched on) or not (switched off) for metric source.
+ *
+ * @return {@code True} if metrics are enabled, otherwise - {@code false}.
+ */
+ @Override public final boolean enabled() {
+ return holder != null;
+ }
+
+ /**
+ * Returns metric instances' holder. Use this on order to avoid metric lookup from map-like data structures.
+ * Returned value is {@code null} if metrics are disabled.
+ *
+ * @return Metrics holder instance if metrics are enabled, otherwise - {@code null}.
+ */
+ public final T holder() {
+ return holder;
+ }
+
+ /**
+ * Method is responsible for creation of appropriate holder instance in underlying implementations.
+ *
+ * @return New instance of metrics holder that must implements {@link Holder} interface.
+ */
+ protected abstract T createHolder();
+
+ /** {@inheritDoc} */
+ @Override public final MetricSet enable() {
+ MetricSetBuilder bldr = new MetricSetBuilder(name);
+
+ T hldr = createHolder();
+
+ init(bldr, hldr);
+
+ MetricSet metricSet = bldr.build();
+
+ return HOLDER_FIELD_UPD.compareAndSet(this, null, hldr) ? metricSet : null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public final void disable() {
+ T holder0 = holder;
+
+ if (HOLDER_FIELD_UPD.compareAndSet(this, holder0, null)) {
+ cleanup(holder0);
+ }
+ }
+
+ /**
+ * Method is responsible for:
+ * <ol>
+ * <li>Creation of {@link MetricSet} instance using provided {@link MetricSetBuilder}.</li>
+ * <li>Creation of metric instances in given holder.</li>
+ * <li>Other initialization if needed.</li>
+ * </ol>.
+ *
+ * @param bldr Metric registry builder.
+ * @param holder Metric instances' holder.
+ */
+ protected abstract void init(MetricSetBuilder bldr, T holder);
+
+ /**
+ * Method is responsible for cleanup and release of all resources initialized or created during {@link #init} method
+ * execution. Note that {@link MetricSet} and {@link Holder} instances will be released automatically.
+ *
+ * @param holder Metric instances holder.
+ */
+ protected void cleanup(T holder) {
+ // No-op.
+ }
+
+ /**
+ * Marker interface for metric instances holder.
+ *
+ * @param <T> Holder type subclass.
+ */
+ protected interface Holder<T extends Holder<T>> {
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
new file mode 100644
index 0000000..5931351
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
@@ -0,0 +1,72 @@
+/*
+ * 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.metrics;
+
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+
+/**
+ * Double metric based on atomic updater of double value.
+ */
+public class AtomicDoubleMetric extends AbstractMetric implements DoubleMetric {
+ /** Field updater. */
+ private static final AtomicLongFieldUpdater<AtomicDoubleMetric> updater =
+ AtomicLongFieldUpdater.newUpdater(AtomicDoubleMetric.class, "val");
+
+ /** Value. */
+ private volatile long val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ */
+ public AtomicDoubleMetric(String name, String desc) {
+ super(name, desc);
+ }
+
+ /**
+ * Adds given value to the metric value.
+ *
+ * @param v Value to be added.
+ */
+ public void add(double v) {
+ for (;;) {
+ long exp = val;
+ long upd = Double.doubleToLongBits(Double.longBitsToDouble(exp) + v);
+ if (updater.compareAndSet(this, exp, upd)) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Sets value.
+ *
+ * @param val Value.
+ */
+ public void value(double val) {
+ this.val = Double.doubleToLongBits(val);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public double value() {
+ return Double.longBitsToDouble(val);
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicIntMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicIntMetric.java
new file mode 100644
index 0000000..7e38221
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicIntMetric.java
@@ -0,0 +1,76 @@
+/*
+ * 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.metrics;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Integer metric is implemented as a volatile {@code int} value.
+ */
+public class AtomicIntMetric extends AbstractMetric implements IntMetric {
+ /** Field updater. */
+ private static final AtomicIntegerFieldUpdater<AtomicIntMetric> updater =
+ AtomicIntegerFieldUpdater.newUpdater(AtomicIntMetric.class, "val");
+
+ /** Value. */
+ private volatile int val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ */
+ public AtomicIntMetric(String name, @Nullable String desc) {
+ super(name, desc);
+ }
+
+ /**
+ * Adds x to the metric.
+ *
+ * @param x Value to be added.
+ */
+ public void add(int x) {
+ updater.addAndGet(this, x);
+ }
+
+ /** Increment the metric. */
+ public void increment() {
+ updater.incrementAndGet(this);
+ }
+
+ /** Decrement the metric. */
+ public void decrement() {
+ updater.decrementAndGet(this);
+ }
+
+ /**
+ * Sets value.
+ *
+ * @param val Value.
+ */
+ public void value(int val) {
+ this.val = val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int value() {
+ return val;
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicLongMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicLongMetric.java
new file mode 100644
index 0000000..5d13eb7
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicLongMetric.java
@@ -0,0 +1,76 @@
+/*
+ * 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.metrics;
+
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Long metric implementation.
+ */
+public class AtomicLongMetric extends AbstractMetric implements LongMetric {
+ /** Field updater. */
+ static final AtomicLongFieldUpdater<AtomicLongMetric> updater =
+ AtomicLongFieldUpdater.newUpdater(AtomicLongMetric.class, "val");
+
+ /** Field value. */
+ private volatile long val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ */
+ public AtomicLongMetric(String name, @Nullable String desc) {
+ super(name, desc);
+ }
+
+ /**
+ * Adds x to the metric.
+ *
+ * @param x Value to be added.
+ */
+ public void add(long x) {
+ updater.getAndAdd(this, x);
+ }
+
+ /** Adds 1 to the metric. */
+ public void increment() {
+ updater.incrementAndGet(this);
+ }
+
+ /** Adds -1 to the metric. */
+ public void decrement() {
+ updater.decrementAndGet(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long value() {
+ return val;
+ }
+
+ /**
+ * Sets value.
+ *
+ * @param val Value.
+ */
+ public void value(long val) {
+ this.val = val;
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/CompositeAwareIterator.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/CompositeAwareIterator.java
new file mode 100644
index 0000000..6c84c98
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/CompositeAwareIterator.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.metrics;
+
+import java.util.Iterator;
+
+/**
+ * The iterator that considers composite metrics as a group of scalar ones, see {@link CompositeMetric#asScalarMetrics()},
+ * and enumerates composite metrics in order to enumerate every scalar metric.
+ */
+public class CompositeAwareIterator implements Iterator<Metric> {
+ /** Delegate iterator of metrics, some of them may be composite. */
+ private final Iterator<Metric> delegate;
+
+ /** Iterator for single composite metric, iterating over the group of scalar ones. */
+ private Iterator<Metric> compositeMetricIterator = null;
+
+ /**
+ * The constructor.
+ *
+ * @param delegate Collection of metrics that can contain composite metrics.
+ */
+ public CompositeAwareIterator(Iterator<Metric> delegate) {
+ this.delegate = delegate;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean hasNext() {
+ if (compositeMetricIterator == null) {
+ return delegate.hasNext();
+ } else if (compositeMetricIterator.hasNext()) {
+ return true;
+ } else {
+ compositeMetricIterator = null;
+
+ return delegate.hasNext();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Metric next() {
+ if (compositeMetricIterator == null) {
+ return nextCompositeAware();
+ } else if (compositeMetricIterator.hasNext()) {
+ return compositeMetricIterator.next();
+ } else {
+ compositeMetricIterator = null;
+
+ return nextCompositeAware();
+ }
+ }
+
+ /**
+ * Next method that is aware of composite metrics.
+ *
+ * @return Next value.
+ */
+ private Metric nextCompositeAware() {
+ Metric nextValue = delegate.next();
+
+ if (nextValue instanceof CompositeMetric) {
+ compositeMetricIterator = ((CompositeMetric) nextValue).asScalarMetrics().iterator();
+
+ return compositeMetricIterator.next();
+ } else {
+ return nextValue;
+ }
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/CompositeMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/CompositeMetric.java
new file mode 100644
index 0000000..91458c0
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/CompositeMetric.java
@@ -0,0 +1,32 @@
+/*
+ * 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.metrics;
+
+import java.util.List;
+
+/**
+ * A composite metric is a group of closely related values. The values themselves are numeric.
+ */
+public interface CompositeMetric extends Metric {
+ /**
+ * Get a group of values composing this metric as a list of scalar metrics.
+ *
+ * @return List of scalar metrics.
+ */
+ List<Metric> asScalarMetrics();
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DistributionMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DistributionMetric.java
new file mode 100644
index 0000000..0cd87c5
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DistributionMetric.java
@@ -0,0 +1,194 @@
+/*
+ * 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.metrics;
+
+import static java.util.Collections.unmodifiableList;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLongArray;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Distribution metric calculates counts of measurements that gets into each bounds interval.
+ * Note, that {@link #value()} will return array length of {@code bounds.length + 1}.
+ * Last element contains count of measurements bigger than most right value of bounds.
+ */
+public class DistributionMetric extends AbstractMetric implements CompositeMetric {
+ /** Updater that atomically updates {@link #scalarMetrics} field. */
+ private static final AtomicReferenceFieldUpdater<DistributionMetric, List> scalarMetricsUpdater =
+ AtomicReferenceFieldUpdater.newUpdater(DistributionMetric.class, List.class, "scalarMetrics");
+
+ /** Distribution metric first interval low bound. */
+ public static final long FIRST_LOW_BOUND = 0;
+
+ /** Distribution metric first interval low bound, string representation. */
+ public static final String FIRST_LOW_BOUND_STRING = "0";
+
+ /** Distribution metric last interval high bound. */
+ public static final String INF = "inf";
+
+ /** Distribution range divider. */
+ public static final char RANGE_DIVIDER = '_';
+
+ /** Distribution bucket divider. */
+ public static final String BUCKET_DIVIDER = ", ";
+
+ /** Distribution metric name and value divider. */
+ public static final char METRIC_DIVIDER = ':';
+
+ /** Count of measurement for each bound. */
+ private final AtomicLongArray measurements;
+
+ /** Bounds of measurements. */
+ private final long[] bounds;
+
+ /** List of scalar metrics. */
+ private volatile List<Metric> scalarMetrics = null;
+
+ /**
+ * The constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ * @param bounds Bounds of the buckets. The array must be sorted and its elements must be unique. The 0-th bound must be
+ * greater or equal to {@code 0}.
+ */
+ public DistributionMetric(String name, @Nullable String desc, long[] bounds) {
+ super(name, desc);
+
+ assert bounds != null && bounds.length > 0;
+ assert bounds[0] >= FIRST_LOW_BOUND;
+ assert isSortedAndUnique(bounds);
+
+ this.bounds = bounds;
+ this.measurements = new AtomicLongArray(bounds.length + 1);
+ }
+
+ /**
+ * Check whether given array is sorted and its elements are unique.
+ *
+ * @param arr Array to check.
+ * @return {@code True} if array sorted and its elements are unique, {@code false} otherwise.
+ */
+ private static boolean isSortedAndUnique(long[] arr) {
+ if (arr.length < 2) {
+ return true;
+ }
+
+ for (int i = 1; i < arr.length; i++) {
+ if (arr[i - 1] >= arr[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Adds a value to the interval which the value belongs to.
+ *
+ * @param x Value.
+ */
+ public void add(long x) {
+ assert x >= 0;
+
+ //Expect arrays of few elements.
+ for (int i = 0; i < bounds.length; i++) {
+ if (x <= bounds[i]) {
+ measurements.incrementAndGet(i);
+
+ return;
+ }
+ }
+
+ measurements.incrementAndGet(bounds.length);
+ }
+
+ /** {@inheritDoc} */
+ public long[] value() {
+ long[] res = new long[measurements.length()];
+
+ for (int i = 0; i < measurements.length(); i++) {
+ res[i] = measurements.get(i);
+ }
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getValueAsString() {
+ StringBuilder sb = new StringBuilder("[");
+
+ List<Metric> scalarMetrics = asScalarMetrics();
+
+ for (int i = 0; i < scalarMetrics.size(); i++) {
+ LongMetric m = (LongMetric) scalarMetrics.get(i);
+
+ sb.append(m.name())
+ .append(METRIC_DIVIDER)
+ .append(m.value());
+
+ if (i < scalarMetrics.size() - 1) {
+ sb.append(BUCKET_DIVIDER);
+ }
+ }
+
+ sb.append(']');
+
+ return sb.toString();
+ }
+
+ /**
+ * Bounds of the buckets of distribution.
+ *
+ * @return Bounds of the buckets of distribution.
+ */
+ public long[] bounds() {
+ return bounds;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List<Metric> asScalarMetrics() {
+ if (scalarMetrics == null) {
+ List<Metric> metrics = new ArrayList<>();
+
+ String from = FIRST_LOW_BOUND_STRING;
+
+ for (int i = 0; i < measurements.length(); i++) {
+ String to = i == measurements.length() - 1 ? INF : String.valueOf(bounds[i]);
+
+ String name = name() + RANGE_DIVIDER + from + RANGE_DIVIDER + to;
+
+ final int index = i;
+ LongGauge gauge = new LongGauge(name, "Single distribution bucket", () -> measurements.get(index));
+
+ metrics.add(gauge);
+
+ from = to;
+ }
+
+ scalarMetricsUpdater.compareAndSet(this, null, unmodifiableList(metrics));
+ }
+
+ return scalarMetrics;
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleAdderMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleAdderMetric.java
new file mode 100644
index 0000000..d67105d
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleAdderMetric.java
@@ -0,0 +1,66 @@
+/*
+ * 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.metrics;
+
+import java.util.concurrent.atomic.DoubleAdder;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Double metric based on {@link DoubleAdder}.
+ */
+public class DoubleAdderMetric extends AbstractMetric implements DoubleMetric {
+ /** Value. */
+ private volatile DoubleAdder val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ */
+ public DoubleAdderMetric(String name, @Nullable String desc) {
+ super(name, desc);
+
+ this.val = new DoubleAdder();
+ }
+
+ /**
+ * Adds x to the metric.
+ *
+ * @param x Value to be added.
+ */
+ public void add(double x) {
+ val.add(x);
+ }
+
+ /**
+ * Sets value.
+ *
+ * @param val Value.
+ */
+ public void value(double val) {
+ DoubleAdder adder = new DoubleAdder();
+ adder.add(val);
+ this.val = adder;
+ }
+
+ /** {@inheritDoc} */
+ @Override public double value() {
+ return val.sum();
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleGauge.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleGauge.java
new file mode 100644
index 0000000..bd555e6
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleGauge.java
@@ -0,0 +1,47 @@
+/*
+ * 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.metrics;
+
+import java.util.function.DoubleSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Double metric based on {@link DoubleSupplier}.
+ */
+public class DoubleGauge extends AbstractMetric implements DoubleMetric {
+ /** Value supplier. */
+ private final DoubleSupplier val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ * @param val Supplier.
+ */
+ public DoubleGauge(String name, @Nullable String desc, DoubleSupplier val) {
+ super(name, desc);
+
+ this.val = val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public double value() {
+ return val.getAsDouble();
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleMetric.java
new file mode 100644
index 0000000..1c7e550
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleMetric.java
@@ -0,0 +1,36 @@
+/*
+ * 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.metrics;
+
+/**
+ * Basic interface for double metric.
+ */
+public interface DoubleMetric extends Metric {
+ /**
+ * Value of the metric.
+ *
+ * @return Value of the metric.
+ */
+ double value();
+
+ /** {@inheritDoc} */
+ @Override
+ default String getValueAsString() {
+ return Double.toString(value());
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/HitRateMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/HitRateMetric.java
new file mode 100644
index 0000000..93a5604
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/HitRateMetric.java
@@ -0,0 +1,195 @@
+/*
+ * 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.metrics;
+
+import java.util.concurrent.atomic.AtomicLongArray;
+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.
+ */
+public class HitRateMetric extends AbstractMetric implements LongMetric {
+ /** Default counters array size. */
+ public static final int DFLT_SIZE = 10;
+
+ /** 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;
+
+ /**
+ * The constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ * @param rateTimeInterval Rate time interval in milliseconds.
+ */
+ public HitRateMetric(String name, @Nullable String desc, long rateTimeInterval) {
+ this(name, desc, rateTimeInterval, DFLT_SIZE);
+ }
+
+ /**
+ * The constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ * @param rateTimeInterval Rate time interval in milliseconds.
+ * @param size Counters array size.
+ */
+ public HitRateMetric(String name, @Nullable String desc, long rateTimeInterval, int size) {
+ super(name, desc);
+
+ assert rateTimeInterval > 0 : "rateTimeInterval should be positive";
+ assert size > 1 : "Minimum value for size is 2";
+
+ this.rateTimeInterval = rateTimeInterval;
+ this.size = size;
+ taggedCounters = new AtomicLongArray(size);
+ lastHitTimes = new AtomicLongArray(size);
+ }
+
+ /**
+ * Adds the given count for hits to the metric.
+ *
+ * @param hits Count of hits.
+ */
+ public void add(long hits) {
+ long curTs = System.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);
+ }
+
+ /** Adds 1 to the metric. */
+ public void increment() {
+ add(1);
+ }
+
+ /** {@inheritDoc} */
+ @Override public long value() {
+ long curTs = System.currentTimeMillis();
+
+ long sum = 0;
+
+ for (int i = 0; i < size; i++) {
+ clearIfObsolete(curTs, i);
+
+ sum += untag(taggedCounters.get(i));
+ }
+
+ return sum;
+ }
+
+ /**
+ * Clear specific counter if obsolete.
+ *
+ * @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.
+ }
+ }
+
+ /**
+ * Whether the last hit time was too long ago.
+ *
+ * @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;
+ }
+
+ /**
+ * Index of counter for given timestamp.
+ *
+ * @param time Timestamp.
+ * @return Index of counter for given timestamp.
+ */
+ private int position(long time) {
+ return (int) ((time % rateTimeInterval * size) / rateTimeInterval);
+ }
+
+ /**
+ * Create a zero value with tag byte.
+ *
+ * @param tag Tag byte.
+ * @return 0L with given tag byte.
+ */
+ private static long taggedLongZero(byte tag) {
+ return ((long) tag << TAG_OFFSET);
+ }
+
+ /**
+ * Long without tag byte.
+ *
+ * @param l Tagged long.
+ * @return Long without tag byte.
+ */
+ private static long untag(long l) {
+ return l & NO_TAG_MASK;
+ }
+
+ /**
+ * Tag byte.
+ *
+ * @param taggedLong Tagged long.
+ * @return Tag byte.
+ */
+ private static byte getTag(long taggedLong) {
+ return (byte) (taggedLong >> TAG_OFFSET);
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntGauge.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntGauge.java
new file mode 100644
index 0000000..99e8c7b
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntGauge.java
@@ -0,0 +1,47 @@
+/*
+ * 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.metrics;
+
+import java.util.function.IntSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementation based on primitive supplier.
+ */
+public class IntGauge extends AbstractMetric implements IntMetric {
+ /** Value supplier. */
+ private final IntSupplier val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ * @param val Supplier.
+ */
+ public IntGauge(String name, @Nullable String desc, IntSupplier val) {
+ super(name, desc);
+
+ this.val = val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int value() {
+ return val.getAsInt();
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntMetric.java
new file mode 100644
index 0000000..3c69686
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntMetric.java
@@ -0,0 +1,36 @@
+/*
+ * 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.metrics;
+
+/**
+ * Interface for the metrics that holds int primitive.
+ */
+public interface IntMetric extends Metric {
+ /**
+ * Value of the metric.
+ *
+ * @return Value of the metric.
+ */
+ int value();
+
+ /** {@inheritDoc} */
+ @Override
+ default String getValueAsString() {
+ return Integer.toString(value());
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongAdderMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongAdderMetric.java
new file mode 100644
index 0000000..3f0f180
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongAdderMetric.java
@@ -0,0 +1,81 @@
+/*
+ * 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.metrics;
+
+import java.util.concurrent.atomic.LongAdder;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Long metric based on {@link LongAdder}.
+ */
+public class LongAdderMetric extends AbstractMetric implements LongMetric {
+ /** Value. */
+ private volatile LongAdder val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ */
+ public LongAdderMetric(String name, @Nullable String desc) {
+ super(name, desc);
+
+ this.val = new LongAdder();
+ }
+
+ /**
+ * Adds x to the metric.
+ *
+ * @param x Value to be added.
+ */
+ public void add(long x) {
+ val.add(x);
+ }
+
+ /**
+ * Increment the metric.
+ */
+ public void increment() {
+ val.increment();
+ }
+
+ /**
+ * Decrement the metric.
+ */
+ public void decrement() {
+ val.decrement();
+ }
+
+ /**
+ * Sets value.
+ *
+ * @param val Value.
+ */
+ public void value(long val) {
+ LongAdder adder = new LongAdder();
+ adder.add(val);
+ this.val = adder;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long value() {
+ return val.sum();
+ }
+
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongGauge.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongGauge.java
new file mode 100644
index 0000000..299f471
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongGauge.java
@@ -0,0 +1,47 @@
+/*
+ * 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.metrics;
+
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementation based on primitive supplier.
+ */
+public class LongGauge extends AbstractMetric implements LongMetric {
+ /** Value supplier. */
+ private final LongSupplier val;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param desc Description.
+ * @param val Supplier.
+ */
+ public LongGauge(String name, @Nullable String desc, LongSupplier val) {
+ super(name, desc);
+
+ this.val = val;
+ }
+
+ /** {@inheritDoc} */
+ @Override public long value() {
+ return val.getAsLong();
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongMetric.java
new file mode 100644
index 0000000..bb2eeeb
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongMetric.java
@@ -0,0 +1,36 @@
+/*
+ * 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.metrics;
+
+/**
+ * Interface for the metrics that holds long primitive.
+ */
+public interface LongMetric extends Metric {
+ /**
+ * Value of the metric.
+ *
+ * @return Value of the metric.
+ */
+ long value();
+
+ /** {@inheritDoc} */
+ @Override
+ default String getValueAsString() {
+ return Long.toString(value());
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/Metric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/Metric.java
new file mode 100644
index 0000000..a3ad48d
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/Metric.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.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Basic metric interface.
+ */
+public interface Metric {
+ /**
+ * Name of the metric.
+ *
+ * @return Name of the metric.
+ */
+ String name();
+
+ /**
+ * Description of the metric.
+ *
+ * @return Description of the metric.
+ */
+ @Nullable
+ String description();
+
+ /**
+ * String representation of metric value.
+ *
+ * @return String representation of metric value.
+ */
+ @Nullable
+ String getValueAsString();
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
new file mode 100644
index 0000000..91cc669
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
@@ -0,0 +1,126 @@
+/*
+ * 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.metrics;
+
+import java.util.Map;
+import org.apache.ignite.internal.manager.IgniteComponent;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Metric manager.
+ */
+public class MetricManager implements IgniteComponent {
+ /**
+ * Metric registry.
+ */
+ private final MetricRegistry registry;
+
+ /**
+ * Constructor.
+ */
+ public MetricManager() {
+ this.registry = new MetricRegistry();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void start() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override public void stop() throws Exception {
+ // No-op.
+ }
+
+ /**
+ * Register metric source. See {@link MetricRegistry#registerSource(MetricSource)}.
+ *
+ * @param src Metric source.
+ */
+ public void registerSource(MetricSource src) {
+ registry.registerSource(src);
+ }
+
+ /**
+ * Unregister metric source. See {@link MetricRegistry#unregisterSource(MetricSource)}.
+ *
+ * @param src Metric source.
+ */
+ public void unregisterSource(MetricSource src) {
+ registry.unregisterSource(src);
+ }
+
+ /**
+ * Unregister metric source by name. See {@link MetricRegistry#unregisterSource(String)}.
+ *
+ * @param srcName Metric source name.
+ */
+ public void unregisterSource(String srcName) {
+ registry.unregisterSource(srcName);
+ }
+
+ /**
+ * Enable metric source. See {@link MetricRegistry#enable(MetricSource)}.
+ *
+ * @param src Metric source.
+ * @return Metric set, or {@code null} if already enabled.
+ */
+ public MetricSet enable(MetricSource src) {
+ return registry.enable(src);
+ }
+
+ /**
+ * Enable metric source by name. See {@link MetricRegistry#enable(String)}.
+ *
+ * @param srcName Source name.
+ * @return Metric set, or {@code null} if already enabled.
+ */
+ public MetricSet enable(final String srcName) {
+ return registry.enable(srcName);
+ }
+
+ /**
+ * Disable metric source. See {@link MetricRegistry#disable(MetricSource)}.
+ *
+ * @param src Metric source.
+ */
+ public void disable(MetricSource src) {
+ registry.disable(src);
+ }
+
+ /**
+ * Disable metric source by name. See {@link MetricRegistry#disable(String)}.
+ *
+ * @param srcName Source name.
+ */
+ public void disable(final String srcName) {
+ registry.disable(srcName);
+ }
+
+ /**
+ * Metrics snapshot. This is a snapshot of metric sets with corresponding version, the values of the metrics in the
+ * metric sets that are included into the snapshot, are changed dynamically.
+ *
+ * @return Metrics snapshot.
+ */
+ @NotNull
+ public IgniteBiTuple<Map<String, MetricSet>, Long> metricSnapshot() {
+ return registry.metricSnapshot();
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricRegistry.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricRegistry.java
new file mode 100644
index 0000000..d0b8e19
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricRegistry.java
@@ -0,0 +1,295 @@
+/*
+ * 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.metrics;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.unmodifiableMap;
+import static java.util.Objects.requireNonNull;
+import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Metric registry. Metrics source (see {@link MetricSource} must be registered in this metrics registry after initialization
+ * of corresponding component and must be unregistered in case of component is destroyed or stopped. Metrics registry also
+ * provides access to all enabled metrics through corresponding metrics sets. Metrics registry lifetime is equal to the node lifetime.
+ */
+public class MetricRegistry {
+ private static final AtomicReferenceFieldUpdater<MetricRegistry, IgniteBiTuple> metricSnapshotUpdater =
+ newUpdater(MetricRegistry.class, IgniteBiTuple.class, "metricSnapshot");
+
+ private final Lock lock = new ReentrantLock();
+
+ /** Registered metric sources. */
+ private final Map<String, MetricSource> sources = new HashMap<>();
+
+ /**
+ * Metrics snapshot. This is a snapshot of metric sets with corresponding version, the values of the metrics in the
+ * metric sets that are included into the snapshot, are changed dynamically.
+ */
+ private volatile IgniteBiTuple<Map<String, MetricSet>, Long> metricSnapshot = new IgniteBiTuple<>(emptyMap(), 0L);
+
+ /**
+ * Register metric source. It must be registered in this metrics registry after initialization of corresponding component
+ * and must be unregistered in case of component is destroyed or stopped, see {@link #unregisterSource(MetricSource)}.
+ * By registering, the metric source isn't enabled implicitly.
+ *
+ * @param src Metric source.
+ * @throws IllegalStateException If metric source with the given name already exists.
+ */
+ public void registerSource(MetricSource src) {
+ lock.lock();
+
+ try {
+ // Metric source shouldn't be enabled before because the second call of MetricSource#enable will return null.
+ assert !src.enabled() : "Metric source shouldn't be enabled before registration in registry.";
+
+ MetricSource old = sources.putIfAbsent(src.name(), src);
+
+ if (old != null) {
+ throw new IllegalStateException("Metrics source with given name already exists: " + src.name());
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Unregister metric source. It must be unregistered in case of corresponding component is destroyed or stopped.
+ * Metric source is also disabled while unregistered, see {@link #disable(String)}.
+ *
+ * @param src Metric source.
+ * @throws IllegalStateException If the given metric source isn't registered.
+ */
+ public void unregisterSource(MetricSource src) {
+ unregisterSource(src.name());
+ }
+
+ /**
+ * Unregister metric source. It must be unregistered in case of corresponding component is destroyed or stopped.
+ * Metric source is also disabled while unregistered, see {@link #disable(String)}.
+ *
+ * @param srcName Metric source name.
+ * @throws IllegalStateException If the metric source with the given name isn't registered.
+ */
+ public void unregisterSource(String srcName) {
+ lock.lock();
+
+ try {
+ disable(srcName);
+
+ sources.remove(srcName);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Enable metric set for the given metric source.
+ *
+ * @param src Metric source.
+ * @return Metric set, or {@code null} if the metric set is already enabled.
+ * @throws IllegalStateException If metric source isn't registered.
+ * @throws IllegalArgumentException If metric source isn't the same as registered.
+ */
+ public MetricSet enable(@NotNull MetricSource src) {
+ lock.lock();
+
+ try {
+ MetricSource registered = checkAndGetRegistered(src);
+
+ MetricSet metricSet = registered.enable();
+
+ if (metricSet != null) {
+ addMetricSet(src.name(), metricSet);
+ }
+
+ return metricSet;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Enable metric set for the given metric source.
+ *
+ * @param srcName Metric source name.
+ * @return Metric set, or {@code null} if the metric set is already enabled.
+ * @throws IllegalStateException If metric source with the given name doesn't exist.
+ */
+ public MetricSet enable(final String srcName) {
+ lock.lock();
+
+ try {
+ MetricSource src = sources.get(srcName);
+
+ if (src == null) {
+ throw new IllegalStateException("Metrics source with given name doesn't exist: " + srcName);
+ }
+
+ MetricSet metricSet = src.enable();
+
+ if (metricSet != null) {
+ addMetricSet(src.name(), metricSet);
+ }
+
+ return metricSet;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Disable metric set for the given metric source.
+ *
+ * @param src Metric source.
+ * @throws IllegalStateException If metric source isn't registered.
+ * @throws IllegalArgumentException If metric source isn't the same as registered.
+ */
+ public void disable(@NotNull MetricSource src) {
+ lock.lock();
+
+ try {
+ MetricSource registered = checkAndGetRegistered(src);
+
+ if (!registered.enabled()) {
+ return;
+ }
+
+ registered.disable();
+
+ removeMetricSet(registered.name());
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Disable metric set for the given metric source.
+ *
+ * @param srcName Metric source name.
+ * @throws IllegalStateException If metric source with given name doesn't exist.
+ */
+ public void disable(final String srcName) {
+ lock.lock();
+
+ try {
+ MetricSource src = sources.get(srcName);
+
+ if (src == null) {
+ throw new IllegalStateException("Metrics source with given name doesn't exists: " + srcName);
+ }
+
+ if (!src.enabled()) {
+ return;
+ }
+
+ src.disable();
+
+ removeMetricSet(srcName);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Check that the given metric source is registered. This method should be called under the {@link MetricRegistry#lock}.
+ *
+ * @param src Metric source.
+ * @return Registered metric source.
+ * @throws IllegalStateException If metric source isn't registered.
+ * @throws IllegalArgumentException If metric source isn't the same as registered.
+ */
+ @NotNull
+ private MetricSource checkAndGetRegistered(@NotNull MetricSource src) {
+ requireNonNull(src);
+
+ MetricSource registered = sources.get(src.name());
+
+ if (registered == null) {
+ throw new IllegalStateException("Metrics source isn't registered: " + src.name());
+ }
+
+ if (!src.equals(registered)) {
+ throw new IllegalArgumentException("Given metric source is not the same as registered by the same name: " + src.name());
+ }
+
+ return registered;
+ }
+
+ /**
+ * Add metric set to {@link MetricRegistry#metricSnapshot}. This creates new version of metric snapshot. This method should be
+ * called under the {@link MetricRegistry#lock}.
+ *
+ * @param srcName Metric source name.
+ * @param metricSet Metric set.
+ */
+ private void addMetricSet(String srcName, MetricSet metricSet) {
+ Map<String, MetricSet> metricSets = new TreeMap<>(metricSnapshot.get1());
+
+ metricSets.put(srcName, metricSet);
+
+ updateMetricSnapshot(metricSets);
+ }
+
+ /**
+ * Removes metric set from {@link MetricRegistry#metricSnapshot}. This creates new version of metric snapshot. This method should be
+ * called under the {@link MetricRegistry#lock}.
+ *
+ * @param srcName Metric source name.
+ */
+ private void removeMetricSet(String srcName) {
+ Map<String, MetricSet> metricSets = new TreeMap<>(metricSnapshot.get1());
+
+ metricSets.remove(srcName);
+
+ updateMetricSnapshot(metricSets);
+ }
+
+ /**
+ * Create a new version of {@link MetricRegistry#metricSnapshot}. This method should be called under the {@link MetricRegistry#lock}.
+ *
+ * @param metricSets New map of metric sets that should be saved to new version of metric snapshot.
+ */
+ private void updateMetricSnapshot(Map<String, MetricSet> metricSets) {
+ IgniteBiTuple<Map<String, MetricSet>, Long> old = metricSnapshot;
+
+ long newVersion = old.get2() + 1;
+
+ IgniteBiTuple<Map<String, MetricSet>, Long> newMetricSnapshot = new IgniteBiTuple<>(unmodifiableMap(metricSets), newVersion);
+
+ metricSnapshotUpdater.compareAndSet(this, old, newMetricSnapshot);
+ }
+
+ /**
+ * Metrics snapshot. This is a snapshot of metric sets with corresponding version, the values of the metrics in the
+ * metric sets that are included into the snapshot, are changed dynamically.
+ *
+ * @return Metrics snapshot.
+ */
+ public IgniteBiTuple<Map<String, MetricSet>, Long> metricSnapshot() {
+ return metricSnapshot;
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSet.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSet.java
new file mode 100644
index 0000000..92c7ad1
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSet.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.metrics;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+
+/**
+ * The Metric set that consists of set of metrics identified by a metric name.
+ * The metrics set is immutable.
+ */
+public class MetricSet implements Iterable<Metric> {
+ /** Metrics set name. */
+ private final String name;
+
+ /** Registered metrics. */
+ private final Map<String, Metric> metrics;
+
+ /**
+ * Creates an instance of a metrics set with given name and metrics.
+ *
+ * @param name Metrics set name.
+ * @param metrics Metrics.
+ */
+ public MetricSet(String name, Map<String, Metric> metrics) {
+ this.name = name;
+ this.metrics = Collections.unmodifiableMap(metrics);
+ }
+
+ /**
+ * Get metric by name.
+ *
+ * @param name Metric name.
+ * @return Metric.
+ */
+ @Nullable
+ @TestOnly
+ public <M extends Metric> M get(String name) {
+ return (M) metrics.get(name);
+ }
+
+ /** {@inheritDoc} */
+ public Iterator<Metric> iterator() {
+ return metrics.values().iterator();
+ }
+
+ /**
+ * Name of the metric set.
+ *
+ * @return Name of the metrics set.
+ */
+ public String name() {
+ return name;
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSetBuilder.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSetBuilder.java
new file mode 100644
index 0000000..ff3af2b
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSetBuilder.java
@@ -0,0 +1,219 @@
+/*
+ * 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.metrics;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Metric set builder.
+ */
+public class MetricSetBuilder {
+ /** Metrics set name. */
+ private final String name;
+
+ /** Registered metrics. */
+ private Map<String, Metric> metrics = new LinkedHashMap<>();
+
+ /**
+ * Creates a new instance of metrics set builder with given name.
+ *
+ * @param name Name of metrics set. Can't be null.
+ */
+ public MetricSetBuilder(String name) {
+ Objects.requireNonNull(name, "Metrics set name can't be null");
+ this.name = name;
+ }
+
+ /**
+ * Build a metric set.
+ *
+ * @return Metric set.
+ */
+ public MetricSet build() {
+ if (metrics == null) {
+ throw new IllegalStateException("Builder can't be used twice.");
+ }
+
+ MetricSet reg = new MetricSet(name, metrics);
+
+ metrics = null;
+
+ return reg;
+ }
+
+ /**
+ * Returns metrics set name.
+ *
+ * @return Metrics set name.
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Adds existing metric with the specified name.
+ *
+ * @param metric Metric.
+ * @throws IllegalStateException If metric with given name is already added.
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Metric> T register(T metric) {
+ T old = (T) metrics.putIfAbsent(metric.name(), metric);
+
+ if (old != null) {
+ throw new IllegalStateException("Metric with given name is already registered [name=" + name
+ + ", metric=" + metric + ']');
+ }
+
+ return metric;
+ }
+
+ /**
+ * Add an atomic integer metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @return Atomic integer metric.
+ */
+ public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+ return register(new AtomicIntMetric(name, description));
+ }
+
+ /**
+ * Add an integer gauge.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @param supplier Supplier of the value.
+ * @return Integer gauge.
+ */
+ public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+ return register(new IntGauge(name, description, supplier));
+ }
+
+ /**
+ * Add an atomic long metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @return Atomic long metric.
+ */
+ public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+ return register(new AtomicLongMetric(name, description));
+ }
+
+ /**
+ * Add a long adder metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @return Long adder metric.
+ */
+ public LongAdderMetric longAdder(String name, @Nullable String description) {
+ return register(new LongAdderMetric(name, description));
+ }
+
+ /**
+ * Add a long gauge.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @param supplier Supplier of the value.
+ * @return Long gauge.
+ */
+ public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+ return register(new LongGauge(name, description, supplier));
+ }
+
+ /**
+ * Add an atomic double metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @return Atomic double metric.
+ */
+ public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+ return register(new AtomicDoubleMetric(name, description));
+ }
+
+ /**
+ * Add a double adder metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @return Double adder metric.
+ */
+ public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+ return register(new DoubleAdderMetric(name, description));
+ }
+
+ /**
+ * Add a double gauge.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @param supplier Supplier of the value.
+ * @return Double gauge.
+ */
+ public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+ return register(new DoubleGauge(name, description, supplier));
+ }
+
+ /**
+ * Add a hit rate metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @param rateTimeInterval Rate time interval in milliseconds.
+ * @return Hit rate metric.
+ */
+ public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+ return register(new HitRateMetric(name, description, rateTimeInterval));
+ }
+
+ /**
+ * Add a hit rate metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @param rateTimeInterval Rate time interval in milliseconds.
+ * @param size Counters array size.
+ * @return Hit rate metric.
+ */
+ public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+ return register(new HitRateMetric(name, description, rateTimeInterval, size));
+ }
+
+ /**
+ * Add a distribution metric.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @param bounds Bounds of the buckets
+ * @return Distribution metrics.
+ */
+ public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+ return register(new DistributionMetric(name, description, bounds));
+ }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSource.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSource.java
new file mode 100644
index 0000000..4c4c1c2
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricSource.java
@@ -0,0 +1,56 @@
+/*
+ * 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.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Interface for all metrics sources. Metric source provides access to related metrics. Metrics source always corresponds
+ * to some component or entity of the system (e.g. node is a metrics source, storage engine is a metric source). Metrics source
+ * has a unique name and type (e.g. class name). The metrics source exposes an interface for modification of metrics values.
+ * Instead of looking up metrics by a name and modifying metrics value directly, developers must use methods defined in the
+ * metrics source.
+ */
+public interface MetricSource {
+ /**
+ * Returns metric source name.
+ *
+ * @return Metric source name.
+ */
+ String name();
+
+ /**
+ * Enables metrics for metric source. Creates and returns {@link MetricSet} built during enabling. Nothing happens if
+ * the metrics are already enabled for this source.
+ *
+ * @return Newly created {@link MetricSet} instance or {@code null} if metrics are already enabled.
+ */
+ @Nullable MetricSet enable();
+
+ /**
+ * Disables metrics for metric source. Nothing happens if the metrics are already disabled for this source.
+ */
+ void disable();
+
+ /**
+ * Checks whether metrics is enabled (switched on) or not (switched off) for metric source.
+ *
+ * @return {@code True} if metrics are enabled, otherwise - {@code false}.
+ */
+ boolean enabled();
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractDoubleMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractDoubleMetricTest.java
new file mode 100644
index 0000000..7cab455
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractDoubleMetricTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.metrics;
+
+/**
+ * Abstract test for double metrics.
+ */
+public abstract class AbstractDoubleMetricTest extends AbstractScalarMetricsTest<DoubleMetric, Double> {
+ /** Test value. */
+ private static final double TEST_VALUE = 100.0;
+
+ /** Expected value. */
+ private double expected = 0;
+
+ /** {@inheritDoc} */
+ @Override protected Double value(DoubleMetric metric) {
+ return metric.value();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Double expected() {
+ return expected;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void increment(DoubleMetric metric) {
+ expected++;
+ increment0(metric);
+ }
+
+ /**
+ * Increment the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void increment0(DoubleMetric metric);
+
+ /** {@inheritDoc} */
+ @Override protected void decrement(DoubleMetric metric) {
+ expected--;
+ decrement0(metric);
+ }
+
+ /**
+ * Decrement the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void decrement0(DoubleMetric metric);
+
+ /** {@inheritDoc} */
+ @Override protected void add(DoubleMetric metric) {
+ expected += TEST_VALUE;
+ add0(metric, TEST_VALUE);
+ }
+
+ /**
+ * Add the value to the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void add0(DoubleMetric metric, double value);
+
+ /** {@inheritDoc} */
+ @Override protected void setValue(DoubleMetric metric) {
+ expected = TEST_VALUE;
+ setValue0(metric, TEST_VALUE);
+ }
+
+ /**
+ * Assign the value to the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void setValue0(DoubleMetric metric, double value);
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractIntMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractIntMetricTest.java
new file mode 100644
index 0000000..74b16e1
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractIntMetricTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.metrics;
+
+/**
+ * Abstract test for integer metrics.
+ */
+public abstract class AbstractIntMetricTest extends AbstractScalarMetricsTest<IntMetric, Integer> {
+ /** Test value. */
+ private static final int TEST_VALUE = 100;
+
+ /** Expected value. */
+ private int expected = 0;
+
+ /** {@inheritDoc} */
+ @Override protected Integer value(IntMetric metric) {
+ return metric.value();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Integer expected() {
+ return expected;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void increment(IntMetric metric) {
+ expected++;
+ increment0(metric);
+ }
+
+ /**
+ * Increment the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void increment0(IntMetric metric);
+
+ /** {@inheritDoc} */
+ @Override protected void decrement(IntMetric metric) {
+ expected--;
+ decrement0(metric);
+ }
+
+ /**
+ * Decrement the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void decrement0(IntMetric metric);
+
+ /** {@inheritDoc} */
+ @Override protected void add(IntMetric metric) {
+ expected += TEST_VALUE;
+ add0(metric, TEST_VALUE);
+ }
+
+ /**
+ * Add the value to the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void add0(IntMetric metric, int value);
+
+ /** {@inheritDoc} */
+ @Override protected void setValue(IntMetric metric) {
+ expected = TEST_VALUE;
+ setValue0(metric, TEST_VALUE);
+ }
+
+ /**
+ * Assign the value to the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void setValue0(IntMetric metric, int value);
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractLongMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractLongMetricTest.java
new file mode 100644
index 0000000..4305d86
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractLongMetricTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.metrics;
+
+/**
+ * Abstract test for long metrics.
+ */
+public abstract class AbstractLongMetricTest extends AbstractScalarMetricsTest<LongMetric, Long> {
+ /** Test value. */
+ private static final long TEST_VALUE = 100;
+
+ /** Expected value. */
+ private long expected = 0;
+
+ /** {@inheritDoc} */
+ @Override protected Long value(LongMetric metric) {
+ return metric.value();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Long expected() {
+ return expected;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void increment(LongMetric metric) {
+ expected++;
+ increment0(metric);
+ }
+
+ /**
+ * Increment the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void increment0(LongMetric metric);
+
+ /** {@inheritDoc} */
+ @Override protected void decrement(LongMetric metric) {
+ expected--;
+ decrement0(metric);
+ }
+
+ /**
+ * Decrement the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void decrement0(LongMetric metric);
+
+ /** {@inheritDoc} */
+ @Override protected void add(LongMetric metric) {
+ expected += TEST_VALUE;
+ add0(metric, TEST_VALUE);
+ }
+
+ /**
+ * Add the value to the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void add0(LongMetric metric, long value);
+
+ /** {@inheritDoc} */
+ @Override protected void setValue(LongMetric metric) {
+ expected = TEST_VALUE;
+ setValue0(metric, TEST_VALUE);
+ }
+
+ /**
+ * Assign the value to the metric.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void setValue0(LongMetric metric, long value);
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractScalarMetricsTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractScalarMetricsTest.java
new file mode 100644
index 0000000..42b62ce
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AbstractScalarMetricsTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.metrics;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Abstract test for scalar metrics.
+ *
+ * @param <M> Metric type.
+ * @param <V> Type of metric value.
+ */
+public abstract class AbstractScalarMetricsTest<M extends Metric, V> {
+ @Test
+ public void testCreateMetric() {
+ String name = "testName";
+ String description = "testDescription";
+
+ M m = createMetric(name, description);
+
+ assertEquals(name, m.name());
+ assertEquals(description, m.description());
+
+ assertEquals(expected(), value(m));
+ assertEquals(expected().toString(), value(m).toString());
+ }
+
+ @Test
+ public void testIncrement() {
+ M m = createMetric();
+
+ increment(m);
+ assertEquals(expected(), value(m));
+ assertEquals(expected().toString(), value(m).toString());
+ }
+
+ @Test
+ public void testDecrement() {
+ M m = createMetric();
+
+ decrement(m);
+ assertEquals(expected(), value(m));
+ assertEquals(expected().toString(), value(m).toString());
+ }
+
+ @Test
+ public void testAdd() {
+ M m = createMetric();
+
+ add(m);
+ assertEquals(expected(), value(m));
+ assertEquals(expected().toString(), value(m).toString());
+ }
+
+ @Test
+ public void testSetValue() {
+ M m = createMetric();
+
+ setValue(m);
+ assertEquals(expected(), value(m));
+ assertEquals(expected().toString(), value(m).toString());
+ }
+
+ /**
+ * Create a metric.
+ *
+ * @return Metric.
+ */
+ private M createMetric() {
+ String name = "testName";
+ String description = "testDescription";
+
+ return createMetric(name, description);
+ }
+
+ /**
+ * Create a metric with given name and description.
+ *
+ * @param name Name.
+ * @param description Description.
+ * @return Metric.
+ */
+ protected abstract M createMetric(String name, String description);
+
+ /**
+ * Return a value of the metric.
+ *
+ * @param metric Metric.
+ * @return Value of the metric.
+ */
+ protected abstract V value(M metric);
+
+ /**
+ * Expected value of the metric in current moment of time.
+ *
+ * @return Expected value.
+ */
+ protected abstract V expected();
+
+ /**
+ * Increments the metric, if applicable.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void increment(M metric);
+
+ /**
+ * Decrements the metric, if applicable.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void decrement(M metric);
+
+ /**
+ * Add some test value to the metric, if applicable.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void add(M metric);
+
+ /**
+ * Assign some test value to the metric, if applicable.
+ *
+ * @param metric Metric.
+ */
+ protected abstract void setValue(M metric);
+}
+
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicDoubleMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicDoubleMetricTest.java
new file mode 100644
index 0000000..5e4df04
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicDoubleMetricTest.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.metrics;
+
+/**
+ * Test for {@link AtomicDoubleMetric}.
+ */
+public class AtomicDoubleMetricTest extends AbstractDoubleMetricTest {
+ /** {@inheritDoc} */
+ @Override protected void increment0(DoubleMetric metric) {
+ ((AtomicDoubleMetric) metric).add(1);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(DoubleMetric metric) {
+ ((AtomicDoubleMetric) metric).add(-1);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(DoubleMetric metric, double value) {
+ ((AtomicDoubleMetric) metric).add(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(DoubleMetric metric, double value) {
+ ((AtomicDoubleMetric) metric).value(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected DoubleMetric createMetric(String name, String description) {
+ return new AtomicDoubleMetric(name, description);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicIntMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicIntMetricTest.java
new file mode 100644
index 0000000..e87f0aa
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicIntMetricTest.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.metrics;
+
+/**
+ * Test for {@link AtomicIntMetric}.
+ */
+public class AtomicIntMetricTest extends AbstractIntMetricTest {
+ /** {@inheritDoc} */
+ @Override protected AtomicIntMetric createMetric(String name, String description) {
+ return new AtomicIntMetric(name, description);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void increment0(IntMetric metric) {
+ ((AtomicIntMetric) metric).increment();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(IntMetric metric) {
+ ((AtomicIntMetric) metric).decrement();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(IntMetric metric, int value) {
+ ((AtomicIntMetric) metric).add(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(IntMetric metric, int value) {
+ ((AtomicIntMetric) metric).value(value);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicLongMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicLongMetricTest.java
new file mode 100644
index 0000000..9d7e672
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/AtomicLongMetricTest.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.metrics;
+
+/**
+ * Test for {@link AtomicLongMetric}.
+ */
+public class AtomicLongMetricTest extends AbstractLongMetricTest {
+ /** {@inheritDoc} */
+ @Override protected void increment0(LongMetric metric) {
+ ((AtomicLongMetric) metric).increment();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(LongMetric metric) {
+ ((AtomicLongMetric) metric).decrement();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(LongMetric metric, long value) {
+ ((AtomicLongMetric) metric).add(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(LongMetric metric, long value) {
+ ((AtomicLongMetric) metric).value(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected LongMetric createMetric(String name, String description) {
+ return new AtomicLongMetric(name, description);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DistributionMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DistributionMetricTest.java
new file mode 100644
index 0000000..b0d1484
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DistributionMetricTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.metrics;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for {@link DistributionMetric}.
+ */
+public class DistributionMetricTest {
+ /**
+ * Check that calling constructor with invalid parameters causes errors.
+ */
+ @Test
+ public void creationTest() {
+ assertThrows(Throwable.class, () -> new DistributionMetric("test", null, null));
+ assertThrows(AssertionError.class, () -> new DistributionMetric("test", null, new long[0]));
+ assertThrows(AssertionError.class, () -> new DistributionMetric("test", null, new long[] { 10, 1}));
+ assertThrows(AssertionError.class, () -> new DistributionMetric("test", null, new long[] { 1, 10, 10, 100}));
+ assertThrows(AssertionError.class, () -> new DistributionMetric("test", null, new long[] { -1, 1, 10, 100}));
+ }
+
+ /**
+ * Create a distribution metric and fill it with some values.
+ *
+ * @return Metric.
+ */
+ private DistributionMetric createAndPrepareMetric() {
+ long[] bounds = new long[] {50, 500};
+
+ DistributionMetric distribution = new DistributionMetric("distribution", null, bounds);
+
+ assertEquals(bounds.length + 1, distribution.value().length);
+
+ distribution.add(10);
+ distribution.add(51);
+ distribution.add(60);
+ distribution.add(600);
+ distribution.add(600);
+ distribution.add(600);
+
+ return distribution;
+ }
+
+ /**
+ * Check that the values in distribution buckets are correct.
+ */
+ @Test
+ public void testBucketValues() {
+ DistributionMetric distribution = createAndPrepareMetric();
+
+ distribution.value();
+
+ assertEquals(1, distribution.value()[0]);
+ assertEquals(2, distribution.value()[1]);
+ assertEquals(3, distribution.value()[2]);
+ }
+
+ /**
+ * Test scalar metrics that are returned by {@link DistributionMetric#asScalarMetrics()}.
+ */
+ @Test
+ public void testScalarMetrics() {
+ DistributionMetric distribution = createAndPrepareMetric();
+
+ List<Metric> scalarMetrics = distribution.asScalarMetrics();
+
+ assertEquals("distribution_0_50", scalarMetrics.get(0).name());
+ assertEquals("distribution_50_500", scalarMetrics.get(1).name());
+ assertEquals("distribution_500_inf", scalarMetrics.get(2).name());
+
+ for (int i = 0; i < scalarMetrics.size(); i++) {
+ LongMetric lm = (LongMetric) scalarMetrics.get(i);
+ assertEquals(i + 1, lm.value());
+ }
+
+ distribution.add(1);
+ distribution.add(100);
+ distribution.add(1000);
+
+ for (int i = 0; i < scalarMetrics.size(); i++) {
+ LongMetric lm = (LongMetric) scalarMetrics.get(i);
+ assertEquals(i + 2, lm.value());
+ }
+ }
+
+ /**
+ * Test the correctness of {@link DistributionMetric#getValueAsString()}.
+ */
+ @Test
+ public void testGetValueAsString() {
+ DistributionMetric distribution = createAndPrepareMetric();
+
+ assertEquals("[distribution_0_50:1, distribution_50_500:2, distribution_500_inf:3]", distribution.getValueAsString());
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DoubleAdderMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DoubleAdderMetricTest.java
new file mode 100644
index 0000000..4725ad2
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DoubleAdderMetricTest.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.metrics;
+
+/**
+ * Test for {@link DoubleAdderMetric}.
+ */
+public class DoubleAdderMetricTest extends AbstractDoubleMetricTest {
+ /** {@inheritDoc} */
+ @Override protected void increment0(DoubleMetric metric) {
+ ((DoubleAdderMetric) metric).add(1);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(DoubleMetric metric) {
+ ((DoubleAdderMetric) metric).add(-1);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(DoubleMetric metric, double value) {
+ ((DoubleAdderMetric) metric).add(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(DoubleMetric metric, double value) {
+ ((DoubleAdderMetric) metric).value(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected DoubleMetric createMetric(String name, String description) {
+ return new DoubleAdderMetric(name, description);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DoubleGaugeTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DoubleGaugeTest.java
new file mode 100644
index 0000000..a5f177d
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/DoubleGaugeTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.metrics;
+
+/**
+ * Test for {@link DoubleGauge}.
+ */
+public class DoubleGaugeTest extends AbstractDoubleMetricTest {
+ /** A value for the supplier. */
+ private double value = 0;
+
+ /** {@inheritDoc} */
+ @Override protected void increment0(DoubleMetric metric) {
+ value++;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(DoubleMetric metric) {
+ value--;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(DoubleMetric metric, double value) {
+ this.value += value;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(DoubleMetric metric, double value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected DoubleMetric createMetric(String name, String description) {
+ return new DoubleGauge(name, description, () -> value);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/HitRateMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/HitRateMetricTest.java
new file mode 100644
index 0000000..a7c5353
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/HitRateMetricTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.metrics;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Hit rate metric test.
+ */
+public class HitRateMetricTest {
+ @Test
+ public void testHitRateMetric() {
+ HitRateMetric hitRateMetric = new HitRateMetric("hitRate", null, 100);
+
+ hitRateMetric.increment();
+
+ doSleep(5);
+ hitRateMetric.add(2);
+
+ doSleep(20);
+
+ hitRateMetric.increment();
+
+ assertEquals(4, hitRateMetric.value());
+
+ doSleep(100);
+ hitRateMetric.increment();
+
+ assertEquals(1, hitRateMetric.value());
+ }
+
+ private void doSleep(long time) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ // No-op.
+ }
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/IntGaugeTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/IntGaugeTest.java
new file mode 100644
index 0000000..e341dfa
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/IntGaugeTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.metrics;
+
+/**
+ * Test for {@link IntGauge}.
+ */
+public class IntGaugeTest extends AbstractIntMetricTest {
+ /** A value for the supplier. */
+ private int value;
+
+ /** {@inheritDoc} */
+ @Override protected void increment0(IntMetric metric) {
+ value++;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(IntMetric metric) {
+ value--;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(IntMetric metric, int value) {
+ this.value += value;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(IntMetric metric, int value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IntMetric createMetric(String name, String description) {
+ return new IntGauge(name, description, () -> value);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/LongAdderMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/LongAdderMetricTest.java
new file mode 100644
index 0000000..641d2f9
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/LongAdderMetricTest.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.metrics;
+
+/**
+ * Test for {@link LongAdderMetric}.
+ */
+public class LongAdderMetricTest extends AbstractLongMetricTest {
+ /** {@inheritDoc} */
+ @Override protected void increment0(LongMetric metric) {
+ ((LongAdderMetric) metric).increment();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(LongMetric metric) {
+ ((LongAdderMetric) metric).decrement();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(LongMetric metric, long value) {
+ ((LongAdderMetric) metric).add(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(LongMetric metric, long value) {
+ ((LongAdderMetric) metric).value(value);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected LongMetric createMetric(String name, String description) {
+ return new LongAdderMetric(name, description);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/LongGaugeMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/LongGaugeMetricTest.java
new file mode 100644
index 0000000..0dd25ca
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/LongGaugeMetricTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.metrics;
+
+/**
+ * Test for {@link LongGauge}.
+ */
+public class LongGaugeMetricTest extends AbstractLongMetricTest {
+ /** A value for the supplier. */
+ private long value = 0;
+
+ /** {@inheritDoc} */
+ @Override protected void increment0(LongMetric metric) {
+ value++;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void decrement0(LongMetric metric) {
+ value--;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void add0(LongMetric metric, long value) {
+ this.value += value;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void setValue0(LongMetric metric, long value) {
+ this.value = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected LongMetric createMetric(String name, String description) {
+ return new LongGauge(name, description, () -> value);
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricEntitiesTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricEntitiesTest.java
new file mode 100644
index 0000000..6b05321
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricEntitiesTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.metrics;
+
+import static java.util.Spliterators.spliteratorUnknownSize;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.StreamSupport.stream;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.lang.IgniteBiTuple;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for metric entities, such as {@link MetricRegistry} and {@link MetricSet}.
+ */
+public class MetricEntitiesTest {
+ private static final String SOURCE_NAME = "testSource";
+ private static final String SCALAR_METRIC_NAME = "testScalarMetric";
+ private static final String COMPOSITE_METRIC_NAME = "testCompositeMetric";
+ private static final long[] DISTRIBUTION_BOUNDS = new long[] { 10, 100, 1000 };
+
+ @Test
+ public void testMetricLifecycle() {
+ MetricRegistry registry = new MetricRegistry();
+
+ MetricSource metricSource = new TestMetricSource();
+
+ registry.registerSource(metricSource);
+ assertEquals(0L, registry.metricSnapshot().get2());
+
+ assertThrows(IllegalStateException.class, () -> registry.registerSource(metricSource));
+
+ assertEquals(0L, registry.metricSnapshot().get2());
+ assertTrue(registry.metricSnapshot().get1().isEmpty());
+
+ MetricSource alreadyEnabled = new TestMetricSource("alreadyEnabled");
+ alreadyEnabled.enable();
+ assertThrows(AssertionError.class, () -> registry.registerSource(alreadyEnabled));
+ assertEquals(0L, registry.metricSnapshot().get2());
+
+ // Enabling metric source, metric snapshot and its version should be changed.
+ MetricSet metricSet = registry.enable(SOURCE_NAME);
+ assertNotNull(metricSet);
+ assertEquals(1L, registry.metricSnapshot().get2());
+ assertFalse(registry.metricSnapshot().get1().isEmpty());
+ assertNull(registry.enable(metricSource));
+
+ assertThrows(IllegalStateException.class, () -> registry.enable("unexisting"));
+ assertEquals(1L, registry.metricSnapshot().get2());
+
+ // Enabling the metric source that was already enabled before, metric snapshot should not be changed.
+ assertNull(registry.enable(SOURCE_NAME));
+ IgniteBiTuple<Map<String, MetricSet>, Long> metricSnapshot = registry.metricSnapshot();
+ assertEquals(1L, metricSnapshot.get2());
+ assertFalse(metricSnapshot.get1().isEmpty());
+ MetricSet ms = metricSnapshot.get1().get(SOURCE_NAME);
+ assertEquals(metricSet, ms);
+
+ // Disable the metric source.
+ registry.disable(SOURCE_NAME);
+ assertEquals(2L, registry.metricSnapshot().get2());
+
+ // Disable unexisting metric source, exception is thrown, metric snapshot should not be changed.
+ assertThrows(IllegalStateException.class, () -> registry.disable("unexisting"));
+ metricSnapshot = registry.metricSnapshot();
+ assertEquals(2L, metricSnapshot.get2());
+ assertTrue(metricSnapshot.get1().isEmpty());
+
+ // Trying to disable the metric source that was already disabled before, metric snapshot should not be changed.
+ registry.disable(SOURCE_NAME);
+ assertEquals(2L, registry.metricSnapshot().get2());
+ registry.disable(metricSource);
+ assertEquals(2L, registry.metricSnapshot().get2());
+
+ // Enabling metric source again, metric snapshot changes.
+ registry.enable(metricSource);
+ assertEquals(3L, registry.metricSnapshot().get2());
+ assertFalse(registry.metricSnapshot().get1().isEmpty());
+
+ // Unregister enabled metric source, it should be disabled, metric snapshot should be changed.
+ registry.unregisterSource(metricSource);
+ assertEquals(4L, registry.metricSnapshot().get2());
+ assertTrue(registry.metricSnapshot().get1().isEmpty());
+
+ // Trying to unregister the metric source that was already unregistered before, metric snapshot should not be changed.
+ assertThrows(IllegalStateException.class, () -> registry.unregisterSource(metricSource));
+ metricSnapshot = registry.metricSnapshot();
+ assertEquals(4L, metricSnapshot.get2());
+ assertTrue(metricSnapshot.get1().isEmpty());
+ }
+
+ @Test
+ public void testMetricSet() {
+ MetricRegistry registry = new MetricRegistry();
+
+ TestMetricSource metricSource = new TestMetricSource();
+
+ registry.registerSource(metricSource);
+
+ assertNull(metricSource.holder());
+
+ MetricSet metricSet = registry.enable(metricSource.name());
+
+ TestMetricSource.Holder holder = metricSource.holder();
+
+ assertNotNull(holder);
+
+ assertTrue(metricSet.get(SCALAR_METRIC_NAME) instanceof IntMetric);
+ assertTrue(metricSet.get(COMPOSITE_METRIC_NAME) instanceof DistributionMetric);
+
+ List<Metric> metrics = stream(spliteratorUnknownSize(metricSet.iterator(), 0), false).collect(toList());
+ assertEquals(2, metrics.size());
+
+ assertEquals(SCALAR_METRIC_NAME, holder.atomicIntMetric.name());
+ assertEquals(COMPOSITE_METRIC_NAME, holder.distributionMetric.name());
+
+ List<Metric> scalarMetrics = stream(spliteratorUnknownSize(new CompositeAwareIterator(metrics.iterator()), 0), false)
+ .collect(toList());
+
+ assertEquals(2 + DISTRIBUTION_BOUNDS.length, scalarMetrics.size());
+
+ assertEquals(SCALAR_METRIC_NAME, scalarMetrics.get(0).name());
+ assertEquals(COMPOSITE_METRIC_NAME + "_0_" + DISTRIBUTION_BOUNDS[0], scalarMetrics.get(1).name());
+ assertEquals(COMPOSITE_METRIC_NAME + '_' + DISTRIBUTION_BOUNDS[0] + "_" + DISTRIBUTION_BOUNDS[1], scalarMetrics.get(2).name());
+ assertEquals(COMPOSITE_METRIC_NAME + '_' + DISTRIBUTION_BOUNDS[1] + "_" + DISTRIBUTION_BOUNDS[2], scalarMetrics.get(3).name());
+ assertEquals(COMPOSITE_METRIC_NAME + '_' + DISTRIBUTION_BOUNDS[2] + "_inf", scalarMetrics.get(4).name());
+
+ registry.disable(metricSource.name());
+
+ assertNull(metricSource.holder());
+ }
+
+ private static class TestMetricSource extends AbstractMetricSource<TestMetricSource.Holder> {
+ protected TestMetricSource() {
+ super(SOURCE_NAME);
+ }
+
+
+ protected TestMetricSource(String name) {
+ super(name);
+ }
+
+ @Override protected Holder createHolder() {
+ return new Holder();
+ }
+
+ @Override protected void init(MetricSetBuilder bldr, Holder holder) {
+ bldr.register(holder.atomicIntMetric);
+ bldr.register(holder.distributionMetric);
+ }
+
+ private static class Holder implements AbstractMetricSource.Holder<Holder> {
+ final AtomicIntMetric atomicIntMetric = new AtomicIntMetric(SCALAR_METRIC_NAME, null);
+ final DistributionMetric distributionMetric = new DistributionMetric(COMPOSITE_METRIC_NAME, null, DISTRIBUTION_BOUNDS);
+ }
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/ThreadPoolMetricSource.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/ThreadPoolMetricSource.java
new file mode 100644
index 0000000..ca4f116
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/ThreadPoolMetricSource.java
@@ -0,0 +1,99 @@
+/*
+ * 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.metrics;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * Metric source for {@link ThreadPoolMetricTest}.
+ */
+public class ThreadPoolMetricSource extends AbstractMetricSource<ThreadPoolMetricSource.Holder> {
+ private final ThreadPoolExecutor exec;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name.
+ * @param exec Executor.
+ */
+ public ThreadPoolMetricSource(String name, ThreadPoolExecutor exec) {
+ super(name);
+
+ this.exec = exec;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Holder createHolder() {
+ return new Holder();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void init(MetricSetBuilder bldr, Holder holder) {
+ bldr.intGauge(
+ "ActiveCount",
+ "Approximate number of threads that are actively executing tasks.",
+ exec::getActiveCount
+ );
+
+ bldr.longGauge(
+ "CompletedTaskCount",
+ "Approximate total number of tasks that have completed execution.",
+ exec::getCompletedTaskCount
+ );
+
+ bldr.intGauge("CorePoolSize", "The core number of threads.", exec::getCorePoolSize);
+
+ bldr.intGauge(
+ "LargestPoolSize",
+ "Largest number of threads that have ever simultaneously been in the pool.",
+ exec::getLargestPoolSize
+ );
+
+ bldr.intGauge(
+ "MaximumPoolSize",
+ "The maximum allowed number of threads.",
+ exec::getMaximumPoolSize
+ );
+
+ bldr.intGauge("PoolSize", "Current number of threads in the pool.", exec::getPoolSize);
+
+ bldr.longGauge(
+ "TaskCount",
+ "Approximate total number of tasks that have been scheduled for execution.",
+ exec::getTaskCount
+ );
+
+ bldr.intGauge("QueueSize", "Current size of the execution queue.", () -> exec.getQueue().size());
+
+ bldr.longGauge(
+ "KeepAliveTime",
+ "Thread keep-alive time, which is the amount of time which threads in excess of "
+ + "the core pool size may remain idle before being terminated.",
+ () -> exec.getKeepAliveTime(MILLISECONDS)
+ );
+ }
+
+ /**
+ * Holder class.
+ */
+ protected static class Holder implements AbstractMetricSource.Holder<Holder> {
+ // No-op.
+ }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/ThreadPoolMetricTest.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/ThreadPoolMetricTest.java
new file mode 100644
index 0000000..c9fb0b7
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/ThreadPoolMetricTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.metrics;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Metrics for thread pool.
+ */
+public class ThreadPoolMetricTest {
+ @Test
+ public void test() throws ExecutionException, InterruptedException {
+ // Should be one per node.
+ MetricRegistry registry = new MetricRegistry();
+
+ // ------------------------------------------------------------------------
+
+ // System component, e.g. thread pool executor
+ ThreadPoolExecutor exec = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
+
+ // Metrics source for thread pool
+ ThreadPoolMetricSource src = new ThreadPoolMetricSource("example.thread_pool.ExamplePool", exec);
+
+ // Register source after the component created.
+ registry.registerSource(src);
+
+ // ------------------------------------------------------------------------
+
+ // Enable metrics by signal (or because configuration)
+ MetricSet metricSet = registry.enable(src.name());
+
+ LongMetric completedTaskCount = metricSet.get("CompletedTaskCount");
+
+ assertEquals(0L, completedTaskCount.value());
+
+ exec.submit(() -> {}).get();
+ assertEquals(1L, completedTaskCount.value());
+
+ // ------------------------------------------------------------------------
+
+ // Disable metrics by signal
+ registry.disable(src.name());
+
+ // Component is stopped\destroyed
+ registry.unregisterSource(src);
+ exec.shutdown();
+ }
+}
diff --git a/modules/runner/pom.xml b/modules/runner/pom.xml
index 5cbc1e7..ae0605d 100644
--- a/modules/runner/pom.xml
+++ b/modules/runner/pom.xml
@@ -118,6 +118,11 @@
<artifactId>ignite-storage-page-memory</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-metrics</artifactId>
+ </dependency>
+
<!-- 3rd party dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index 400b345..cf8a1b6 100644
--- a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -59,6 +59,7 @@
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.metastorage.MetaStorageManager;
import org.apache.ignite.internal.metastorage.server.persistence.RocksDbKeyValueStorage;
+import org.apache.ignite.internal.metrics.MetricManager;
import org.apache.ignite.internal.raft.Loza;
import org.apache.ignite.internal.recovery.ConfigurationCatchUpListener;
import org.apache.ignite.internal.recovery.RecoveryCompletionFutureFactory;
@@ -198,6 +199,9 @@
/** Schema manager. */
private final SchemaManager schemaManager;
+ /** Metric manager. */
+ private final MetricManager metricManager;
+
/**
* The Constructor.
*
@@ -215,6 +219,8 @@
vaultMgr = createVault(workDir);
+ metricManager = new MetricManager();
+
ConfigurationModules modules = loadConfigurationModules(serviceProviderClassLoader);
nodeCfgMgr = new ConfigurationManager(
@@ -428,6 +434,7 @@
// Start the components that are required to join the cluster.
lifecycleManager.startComponents(
+ metricManager,
nettyBootstrapFactory,
clusterSvc,
restComponent,
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java
index ce2e0fc..7f673e1 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java
@@ -33,7 +33,7 @@
import org.apache.ignite.internal.sql.engine.AsyncCursor.BatchedResult;
import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
import org.apache.ignite.internal.sql.engine.SqlQueryType;
-import org.apache.ignite.internal.sql.engine.util.TransformingIterator;
+import org.apache.ignite.internal.util.TransformingIterator;
import org.apache.ignite.sql.NoRowSetExpectedException;
import org.apache.ignite.sql.ResultSetMetadata;
import org.apache.ignite.sql.SqlException;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AbstractIndexScan.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AbstractIndexScan.java
index 028525a..2acc3f4 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AbstractIndexScan.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/AbstractIndexScan.java
@@ -22,8 +22,8 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.calcite.rel.type.RelDataType;
-import org.apache.ignite.internal.sql.engine.util.FilteringIterator;
-import org.apache.ignite.internal.sql.engine.util.TransformingIterator;
+import org.apache.ignite.internal.util.FilteringIterator;
+import org.apache.ignite.internal.util.TransformingIterator;
import org.apache.ignite.lang.IgniteInternalException;
/**
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RuntimeHashIndex.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RuntimeHashIndex.java
index 143f337..3d31632 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RuntimeHashIndex.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/RuntimeHashIndex.java
@@ -28,7 +28,7 @@
import java.util.function.Supplier;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.sql.engine.exec.exp.agg.GroupKey;
-import org.apache.ignite.internal.sql.engine.util.FilteringIterator;
+import org.apache.ignite.internal.util.FilteringIterator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableFunctionScan.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableFunctionScan.java
index 2f7822d..5e1cf4b 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableFunctionScan.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/TableFunctionScan.java
@@ -20,7 +20,7 @@
import java.util.Iterator;
import java.util.function.Supplier;
import org.apache.ignite.internal.sql.engine.exec.RowHandler.RowFactory;
-import org.apache.ignite.internal.sql.engine.util.TransformingIterator;
+import org.apache.ignite.internal.util.TransformingIterator;
import org.jetbrains.annotations.NotNull;
/**
diff --git a/parent/pom.xml b/parent/pom.xml
index 8a7de0d..d15b3ca 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -469,6 +469,12 @@
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-metrics</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
<!-- 3rd party dependencies -->
<dependency>
<groupId>org.jetbrains</groupId>
diff --git a/pom.xml b/pom.xml
index d514df6..012de13 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,7 @@
<module>modules/metastorage-client</module>
<module>modules/metastorage-common</module>
<module>modules/metastorage-server</module>
+ <module>modules/metrics</module>
<module>modules/network</module>
<module>modules/network-annotation-processor</module>
<module>modules/network-api</module>