You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ignite.apache.org by "dant3 (via GitHub)" <gi...@apache.org> on 2026/03/23 18:36:57 UTC

[PR] IGNITE-28230 create metrics-api module [ignite-3]

dant3 opened a new pull request, #7855:
URL: https://github.com/apache/ignite-3/pull/7855

   https://issues.apache.org/jira/browse/IGNITE-28230


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo merged PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010810587


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   if we plan to keep the contract, then why do we need two maps anyway? Can we still have only one and have a boolean guard or check "enabled" to prohibit adding new metrics?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010872157


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {

Review Comment:
   fixed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3014458387


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
+            AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
+
+    private final String name;
+
+    private final String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private volatile @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "name");
+        this.description = Objects.requireNonNull(description, "description");
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
+            return newMetricSet;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void disable() {
+        METRIC_SET_UPD.set(this, null);

Review Comment:
   if we do that we will loose CAS guarantees on the other call, so i'd prefer to leave it as is



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010688450


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   Why do you introduce this logic in this PR then? It's not needed anywhere yet



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010688450


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   Why do you introduce this logic in this PR then? It's not needed anywhere yet. Right now it looks very strange: we have a two maps at the same time in this class



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   Why do you introduce this logic in this PR then? It's not needed anywhere yet. Right now it looks very strange: we have two maps at the same time in this class



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3014509144


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
+            AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
+
+    private final String name;
+
+    private final String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private volatile @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "name");
+        this.description = Objects.requireNonNull(description, "description");
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
+            return newMetricSet;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void disable() {
+        METRIC_SET_UPD.set(this, null);

Review Comment:
   > if we do that we will loose CAS guarantees on the other call
   
   Sorry, what do you mean? Why are we going to lose any guarantees?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3014694133


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
+            AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
+
+    private final String name;
+
+    private final String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private volatile @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "name");
+        this.description = Objects.requireNonNull(description, "description");
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
+            return newMetricSet;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void disable() {
+        METRIC_SET_UPD.set(this, null);

Review Comment:
   From `AtomicReferenceFieldUpdater` javadoc:
   
   > Because this class cannot ensure that all uses of the field are appropriate for purposes of atomic access, it can guarantee atomicity only with respect to other invocations of compareAndSet and set on the same updater.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3014710442


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
+            AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
+
+    private final String name;
+
+    private final String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private volatile @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "name");
+        this.description = Objects.requireNonNull(description, "description");
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
+            return newMetricSet;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void disable() {
+        METRIC_SET_UPD.set(this, null);

Review Comment:
   In other words it only guaratees for `compareAndSet` to be correct within other `compareAndSet` and `set` operations performed on the same instance of `AtomicReferenceFieldUpdater`. If we do volatile write without `set`, `compareAndSet` in `enable` method has no way of knowing about that



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010710716


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {

Review Comment:
   > However we could actually try to enforce it as non-nullable here to fix that bad practice.
   
   Yes, that's what I was implying



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010784639


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {

Review Comment:
   VarMetric is Java 17+ FWIF



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {

Review Comment:
   VarMetric is Java 17+ FWIW



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010778583


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   > Why do you introduce this logic in this PR then? It's not needed anywhere yet. Right now it looks very strange: we have two maps at the same time in this class
   
   I will have to introduce it in every metric source then as well as in MetricsManager - a change in the core contract between MetricsManager and MetricSource



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010797994


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {

Review Comment:
   > Actually I used VarHandle at first but changed it to synchronized to make the code simplier since concurrent calls to enable/disable, while possible, are rare.
   
   But it doesn't make sense to take a full blown lock here.
   
   > VarMetric is Java 17+ FWIW
   
   what's VarMetric? VarHandle is not Java 17



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010841679


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   Add a TODO here with the corresponding ticket then, please



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010724858


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {

Review Comment:
   It's used for grouping in JMX: sources with the same group get folded under one MBean folder. Most sources don't set it. This is a preexisting contract of our metrics framework.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010359389


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();

Review Comment:
   ```suggestion
       private final ConcurrentMap<String, Metric> metrics = new ConcurrentHashMap<>();
   ```



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {

Review Comment:
   Why is this method "synchronized"? To handle concurrent "enable"? If yes, it can be solved by an `AtomicReference` or `VarHandle`



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {

Review Comment:
   What's a "group"?



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        return metricSet;
+    }
+
+    @Override
+    public synchronized void disable() {

Review Comment:
   Same here and in "enabled", I don't understand why they are syncrhonized



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {

Review Comment:
   Why is description nullable? Don't we always want to have a description?



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   So, new metrics are not allowed after "enable" has been called? Or how is it supposed to work?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010671276


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   Yes, this is precisely how it works for every MetricSource we have nowadays. This is absolutely stupid and artificial limitation of our metrics framework. Out of scope of that PR though since it has to be fixed on MetricManager side



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010804038


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {

Review Comment:
   Please add a javadoc description



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010810805


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   We will be able to easily change that once we get rid of miriads of custom MetricSource implementations



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3014694133


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
+            AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
+
+    private final String name;
+
+    private final String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private volatile @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "name");
+        this.description = Objects.requireNonNull(description, "description");
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
+            return newMetricSet;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void disable() {
+        METRIC_SET_UPD.set(this, null);

Review Comment:
   From AtomicReferenceFieldUpdater javadoc:
   
   > Because this class cannot ensure that all uses of the field are appropriate for purposes of atomic access, it can guarantee atomicity only with respect to other invocations of compareAndSet and set on the same updater.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010731319


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {

Review Comment:
   Actually I used VarHandle at first but changed it to synchronized to make the code simplier since concurrent calls to enable/disable, while possible, are rare.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010700719


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {

Review Comment:
   We have at least 10 implementations of MetricSource that don't set any description, and this being nullable is the general contract we have nowadays. Since this specific PR is not about changing that contract I left it as is.
   However we could actually try to enforce it as non-nullable here to fix that bad practice.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010954361


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
+            AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
+
+    private final String name;
+
+    private final String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private volatile @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "name");
+        this.description = Objects.requireNonNull(description, "description");
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
+            return newMetricSet;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void disable() {
+        METRIC_SET_UPD.set(this, null);

Review Comment:
   Not a big deal, but you can simply write `metricSet = null` here



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3010810587


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   if we plan to keep the contract, then why do we need two maps? Can we still have only one and have a boolean guard and check "enabled" to prohibit adding new metrics?



##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   if we plan to keep the contract, then why do we need two maps? Can we still have only one and have a boolean guard or check "enabled" to prohibit adding new metrics?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "dant3 (via GitHub)" <gi...@apache.org>.
dant3 commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3014461846


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.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 java.util.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    private final String name;
+
+    private final @Nullable String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name) {
+        this(name, null, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, @Nullable String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "Metric source name must not be null");
+        this.description = description;
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public @Nullable String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public synchronized @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        metricSet = new MetricSet(name, description, group, new HashMap<>(metrics));

Review Comment:
   created a task, added todo



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-28230 Implement SimpleMetricSource [ignite-3]

Posted by "sashapolo (via GitHub)" <gi...@apache.org>.
sashapolo commented on code in PR #7855:
URL: https://github.com/apache/ignite-3/pull/7855#discussion_r3014741966


##########
modules/metrics/src/main/java/org/apache/ignite/internal/metrics/SimpleMetricSource.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.HashMap;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A {@link MetricSource} with factory methods for creating and registering metrics.
+ *
+ * <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
+ * Enable/disable controls registry visibility only — metrics themselves are always alive
+ * and values persist across enable/disable cycles.
+ *
+ * <p>Usage:
+ * <pre>{@code
+ * public class MyMetrics {
+ *     private final LongAdderMetric requests;
+ *     private final DistributionMetric duration;
+ *
+ *     public MyMetrics(SimpleMetricSource source) {
+ *         requests = source.longAdder("Requests", "Total requests.");
+ *         duration = source.distribution("Duration", "Request duration in ms.",
+ *                 new long[]{1, 5, 10, 50, 100, 500});
+ *     }
+ *
+ *     public void onRequest(long durationMs) {
+ *         requests.increment();
+ *         duration.add(durationMs);
+ *     }
+ * }
+ * }</pre>
+ */
+public final class SimpleMetricSource implements MetricSource {
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
+            AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
+
+    private final String name;
+
+    private final String description;
+
+    private final @Nullable String group;
+
+    private final ConcurrentHashMap<String, Metric> metrics = new ConcurrentHashMap<>();
+
+    /** Non-null when enabled. */
+    private volatile @Nullable MetricSet metricSet;
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description) {
+        this(name, description, null);
+    }
+
+    /** Constructor. */
+    public SimpleMetricSource(String name, String description, @Nullable String group) {
+        this.name = Objects.requireNonNull(name, "name");
+        this.description = Objects.requireNonNull(description, "description");
+        this.group = group;
+    }
+
+    @Override
+    public final String name() {
+        return name;
+    }
+
+    @Override
+    public String description() {
+        return description;
+    }
+
+    @Override
+    public @Nullable String group() {
+        return group;
+    }
+
+    /** Creates an {@link AtomicIntMetric}. */
+    public AtomicIntMetric atomicInt(String name, @Nullable String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    /** Creates an {@link IntGauge}. */
+    public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicLongMetric}. */
+    public AtomicLongMetric atomicLong(String name, @Nullable String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    /** Creates a {@link LongAdderMetric}. */
+    public LongAdderMetric longAdder(String name, @Nullable String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    /** Creates a {@link LongGauge}. */
+    public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    /** Creates an {@link AtomicDoubleMetric}. */
+    public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleAdderMetric}. */
+    public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    /** Creates a {@link DoubleGauge}. */
+    public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+
+    /** Creates a {@link HitRateMetric} with default counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
+        return register(new HitRateMetric(name, description, rateTimeInterval));
+    }
+
+    /** Creates a {@link HitRateMetric} with custom counters array size. */
+    public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
+        return register(new HitRateMetric(name, description, rateTimeInterval, size));
+    }
+
+    /** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
+    public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
+        return register(new DistributionMetric(name, description, bounds));
+    }
+
+    /**
+     * Registers a pre-created metric. Use this for types without a dedicated factory method
+     * ({@link StringGauge}, {@link UuidGauge}, etc.).
+     *
+     * @return The same metric instance.
+     * @throws IllegalStateException If a metric with the given name is already registered.
+     */
+    public <T extends Metric> T register(T metric) {
+        Objects.requireNonNull(metric, "metric");
+
+        Metric existing = metrics.putIfAbsent(metric.name(), metric);
+
+        if (existing != null) {
+            throw new IllegalStateException("Metric with given name is already registered [sourceName="
+                    + this.name + ", metricName=" + metric.name() + ']');
+        }
+
+        return metric;
+    }
+
+    @Override
+    public @Nullable MetricSet enable() {
+        if (metricSet != null) {
+            return null;
+        }
+
+        MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
+
+        if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
+            return newMetricSet;
+        }
+
+        return null;
+    }
+
+    @Override
+    public void disable() {
+        METRIC_SET_UPD.set(this, null);

Review Comment:
   I'm pretty sure that `.set` and simple volatile assignments are equivalent, especially if you have a look at the implementation. However, this is not a big deal and the javadoc is quite confusing in this regard, so I leave it up to you to choose the variant you like.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org