Skip to content

Commit a1df12f

Browse files
authored
IGNITE-28230 Implement SimpleMetricSource (#7855)
1 parent 613f646 commit a1df12f

File tree

3 files changed

+487
-0
lines changed

3 files changed

+487
-0
lines changed

modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetricSource.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
* Base class for all metric sources.
2727
*
2828
* @param <T> Holder type.
29+
* @deprecated Use {@link SimpleMetricSource} instead.
2930
*/
31+
@Deprecated
3032
public abstract class AbstractMetricSource<T extends AbstractMetricSource.Holder<T>> implements MetricSource {
3133
/** Holder field updater. */
3234
@SuppressWarnings("rawtypes")
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.internal.metrics;
19+
20+
import java.util.HashMap;
21+
import java.util.Objects;
22+
import java.util.concurrent.ConcurrentHashMap;
23+
import java.util.concurrent.ConcurrentMap;
24+
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
25+
import java.util.function.DoubleSupplier;
26+
import java.util.function.IntSupplier;
27+
import java.util.function.LongSupplier;
28+
import org.jetbrains.annotations.Nullable;
29+
30+
/**
31+
* A {@link MetricSource} with factory methods for creating and registering metrics.
32+
*
33+
* <p>Metrics are created via factory methods and stored in a {@link ConcurrentHashMap}.
34+
* Enable/disable controls registry visibility only — metrics themselves are always alive
35+
* and values persist across enable/disable cycles.
36+
*
37+
* <p>Usage:
38+
* <pre>{@code
39+
* public class MyMetrics {
40+
* private final LongAdderMetric requests;
41+
* private final DistributionMetric duration;
42+
*
43+
* public MyMetrics(SimpleMetricSource source) {
44+
* requests = source.longAdder("Requests", "Total requests.");
45+
* duration = source.distribution("Duration", "Request duration in ms.",
46+
* new long[]{1, 5, 10, 50, 100, 500});
47+
* }
48+
*
49+
* public void onRequest(long durationMs) {
50+
* requests.increment();
51+
* duration.add(durationMs);
52+
* }
53+
* }
54+
* }</pre>
55+
*/
56+
public final class SimpleMetricSource implements MetricSource {
57+
@SuppressWarnings("rawtypes")
58+
private static final AtomicReferenceFieldUpdater<SimpleMetricSource, MetricSet> METRIC_SET_UPD =
59+
AtomicReferenceFieldUpdater.newUpdater(SimpleMetricSource.class, MetricSet.class, "metricSet");
60+
61+
private final String name;
62+
63+
private final String description;
64+
65+
private final @Nullable String group;
66+
67+
private final ConcurrentMap<String, Metric> metrics = new ConcurrentHashMap<>();
68+
69+
/** Non-null when enabled. */
70+
private volatile @Nullable MetricSet metricSet;
71+
72+
/**
73+
* Constructor.
74+
*
75+
* @param name Metric source name.
76+
* @param description Metric source description.
77+
*/
78+
public SimpleMetricSource(String name, String description) {
79+
this(name, description, null);
80+
}
81+
82+
/**
83+
* Constructor.
84+
*
85+
* @param name Metric source name.
86+
* @param description Metric source description.
87+
* @param group Optional group name for additional grouping in external systems (e.g. JMX).
88+
*/
89+
public SimpleMetricSource(String name, String description, @Nullable String group) {
90+
this.name = Objects.requireNonNull(name, "name");
91+
this.description = Objects.requireNonNull(description, "description");
92+
this.group = group;
93+
}
94+
95+
@Override
96+
public final String name() {
97+
return name;
98+
}
99+
100+
@Override
101+
public String description() {
102+
return description;
103+
}
104+
105+
@Override
106+
public @Nullable String group() {
107+
return group;
108+
}
109+
110+
/** Creates an {@link AtomicIntMetric}. */
111+
public AtomicIntMetric atomicInt(String name, @Nullable String description) {
112+
return register(new AtomicIntMetric(name, description));
113+
}
114+
115+
/** Creates an {@link IntGauge}. */
116+
public IntGauge intGauge(String name, @Nullable String description, IntSupplier supplier) {
117+
return register(new IntGauge(name, description, supplier));
118+
}
119+
120+
/** Creates an {@link AtomicLongMetric}. */
121+
public AtomicLongMetric atomicLong(String name, @Nullable String description) {
122+
return register(new AtomicLongMetric(name, description));
123+
}
124+
125+
/** Creates a {@link LongAdderMetric}. */
126+
public LongAdderMetric longAdder(String name, @Nullable String description) {
127+
return register(new LongAdderMetric(name, description));
128+
}
129+
130+
/** Creates a {@link LongGauge}. */
131+
public LongGauge longGauge(String name, @Nullable String description, LongSupplier supplier) {
132+
return register(new LongGauge(name, description, supplier));
133+
}
134+
135+
/** Creates an {@link AtomicDoubleMetric}. */
136+
public AtomicDoubleMetric atomicDouble(String name, @Nullable String description) {
137+
return register(new AtomicDoubleMetric(name, description));
138+
}
139+
140+
/** Creates a {@link DoubleAdderMetric}. */
141+
public DoubleAdderMetric doubleAdder(String name, @Nullable String description) {
142+
return register(new DoubleAdderMetric(name, description));
143+
}
144+
145+
/** Creates a {@link DoubleGauge}. */
146+
public DoubleGauge doubleGauge(String name, @Nullable String description, DoubleSupplier supplier) {
147+
return register(new DoubleGauge(name, description, supplier));
148+
}
149+
150+
/** Creates a {@link HitRateMetric} with default counters array size. */
151+
public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval) {
152+
return register(new HitRateMetric(name, description, rateTimeInterval));
153+
}
154+
155+
/** Creates a {@link HitRateMetric} with custom counters array size. */
156+
public HitRateMetric hitRate(String name, @Nullable String description, long rateTimeInterval, int size) {
157+
return register(new HitRateMetric(name, description, rateTimeInterval, size));
158+
}
159+
160+
/** Creates a {@link DistributionMetric}. Bounds must be sorted, unique, and start from {@code >= 0}. */
161+
public DistributionMetric distribution(String name, @Nullable String description, long[] bounds) {
162+
return register(new DistributionMetric(name, description, bounds));
163+
}
164+
165+
/**
166+
* Registers a pre-created metric. Use this for types without a dedicated factory method
167+
* ({@link StringGauge}, {@link UuidGauge}, etc.).
168+
*
169+
* @return The same metric instance.
170+
* @throws IllegalStateException If a metric with the given name is already registered.
171+
*/
172+
public <T extends Metric> T register(T metric) {
173+
Objects.requireNonNull(metric, "metric");
174+
175+
Metric existing = metrics.putIfAbsent(metric.name(), metric);
176+
177+
if (existing != null) {
178+
throw new IllegalStateException("Metric with given name is already registered [sourceName="
179+
+ this.name + ", metricName=" + metric.name() + ']');
180+
}
181+
182+
return metric;
183+
}
184+
185+
@Override
186+
public @Nullable MetricSet enable() {
187+
if (metricSet != null) {
188+
return null;
189+
}
190+
191+
// TODO: IGNITE-28412 consider making MetricSet observable instead of a single snapshot to support post-enable metric addition.
192+
MetricSet newMetricSet = new MetricSet(name, description, group, new HashMap<>(metrics));
193+
194+
if (METRIC_SET_UPD.compareAndSet(this, null, newMetricSet)) {
195+
return newMetricSet;
196+
}
197+
198+
return null;
199+
}
200+
201+
@Override
202+
public void disable() {
203+
METRIC_SET_UPD.set(this, null);
204+
}
205+
206+
@Override
207+
public boolean enabled() {
208+
return metricSet != null;
209+
}
210+
211+
}

0 commit comments

Comments
 (0)