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

io.reacted.drivers.channels.replay.ReplayLocalDriver Maven / Gradle / Ivy

There is a newer version: 2.1.4
Show newest version
/*
 * Copyright (c) 2020 ,  [ [email protected] ]
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

package io.reacted.drivers.channels.replay;

import io.reacted.core.config.ChannelId;
import io.reacted.core.drivers.system.LocalDriver;
import io.reacted.core.messages.Message;
import io.reacted.core.messages.reactors.DeliveryStatus;
import io.reacted.core.messages.reactors.DeliveryStatusUpdate;
import io.reacted.core.messages.reactors.EventExecutionAttempt;
import io.reacted.core.reactors.ReActorId;
import io.reacted.core.reactorsystem.ReActorContext;
import io.reacted.core.reactorsystem.ReActorSystem;
import io.reacted.drivers.channels.chroniclequeue.CQDriverConfig;
import io.reacted.patterns.NonNullByDefault;
import io.reacted.patterns.Try;
import io.reacted.patterns.UnChecked;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
import net.openhft.chronicle.queue.ChronicleQueue;
import net.openhft.chronicle.queue.ExcerptTailer;
import net.openhft.chronicle.threads.Pauser;
import net.openhft.chronicle.wire.DocumentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@NonNullByDefault
public class ReplayLocalDriver extends LocalDriver {
    private static final Logger LOGGER = LoggerFactory.getLogger(ReplayLocalDriver.class);
    private final Set spawnedReActors;
    @Nullable
    private ChronicleQueue chronicle;
    @Nullable
    private ReActorSystem replayedActorSystem;

    public ReplayLocalDriver(CQDriverConfig driverConfig) {
        super(driverConfig);
        this.spawnedReActors = ConcurrentHashMap.newKeySet();
    }

    @Override
    public void initDriverLoop(ReActorSystem replayedActorSystem) {
        this.chronicle = ChronicleQueue.singleBuilder(getDriverConfig().getChronicleFilesDir()).build();
        this.replayedActorSystem = replayedActorSystem;
    }

    @Override
    public UnChecked.CheckedRunnable getDriverLoop() {
        return () -> replayerMainLoop(Objects.requireNonNull(replayedActorSystem), Objects.requireNonNull(chronicle));
    }

    @Override
    public ChannelId getChannelId() {
        return ChannelId.ChannelType.REPLAY_CHRONICLE_QUEUE.forChannelName(getDriverConfig().getChannelName());
    }

    @Override
    public Properties getChannelProperties() { return new Properties(); }

    @Override
    public DeliveryStatus sendMessage(ReActorContext destination, Message message) {
        if (!(message.getPayload() instanceof DeliveryStatusUpdate)) {
            spawnedReActors.add(message.getDestination().getReActorId());
            spawnedReActors.add(message.getSender().getReActorId());
        }
        return DeliveryStatus.SENT;
    }

    @Override
    public CompletionStage sendAsyncMessage(ReActorContext destination, Message message) {
        return CompletableFuture.completedFuture(sendMessage(destination, message));
    }

    @Override
    public CompletionStage> cleanDriverLoop() {
        return CompletableFuture.completedFuture(Try.ofRunnable(() -> Objects.requireNonNull(chronicle).close()));
    }

    private void replayerMainLoop(ReActorSystem localReActorSystem, ChronicleQueue chronicle) {
        Map emptyMap = new HashMap<>();
        ExcerptTailer chronicleReader = chronicle.createTailer();
        Map> dstToMessageBySeqNum = new HashMap<>();
        Pauser pauser = Pauser.balanced();

        while (!Thread.currentThread().isInterrupted() && !chronicle.isClosed()) {

            try(DocumentContext documentContext = chronicleReader.readingDocument()) {
                if (!documentContext.isPresent()) {
                    pauser.pause();
                    continue;
                }
                var nextMessage = documentContext.wire().read().object(Message.class);

                if (nextMessage == null) {
                    pauser.pause();
                    continue;
                }
                pauser.reset();

                if (!isForLocalReActorSystem(localReActorSystem, nextMessage)) {
                    continue;
                }
                Serializable payload = nextMessage.getPayload();

                if (!(payload instanceof EventExecutionAttempt executionAttempt)) {
                    dstToMessageBySeqNum.computeIfAbsent(nextMessage.getDestination().getReActorId(),
                                                         reActorId -> new HashMap<>())
                                        .put(nextMessage.getSequenceNumber(), nextMessage);
                    continue;
                }
                while (!isTargetReactorAlreadySpawned(nextMessage)) {
                    pauser.pause();
                }
                pauser.reset();

                var message = dstToMessageBySeqNum.getOrDefault(executionAttempt.getReActorId(), emptyMap)
                                                           .remove(executionAttempt.getMsgSeqNum());
                ReActorContext destinationCtx = localReActorSystem.getReActorCtx(executionAttempt.getReActorId());

                if (destinationCtx == null || message == null) {
                    LOGGER.error("Unable to delivery message {} for ReActor {}",
                            message, executionAttempt.getReActorId(), new IllegalStateException());
                } else if (syncForwardMessageToLocalActor(destinationCtx, message).isNotDelivered()) {
                        LOGGER.error("Unable to delivery message {} for ReActor {}",
                                message, executionAttempt.getReActorId());
                }
            } catch (Exception anyError) {
                LOGGER.error("Error reading message from CQ", anyError);
                break;
            }
        }
    }
    private boolean isForLocalReActorSystem(ReActorSystem replayedAs, Message newMessage) {
        return newMessage.getDestination().getReActorSystemRef().equals(replayedAs.getLoopback());
    }

    private boolean isTargetReactorAlreadySpawned(Message newMessage) {
        //Every spawned reactor receives an init message, so there must be a send for it
        return spawnedReActors.contains(newMessage.getSender().getReActorId());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy