/* * Copyright 2014-2020 Real Logic Limited. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.aeron.status; import org.agrona.BitUtil; import org.agrona.DirectBuffer; import org.agrona.MutableDirectBuffer; import org.agrona.concurrent.status.AtomicCounter; import org.agrona.concurrent.status.CountersManager; import org.agrona.concurrent.status.CountersReader; import static org.agrona.BitUtil.SIZE_OF_INT; import static org.agrona.BitUtil.SIZE_OF_LONG; import static org.agrona.concurrent.status.CountersReader.*; /** * Allocate a counter for tracking the last heartbeat of an entity with a given registration id. */ public class HeartbeatTimestamp { /** * Type id of an Aeron client heartbeat. */ public static final int CLIENT_HEARTBEAT_TYPE_ID = 11; /** * Offset in the key meta data for the registration id of the counter. */ public static final int REGISTRATION_ID_OFFSET = 0; /** * Allocate a counter for tracking the last heartbeat of an entity. * * @param tempBuffer to be used for labels and key. * @param name of the counter for the label. * @param typeId of the counter for classification. * @param countersManager from which to allocated the underlying storage. * @param registrationId to be associated with the counter. * @return a new {@link AtomicCounter} for tracking the last heartbeat. */ public static AtomicCounter allocate( final MutableDirectBuffer tempBuffer, final String name, final int typeId, final CountersManager countersManager, final long registrationId) { return new AtomicCounter( countersManager.valuesBuffer(), allocateCounterId(tempBuffer, name, typeId, countersManager, registrationId), countersManager); } /** * Allocate a counter id for tracking the last heartbeat of an entity. * * @param tempBuffer to be used for labels and key. * @param name of the counter for the label. * @param typeId of the counter for classification. * @param countersManager from which to allocated the underlying storage. * @param registrationId to be associated with the counter. * @return the counter id to be used. */ public static int allocateCounterId( final MutableDirectBuffer tempBuffer, final String name, final int typeId, final CountersManager countersManager, final long registrationId) { tempBuffer.putLong(REGISTRATION_ID_OFFSET, registrationId); final int keyLength = REGISTRATION_ID_OFFSET + SIZE_OF_LONG; final int labelOffset = BitUtil.align(keyLength, SIZE_OF_INT); int labelLength = 0; labelLength += tempBuffer.putStringWithoutLengthAscii(labelOffset + labelLength, name); labelLength += tempBuffer.putStringWithoutLengthAscii(labelOffset + labelLength, ": "); labelLength += tempBuffer.putLongAscii(labelOffset + labelLength, registrationId); return countersManager.allocate( typeId, tempBuffer, 0, keyLength, tempBuffer, labelOffset, labelLength); } /** * Find the active counter id for a heartbeat timestamp. * * @param countersReader to search within. * @param counterTypeId to match on. * @param registrationId for the active client. * @return the counter id if found otherwise {@link CountersReader#NULL_COUNTER_ID}. */ public static int findCounterIdByRegistrationId( final CountersReader countersReader, final int counterTypeId, final long registrationId) { final DirectBuffer buffer = countersReader.metaDataBuffer(); for (int i = 0, size = countersReader.maxCounterId(); i < size; i++) { if (countersReader.getCounterState(i) == RECORD_ALLOCATED && countersReader.getCounterTypeId(i) == counterTypeId) { final int recordOffset = CountersReader.metaDataOffset(i); if (buffer.getLong(recordOffset + KEY_OFFSET + REGISTRATION_ID_OFFSET) == registrationId) { return i; } } } return NULL_COUNTER_ID; } /** * Is the counter active for usage? Checks to see if reclaimed or reused and matches registration id. * * @param countersReader to search within. * @param counterId to test. * @param counterTypeId to validate type. * @param registrationId for the entity. * @return true if still valid otherwise false. */ public static boolean isActive( final CountersReader countersReader, final int counterId, final int counterTypeId, final long registrationId) { final DirectBuffer buffer = countersReader.metaDataBuffer(); final int recordOffset = CountersReader.metaDataOffset(counterId); return countersReader.getCounterTypeId(counterId) == counterTypeId && buffer.getLong(recordOffset + KEY_OFFSET + REGISTRATION_ID_OFFSET) == registrationId && countersReader.getCounterState(counterId) == RECORD_ALLOCATED; } }