All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.rocketmq.shaded.io.opentelemetry.sdk.metrics.internal.aggregator.AggregatorHandle Maven / Gradle / Ivy

There is a newer version: 5.0.7
Show newest version
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.apache.rocketmq.shaded.io.opentelemetry.sdk.metrics.internal.aggregator;

import org.apache.rocketmq.shaded.io.opentelemetry.api.common.Attributes;
import org.apache.rocketmq.shaded.io.opentelemetry.context.Context;
import org.apache.rocketmq.shaded.io.opentelemetry.sdk.metrics.data.ExemplarData;
import org.apache.rocketmq.shaded.io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarReservoir;
import org.apache.rocketmq.shaded.io.opentelemetry.sdk.metrics.internal.state.BoundStorageHandle;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * Aggregator represents the abstract class that is used for synchronous instruments. It must be
 * thread-safe and avoid locking when possible, because values are recorded synchronously on the
 * calling thread.
 *
 * 

An {@link AggregatorHandle} must be created for every unique {@code LabelSet} recorded, and * can be referenced by the bound instruments. * *

It atomically counts the number of references (usages) while also keeping a state of * mapped/unmapped into an external map. It uses an atomic value where the least significant bit is * used to keep the state of mapping ('1' is used for unmapped and '0' is for mapped) and the rest * of the bits are used for reference (usage) counting. * *

This class is internal and is hence not for public use. Its APIs are unstable and can change * at any time. */ @ThreadSafe public abstract class AggregatorHandle implements BoundStorageHandle { // Atomically counts the number of references (usages) while also keeping a state of // mapped/unmapped into a registry map. private final AtomicLong refCountMapped; // Note: This is not 100% thread-safe. There is a race condition where recordings can // be made in the moment between the reset and the setting of this field's value. In those // cases, it is possible that a recording could be missed in a given recording interval, but // it should be picked up in the next, assuming that more recordings are being made. private volatile boolean hasRecordings = false; // A reservoir of sampled exemplars for this time period. private final ExemplarReservoir exemplarReservoir; protected AggregatorHandle(ExemplarReservoir exemplarReservoir) { // Start with this binding already bound. this.refCountMapped = new AtomicLong(2); this.exemplarReservoir = exemplarReservoir; } /** * Acquires this {@code Aggregator} for use. Returns {@code true} if the entry is still mapped and * increases the reference usages, if unmapped returns {@code false}. * * @return {@code true} if successful. */ public final boolean acquire() { // Every reference adds/removes 2 instead of 1 to avoid changing the mapping bit. return (refCountMapped.addAndGet(2L) & 1L) == 0; } /** Release this {@code Aggregator}. It decreases the reference usage. */ @Override public final void release() { // Every reference adds/removes 2 instead of 1 to avoid changing the mapping bit. refCountMapped.getAndAdd(-2L); } /** * Flips the mapped bit to "unmapped" state and returns true if both of the following conditions * are true upon entry to this function: 1) There are no active references; 2) The mapped bit is * in "mapped" state; otherwise no changes are done to mapped bit and false is returned. * * @return {@code true} if successful. */ public final boolean tryUnmap() { if (refCountMapped.get() != 0) { // Still references (usages) to this bound or already unmapped. return false; } return refCountMapped.compareAndSet(0L, 1L); } /** * Returns the current value into as {@link T} and resets the current value in this {@code * Aggregator}. */ @Nullable public final T accumulateThenReset(Attributes attributes) { if (!hasRecordings) { return null; } hasRecordings = false; return doAccumulateThenReset(exemplarReservoir.collectAndReset(attributes)); } /** Implementation of the {@code accumulateThenReset}. */ protected abstract T doAccumulateThenReset(List exemplars); @Override public final void recordLong(long value, Attributes attributes, Context context) { exemplarReservoir.offerLongMeasurement(value, attributes, context); recordLong(value); } /** * Updates the current aggregator with a newly recorded {@code long} value. * *

Visible for Testing * * @param value the new {@code long} value to be added. */ public final void recordLong(long value) { doRecordLong(value); hasRecordings = true; } /** * Concrete Aggregator instances should implement this method in order support recordings of long * values. */ protected void doRecordLong(long value) { throw new UnsupportedOperationException( "This aggregator does not support recording long values."); } @Override public final void recordDouble(double value, Attributes attributes, Context context) { exemplarReservoir.offerDoubleMeasurement(value, attributes, context); recordDouble(value); } /** * Updates the current aggregator with a newly recorded {@code double} value. * *

Visible for Testing * * @param value the new {@code double} value to be added. */ public final void recordDouble(double value) { doRecordDouble(value); hasRecordings = true; } /** * Concrete Aggregator instances should implement this method in order support recordings of * double values. */ protected void doRecordDouble(double value) { throw new UnsupportedOperationException( "This aggregator does not support recording double values."); } }