Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

IDIEP-114
Author
Sponsor
Created
Status

Status
colourGrey
titleDRAFT


Table of Contents

Motivation

...

  • 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

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.

...

Interfaces for existing metrics

Code Block
languagejava
package org.apache.ignite.metric;

public interface IgniteMetrics extends Iterable<ReadOnlyMetricRegistry> {
    MetricRegistry customRegistry(String registryName);
    @Nullable ReadOnlyMetricRegistry findRegistry(String registryName);
    void removeCustomRegistry(String registryName);
}

...

Code Block
languagejava
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);
}

API alternative: single minimal facade

Instead of the interfeces set above, we could bring only a minimal metric management interface.

Code Block
languagejava
package org.apache.ignite;

public interface IgniteMetrics extends Iterable<ReadOnlyMetricRegistry> {
    LongConsumer longMetric(String registryName, String metricName, @Nullable String description);
    DoubleConsumer doubleMetric(String registryName, String metricName, @Nullable String description);
    IntConsumer booleanMetric(String registryName, String metricName, @Nullable String description);
    void longMetric(String registryName, String metricName, LongSupplier supplier, @Nullable String description);
    void doubleMetric(String registryName, String metricName, DoubleSupplier supplier, @Nullable String description);
    void intMetric(String registryName, String metricName, BooleanSupplier supplier, @Nullable String description);
    void removeCustomMetric(String registryName, String metricName);
    void removeCustomRegistry(String registryName);
    @Nullable ReadOnlyMetricRegistry findRegistry(String registryName);
}

Code examples

Expand
titleCustom 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 "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 cancel() {
        refresh();

        ignite.metrics().customRegistry(regName(ctx.name())).remove(COUNTER_METRIC_NAME);
    }

    /** {@inheritDoc} */
    @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;
    }
}

...