...
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.
The most important reason to provide custom is probably the convenience of collecting of desired metrics using one platform, the same client, through the same API. This feature can simplify user application.
Famous databases with custom metrics: Oracle DB , Postgres , Oracle Coherence , MS SQL Server , IBM DB2
Examples of custom metric usages:
- Some metrics may not be provided yet and user registers own.
- Metrics for load balancing.
- Custom conflict resolver successes/fails.
- Various metrics of business processes or related to user application.
- Administration metrics.
- Security-related metrics.
Examples of databases with custom metrics
Oracle DB , Postgres , Oracle Coherence , MS SQL Server , IBM DB2
Implementation proposal
...
- Custom Metrics
...
- based on
...
- the New Metric System.
...
- Allow to register essential ready-to-use metrics like LongMetric which can be exported by a
...
- providing metric exporter.
- User can
...
- add own metrics
...
- into different registries.
- Custom metrics are separated from the internals
...
- by registry name prefix "custom." (lower cased).
...
- Management of custom metrics
...
- might require a permission.
Risks and Assumptions
- Custom metrics affect performance and take additional resources.
- We should document examples of effecient metrics implementations like LongAdder.
API
1. New metrics facade to Ignite:
In future, we might want to provide an internal API to obtain metrics like this. It could be quite different and we should split them.
- 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
The APIs are supposed to be experimental.
Interfaces for existing metrics
...
org.apache.ignite.metric; |
...
public interface IgniteMetrics |
...
extends Iterable<ReadOnlyMetricRegistry> {
MetricRegistry customRegistry(String registryName);
@Nullable ReadOnlyMetricRegistry findRegistry(String registryName);
|
...
...
...
removeCustomRegistry(String registryName); |
...
Names like "LongMetric" or "ObjectMetric" we already have in the package "org.apache.ignite.spi.metric".
}
2. New interface IgniteCustomMetrics
To keep custom metrics detached from the internals, we should either automaticall add a prefix to custom name or throw an exception if provided registry name doesn't start with that prefix.
...
...
.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, Supplier<T> supplier, 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);
void reset();
} |
Examples of updatable metrics
Code Block |
---|
|
package org.apache.ignite.metric;
public interface ObjectValueMetric<T> extends ObjectMetric<T> {
void value(T value);
}
public interface DoubleValueMetric extends DoubleMetric {
void add(double value);
void value(double value);
}
public interface LongSumMetric extends LongMetric {
void add(long value);
void increment();
void decrement();
}
public interface LongValueMetric extends LongSumMetric {
void value(long value);
} |
Code examples
Expand |
---|
title | Custom metric within a service |
---|
|
Code Block |
---|
| 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();
@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.");
}
@Override public void cancel() {
refresh();
ignite.metrics().customRegistry(regName(ctx.name())).remove(COUNTER_METRIC_NAME);
}
@Override public void refresh() {
metricValue.set(0);
remoteId.set(null);
}
@Override public void invoke(int param) {
if (ctx.isCancelled())
return;
remoteId.compareAndSet(null, UUID.randomUUID());
// Updates metric sometimes.
if (!ctx.isCancelled() && param % 10 == 0)
metricValue.set(param / 10);
}
private static String regName(String svcName) {
return "service." + svcName;
}
} |
|
Expand |
---|
title | Custom metrics within a computation |
---|
|
Code Block |
---|
| private static final class TestCustomMetricsComputeTask extends ComputeTaskAdapter<Void, Long> {
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) {
// Does some job.
try {
U.sleep(ThreadLocalRandom.current().nextInt(50));
}
catch (IgniteInterruptedCheckedException ignored) {
//No op.
}
long increment = ThreadLocalRandom.current().nextLong(100);
val += increment;
metricTicks.increment()
}
metricCur.value(val);
metricTotal.add(val);
return isCancelled() ? 0 : val;
}
}
} |
|
Risks and Assumptions
- Custom metrics can affect the performance.
- There could be race conditions on metric registrations.
- We do not provide varuous useful metric implementatons.
- Custom metrics aren't stored and require re-registration after node restart. At least at the first phase.
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 read-only internal metrics.
- Extending the initial
- Implementation of the initial API.
- Extending this API with metrics like Histogram , or HitRate, LongAdder.
- Introduce a permission of 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