...
ID | IEP-114 |
Author | |
Sponsor | |
Created |
|
Status | |
Motivation
It is useful for user to work with own metrics, not only with provided by Ignite. Current The current public metrics API doesn't expose any method to add or delete additional metrics.
...
- Custom metrics affect performance and take additional resources.
- There could be race conditions on metric registrations. Metrics with incompatible types can be concurrently registered with the same names.
- We do not expose API of all internal metrics. At least with the first tickets.
- Custom metrics aren't stored and require re-registration after node restart. At least with the first tickets.
API
APIs description
To give an user the ability to register additional metrics, we could either:
- Refactor a bit our current metrics. Create interfaces for writable metric registry, metric manager. Create interfaces for metrics like int, double, long, longAdder, boolean, object and the gauge metrics (IntGauge, LongGauge, etc.).
- Provide tiny-enough facade with consumers and suppliers for int, long, double value metrics.
These two approaches are shown below.
The APIs are supposed to be experimental.
Obtaining Custom Metrics
package org.apache.ignite;
public interface Ignite {
IgniteMetrics metrics();
}
...
Interfaces for existing metrics
...
|
package org.apache.ignite.metric; |
...
public interface IgniteMetrics extends Iterable<ReadOnlyMetricRegistry> |
...
{
MetricRegistry customRegistry(String registryName); |
...
@Nullable ReadOnlyMetricRegistry findRegistry(String registryName); |
...
void removeCustomRegistry(String registryName); |
...
Names like "LongMetric" or "ObjectMetric" we already have in the package "org.apache.ignite.spi.metric".
IgniteMetricRegistry
...
org.apache.ignite.metric; |
...
public interface MetricRegistry extends ReadOnlyMetricRegistry { |
...
IntMetric register(String name, IntSupplier supplier, @Nullable String desc); |
...
LongMetric register(String name, LongSupplier supplier, @Nullable String desc); |
...
DoubleMetric register(String name, DoubleSupplier supplier, @Nullable String desc); |
...
...
<T> ObjectMetric<T> register(String name, |
...
...
Class<T> type, @Nullable String desc); |
...
BooleanMetric register(String name, BooleanSupplier supplier, @Nullable String desc); |
...
IntValueMetric intMetric(String name, @Nullable String desc); |
...
LongValueMetric longMetric(String name, @Nullable String desc); |
...
LongSumMetric longAdderMetric(String name, @Nullable String desc); |
...
DoubleValueMetric doubleMetric(String name, @Nullable String desc); |
...
...
<T> ObjectValueMetric<T> objectMetric(String name, |
...
Class<T> type, @Nullable String desc); |
...
void remove(String name); |
...
...
Updatable metric interfaces list
To the package "org.apache.ignite.metric" we add:
- BooleanValueMetric
- IntValueMetric
- LongValueMetric
- LongSumMetric (a long adder)
- DoubleValueMetric
- ObjectValueMetric<T> (an object metric)
Names like "LongMetric" or "ObjectMetric" we already have in the package "org.apache.ignite.spi.metric".
Examples of updatable metrics
Code Block |
---|
|
package org.apache.ignite.metric; |
...
...
interface ObjectValueMetric<T> extends ObjectMetric<T> {
void value(T value); |
...
...
public interface DoubleValueMetric extends DoubleMetric { |
...
...
void value(double value); |
...
...
public interface LongSumMetric extends LongMetric { |
...
...
...
...
...
public interface LongValueMetric extends LongSumMetric { |
...
...
API alternative: single minimal facade
Instead of the interfeces set above, we could bring only a minimal metric management interface.
package org.apache.ignite;
...
Code examples
Expand |
---|
title | Custom metric within a service |
---|
|
/** */ | public static final class TestCustomMetricsService implements TestService { |
/** */ @IgniteInstanceResource private Ignite ignite; /** */ @ServiceContextResource private ServiceContext ctx; /** */ private AtomicReference<UUID> remoteId; /** */ private final AtomicInteger metricValue = new AtomicInteger(); /** {@inheritDoc} */ @Override public void init() throws Exception { remoteId = new AtomicReference<>(); // Registers metric @IgniteInstanceResource
private Ignite ignite;
@ServiceContextResource
private ServiceContext ctx;
private AtomicReference<UUID> remoteId;
private final AtomicInteger metricValue = new AtomicInteger();
@Override public void init() throws Exception {
remoteId = new AtomicReference<>();
// Registers metric "custom.service.svc.filteredInvocation" |
ignite.metrics().customRegistry(regName(ctx.name())).gauge("filteredInvocation", metricValue::get, "Counter of speceific service invocation."); |
// Registers metric "custom.service.svc.loaded" |
ignite.metrics().customRegistry(regName(ctx.name())).gauge("loaded", () -> metricValue.get() >= 100, "Load flag."); |
// Registers metric "custom.service.svc.remote.classId" |
ignite.metrics().customRegistry(regName(ctx.name())).gauge("remote.classId", | () -> remoteId.get(), UUID.class, "Remote system class id."); |
/** {@inheritDoc} */ @Override public void @Override public void cancel() { |
ignite.metrics().customRegistry(regName(ctx.name())).remove(COUNTER_METRIC_NAME); |
/** {@inheritDoc} */ @Override public void @Override public void refresh() { |
/** */ @Override public void @Override public void invoke(int param) { |
remoteId.compareAndSet(null, UUID.randomUUID()); |
// Updates metric sometimes. |
if (!ctx.isCancelled() && param % 10 == 0) |
metricValue.set(param / 10); |
/** */ private static String private static String regName(String svcName) { |
return "service." + svcName; |
|
Expand |
---|
title | Custom metrics within a computation |
---|
|
/** */ | private static final class TestCustomMetricsComputeTask extends ComputeTaskAdapter<Void, Long> { |
/** */ private static final class TestComputeJob extends ComputeJobAdapter { /** Ignite instance. */ @IgniteInstanceResource private Ignite ignite; /** {@inheritDoc} */ @Override public Long execute() throws IgniteException { long val = 0; // Some job limit. long limit = 300 +
private static final class TestComputeJob extends ComputeJobAdapter {
@IgniteInstanceResource
private Ignite ignite;
@Override public Long execute() throws IgniteException {
long val = 0;
// Some job limit.
long limit = 300 + ThreadLocalRandom.current().nextLong(700); |
// Registers metric "custom.task.test.current" |
LongValueMetric metricCur = ignite.metrics().customRegistry("task.test").longMetric("current", null); | // Registers metric "custom.task.test.total.sum" |
LongSumMetric metricTotal = ignite.metrics().customRegistry("task.test").longAdderMetric("total.sum", null); | // Registers metric "custom.task.test.ticks" |
LongSumMetric metricTicks = ignite.metrics().customRegistry("task.test").longAdderMetric("ticks", null); |
while (!isCancelled() && val < limit) { |
U.sleep(ThreadLocalRandom.current().nextInt(50)); |
catch (IgniteInterruptedCheckedException ignored) { |
long increment = ThreadLocalRandom.current().nextLong(100); |
return isCancelled() ? 0 : val; |
|
Further Steps
We already have implementations of more complex and useful metrics. We could also store custom metrics. Thus, the development stages might be:
- An API to expose internals read-only internal metrics was already suggested. Might be joined with the custom metrics. By the methods like "findRegistry" we can also return also read-only internal metrics.
- Extending the initial API with more complex metrics like Histogram or HitRate.
- Introduce a permission for custom metric management.
- Storing registered custom metrics.
- Allowing to change settings of configurable custom metrics like histograms.
...
- IEP-35 Monitoring & Profiling
- New Metric System
- Ticket of a public metric API
- IEP-116 : Ignite 3 metric
Discussion Links
https://lists.apache.org/thread/9zs988jbw3jc6fzj6zhz0t5g8bl3lx5t
Tickets