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

io.zeebe.broker.clustering.raft.RaftService Maven / Gradle / Ivy

/*
 * Zeebe Broker Core
 * Copyright © 2017 camunda services GmbH ([email protected])
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
package io.zeebe.broker.clustering.raft;

import static io.zeebe.broker.clustering.ClusterServiceNames.CLUSTER_MANAGER_SERVICE;
import static io.zeebe.broker.logstreams.LogStreamServiceNames.logStreamServiceName;

import java.util.List;

import io.zeebe.broker.Loggers;
import io.zeebe.broker.clustering.management.OnOpenLogStreamListener;
import io.zeebe.broker.logstreams.LogStreamService;
import io.zeebe.broker.logstreams.LogStreamServiceNames;
import io.zeebe.logstreams.log.LogStream;
import io.zeebe.protocol.Protocol;
import io.zeebe.raft.*;
import io.zeebe.raft.controller.MemberReplicateLogController;
import io.zeebe.raft.state.RaftState;
import io.zeebe.servicecontainer.*;
import io.zeebe.transport.*;
import io.zeebe.util.buffer.BufferUtil;
import io.zeebe.util.sched.Actor;
import io.zeebe.util.sched.ActorScheduler;
import io.zeebe.util.sched.channel.OneToOneRingBufferChannel;
import io.zeebe.util.sched.future.ActorFuture;
import io.zeebe.util.sched.future.CompletableActorFuture;
import org.agrona.DirectBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import org.agrona.concurrent.ringbuffer.RingBufferDescriptor;
import org.slf4j.Logger;

public class RaftService extends Actor implements Service, RaftStateListener
{
    private static final Logger LOG = Loggers.SERVICES_LOGGER;

    private final RaftConfiguration configuration;
    private final SocketAddress socketAddress;
    private final LogStream logStream;
    private final List members;
    private final RaftPersistentStorage persistentStorage;
    private final RaftStateListener raftStateListener;
    private final ServiceName logStreamServiceName;
    private final OnOpenLogStreamListener onOpenLogStreamListener;
    private final ServiceName raftServiceName;

    private Injector clientTransportInjector = new Injector<>();
    private ActorScheduler actorScheduler;
    private Raft raft;

    private CompletableActorFuture raftServiceCloseFuture;
    private CompletableActorFuture raftServiceOpenFuture;
    private ServiceStartContext startContext;
    private RaftState currentRaftState;

    public RaftService(final RaftConfiguration configuration, final SocketAddress socketAddress, final LogStream logStream,
                       final List members, final RaftPersistentStorage persistentStorage,
                       RaftStateListener raftStateListener, OnOpenLogStreamListener onOpenLogStreamListener,
                       ServiceName raftServiceName)
    {
        this.configuration = configuration;
        this.socketAddress = socketAddress;
        this.logStream = logStream;
        this.members = members;
        this.persistentStorage = persistentStorage;
        this.raftStateListener = raftStateListener;
        this.logStreamServiceName = logStreamServiceName(logStream.getLogName());
        this.onOpenLogStreamListener = onOpenLogStreamListener;
        this.raftServiceName = raftServiceName;
    }

    @Override
    protected void onActorStarted()
    {
        actor.runOnCompletion(logStream.openAsync(), ((aVoid, throwable) ->
        {
            if (throwable == null)
            {
                final ClientTransport clientTransport = clientTransportInjector.getValue();

                final OneToOneRingBufferChannel messageBuffer = new OneToOneRingBufferChannel(new UnsafeBuffer(new byte[(MemberReplicateLogController.REMOTE_BUFFER_SIZE) + RingBufferDescriptor.TRAILER_LENGTH]));

                raft = new Raft(
                                actorScheduler,
                                configuration,
                                socketAddress,
                                logStream,
                                clientTransport,
                                persistentStorage,
                                messageBuffer,
                                raftStateListener,
                                RaftService.this);

                raft.addMembers(members);
                actorScheduler.submitActor(raft);

                raftServiceOpenFuture.complete(null);
            }
            else
            {
                raftServiceOpenFuture.completeExceptionally(throwable);
                Loggers.CLUSTERING_LOGGER.debug("Failed to open log stream.");
            }
        }));
    }

    @Override
    public void start(final ServiceStartContext startContext)
    {
        this.actorScheduler = startContext.getScheduler();

        raftServiceOpenFuture = new CompletableActorFuture<>();
        actorScheduler.submitActor(this);

        this.startContext = startContext;
        this.startContext.async(raftServiceOpenFuture);
    }

    @Override
    protected void onActorClosing()
    {
    }

    @Override
    public void stop(final ServiceStopContext stopContext)
    {
        raftServiceCloseFuture = new CompletableActorFuture<>();

        actor.call(() ->
        {
            actor.runOnCompletion(raft.close(), (v1, t1) ->
            {
                actor.runOnCompletion(logStream.closeAsync(), ((v2, t2) ->
                {
                    if (t1 != null)
                    {
                        raftServiceCloseFuture.completeExceptionally(t1);
                    }
                    else if (t2 != null)
                    {
                        raftServiceCloseFuture.completeExceptionally(t2);
                    }
                    else
                    {
                        raftServiceCloseFuture.complete(null);
                    }
                    actor.close();
                }));
            });
        });
        stopContext.async(raftServiceCloseFuture);
    }

    @Override
    public Raft get()
    {
        return raft;
    }

    public Injector getClientTransportInjector()
    {
        return clientTransportInjector;
    }

    @Override
    public void onStateChange(int partitionId, DirectBuffer topicName, SocketAddress socketAddress, RaftState raftState)
    {
        actor.call(() ->
        {
            currentRaftState = raftState;

            if (currentRaftState == RaftState.LEADER)
            {
                Loggers.CLUSTERING_LOGGER.debug("Start log stream...topic {}", BufferUtil.bufferAsString(raft.getLogStream().getTopicName()));
                final LogStream logStream = raft.getLogStream();
                final LogStreamService service = new LogStreamService(logStream);

                final ServiceName streamGroup = Protocol.SYSTEM_TOPIC_BUF.equals(logStream.getTopicName()) ?
                    LogStreamServiceNames.SYSTEM_STREAM_GROUP :
                    LogStreamServiceNames.WORKFLOW_STREAM_GROUP;

                final ActorFuture future =
                    startContext
                        .createService(logStreamServiceName, service)
                        .dependency(CLUSTER_MANAGER_SERVICE)
                        .dependency(raftServiceName)
                        .group(streamGroup)
                        .install();

                actor.runOnCompletion(future, (v, throwable) ->
                {
                    if (throwable == null)
                    {
                        actor.submit(() ->
                        {
                            onOpenLogStreamListener.onOpenLogStreamService(raft.getLogStream());
                        });
                    }
                    else
                    {
                        LOG.error("Failed to install log stream service '{}'", logStreamServiceName);
                    }

                });
            }
            else if (currentRaftState == RaftState.FOLLOWER &&
                     startContext.hasService(logStreamServiceName))
            {
                startContext.removeService(logStreamServiceName);
            }

        });
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy