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

io.aeron.cluster.PendingServiceMessageTracker Maven / Gradle / Ivy

There is a newer version: 1.48.0
Show newest version
/*
 * Copyright 2014-2024 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.cluster;

import io.aeron.Counter;
import io.aeron.cluster.client.ClusterException;
import io.aeron.cluster.codecs.MessageHeaderDecoder;
import io.aeron.cluster.codecs.SessionMessageHeaderDecoder;
import io.aeron.cluster.codecs.SessionMessageHeaderEncoder;
import io.aeron.cluster.service.ClusterClock;
import io.aeron.exceptions.AeronException;
import org.agrona.DirectBuffer;
import org.agrona.ExpandableRingBuffer;
import org.agrona.MutableDirectBuffer;
import org.agrona.collections.MutableInteger;

import static io.aeron.Aeron.NULL_VALUE;
import static io.aeron.cluster.client.AeronCluster.SESSION_HEADER_LENGTH;

final class PendingServiceMessageTracker
{
    private static final int SERVICE_MESSAGE_LIMIT = 20;

    private final int serviceId;
    private int pendingMessageHeadOffset = 0;
    private int uncommittedMessages = 0;
    private long nextServiceSessionId;
    private long logServiceSessionId;
    private long leadershipTermId = NULL_VALUE;

    private final Counter commitPosition;
    private final LogPublisher logPublisher;
    private final ClusterClock clusterClock;
    private final ExpandableRingBuffer pendingMessages = new ExpandableRingBuffer();
    private final ExpandableRingBuffer.MessageConsumer messageAppender = this::messageAppender;
    private final ExpandableRingBuffer.MessageConsumer leaderMessageSweeper = this::leaderMessageSweeper;
    private final ExpandableRingBuffer.MessageConsumer followerMessageSweeper = this::followerMessageSweeper;

    PendingServiceMessageTracker(
        final int serviceId,
        final Counter commitPosition,
        final LogPublisher logPublisher,
        final ClusterClock clusterClock)
    {
        this.serviceId = serviceId;
        this.commitPosition = commitPosition;
        this.logPublisher = logPublisher;
        this.clusterClock = clusterClock;

        logServiceSessionId = ((long)serviceId << 56) | Long.MIN_VALUE;
        nextServiceSessionId = logServiceSessionId + 1;
    }

    void leadershipTermId(final long leadershipTermId)
    {
        this.leadershipTermId = leadershipTermId;
    }

    int serviceId()
    {
        return serviceId;
    }

    long nextServiceSessionId()
    {
        return nextServiceSessionId;
    }

    long logServiceSessionId()
    {
        return logServiceSessionId;
    }

    void enqueueMessage(final MutableDirectBuffer buffer, final int offset, final int length)
    {
        final long clusterSessionId = nextServiceSessionId++;
        if (clusterSessionId > logServiceSessionId)
        {
            final int headerOffset = offset - SessionMessageHeaderDecoder.BLOCK_LENGTH;
            final int clusterSessionIdOffset =
                headerOffset + SessionMessageHeaderDecoder.clusterSessionIdEncodingOffset();
            final int timestampOffset = headerOffset + SessionMessageHeaderDecoder.timestampEncodingOffset();

            buffer.putLong(clusterSessionIdOffset, clusterSessionId, SessionMessageHeaderDecoder.BYTE_ORDER);
            buffer.putLong(timestampOffset, Long.MAX_VALUE, SessionMessageHeaderDecoder.BYTE_ORDER);
            if (!pendingMessages.append(buffer, offset - SESSION_HEADER_LENGTH, length + SESSION_HEADER_LENGTH))
            {
                throw new ClusterException("pending service message buffer at capacity=" + pendingMessages.size() +
                    " for serviceId=" + serviceId);
            }
        }
    }

    void sweepFollowerMessages(final long clusterSessionId)
    {
        logServiceSessionId = clusterSessionId;
        pendingMessages.consume(followerMessageSweeper, Integer.MAX_VALUE);
    }

    void sweepLeaderMessages()
    {
        if (uncommittedMessages > 0)
        {
            pendingMessageHeadOffset -= pendingMessages.consume(leaderMessageSweeper, Integer.MAX_VALUE);
            pendingMessageHeadOffset = Math.max(pendingMessageHeadOffset, 0);
        }
    }

    void restoreUncommittedMessages()
    {
        if (uncommittedMessages > 0)
        {
            pendingMessages.consume(leaderMessageSweeper, Integer.MAX_VALUE);
            pendingMessages.forEach(PendingServiceMessageTracker::messageReset, Integer.MAX_VALUE);
            uncommittedMessages = 0;
            pendingMessageHeadOffset = 0;
        }
    }

    void appendMessage(final DirectBuffer buffer, final int offset, final int length)
    {
        pendingMessages.append(buffer, offset, length);
    }

    void loadState(final long nextServiceSessionId, final long logServiceSessionId, final int pendingMessageCapacity)
    {
        this.nextServiceSessionId = nextServiceSessionId;
        this.logServiceSessionId = logServiceSessionId;
        pendingMessages.reset(pendingMessageCapacity);
    }

    int poll()
    {
        return pendingMessages.forEach(pendingMessageHeadOffset, messageAppender, SERVICE_MESSAGE_LIMIT);
    }

    int size()
    {
        return pendingMessages.size();
    }

    void verify()
    {
        final MutableInteger messageCount = new MutableInteger();
        final ExpandableRingBuffer.MessageConsumer messageConsumer =
            (buffer, offset, length, headOffset) ->
            {
                messageCount.increment();

                final int headerOffset = offset + MessageHeaderDecoder.ENCODED_LENGTH;
                final int clusterSessionIdOffset =
                    headerOffset + SessionMessageHeaderDecoder.clusterSessionIdEncodingOffset();

                final long clusterSessionId = buffer.getLong(
                    clusterSessionIdOffset, SessionMessageHeaderDecoder.BYTE_ORDER);

                if (clusterSessionId != (logServiceSessionId + messageCount.get()))
                {
                    throw new ClusterException("snapshot has incorrect pending message:" +
                        " serviceId=" + serviceId +
                        " nextServiceSessionId=" + nextServiceSessionId +
                        " logServiceSessionId=" + logServiceSessionId +
                        " clusterSessionId=" + clusterSessionId +
                        " pendingMessageIndex=" + messageCount.get(),
                        AeronException.Category.FATAL);
                }

                return true;
            };

        pendingMessages.forEach(messageConsumer, Integer.MAX_VALUE);

        if (nextServiceSessionId != (logServiceSessionId + messageCount.get() + 1))
        {
            throw new ClusterException("snapshot has incorrect pending message state:" +
                " serviceId=" + serviceId +
                " nextServiceSessionId=" + nextServiceSessionId +
                " logServiceSessionId=" + logServiceSessionId +
                " pendingMessageCount=" + messageCount.get(),
                AeronException.Category.FATAL);
        }
    }

    void reset()
    {
        pendingMessages.forEach(PendingServiceMessageTracker::messageReset, Integer.MAX_VALUE);
    }

    ExpandableRingBuffer pendingMessages()
    {
        return pendingMessages;
    }

    private boolean messageAppender(
        final MutableDirectBuffer buffer, final int offset, final int length, final int headOffset)
    {
        final int headerOffset = offset + MessageHeaderDecoder.ENCODED_LENGTH;
        final int clusterSessionIdOffset = headerOffset + SessionMessageHeaderDecoder.clusterSessionIdEncodingOffset();
        final int timestampOffset = headerOffset + SessionMessageHeaderDecoder.timestampEncodingOffset();
        final long clusterSessionId = buffer.getLong(clusterSessionIdOffset, SessionMessageHeaderDecoder.BYTE_ORDER);

        final long appendPosition = logPublisher.appendMessage(
            leadershipTermId,
            clusterSessionId,
            clusterClock.time(),
            buffer,
            offset + SESSION_HEADER_LENGTH,
            length - SESSION_HEADER_LENGTH);

        if (appendPosition > 0)
        {
            ++uncommittedMessages;
            pendingMessageHeadOffset = headOffset;
            buffer.putLong(timestampOffset, appendPosition, SessionMessageHeaderEncoder.BYTE_ORDER);

            return true;
        }

        return false;
    }

    private static boolean messageReset(
        final MutableDirectBuffer buffer, final int offset, final int length, final int headOffset)
    {
        final int timestampOffset = offset +
            MessageHeaderDecoder.ENCODED_LENGTH + SessionMessageHeaderDecoder.timestampEncodingOffset();
        final long appendPosition = buffer.getLong(timestampOffset, SessionMessageHeaderDecoder.BYTE_ORDER);

        if (appendPosition < Long.MAX_VALUE)
        {
            buffer.putLong(timestampOffset, Long.MAX_VALUE, SessionMessageHeaderEncoder.BYTE_ORDER);
            return true;
        }

        return false;
    }

    private boolean leaderMessageSweeper(
        final MutableDirectBuffer buffer, final int offset, final int length, final int headOffset)
    {
        final int headerOffset = offset + MessageHeaderDecoder.ENCODED_LENGTH;
        final int clusterSessionIdOffset = headerOffset + SessionMessageHeaderDecoder.clusterSessionIdEncodingOffset();
        final int timestampOffset = headerOffset + SessionMessageHeaderDecoder.timestampEncodingOffset();
        final long appendPosition = buffer.getLong(timestampOffset, SessionMessageHeaderDecoder.BYTE_ORDER);

        if (commitPosition.getWeak() >= appendPosition)
        {
            logServiceSessionId = buffer.getLong(clusterSessionIdOffset, SessionMessageHeaderDecoder.BYTE_ORDER);
            --uncommittedMessages;

            return true;
        }

        return false;
    }

    private boolean followerMessageSweeper(
        final MutableDirectBuffer buffer, final int offset, final int length, final int headOffset)
    {
        final int clusterSessionIdOffset = offset +
            MessageHeaderDecoder.ENCODED_LENGTH + SessionMessageHeaderDecoder.clusterSessionIdEncodingOffset();

        return buffer.getLong(clusterSessionIdOffset, SessionMessageHeaderDecoder.BYTE_ORDER) <= logServiceSessionId;
    }

    static int serviceId(final long clusterSessionId)
    {
        return ((int)(clusterSessionId >>> 56)) & 0x7F;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy