Search in sources :

Example 1 with Procedure

use of akka.japi.Procedure in project controller by opendaylight.

the class ElectionTermImplTest method testUpdateAndPersist.

@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testUpdateAndPersist() throws Exception {
    ElectionTermImpl impl = new ElectionTermImpl(mockPersistence, "test", LOG);
    impl.updateAndPersist(10, "member-1");
    assertEquals("getCurrentTerm", 10, impl.getCurrentTerm());
    assertEquals("getVotedFor", "member-1", impl.getVotedFor());
    ArgumentCaptor<Object> message = ArgumentCaptor.forClass(Object.class);
    ArgumentCaptor<Procedure> procedure = ArgumentCaptor.forClass(Procedure.class);
    verify(mockPersistence).persist(message.capture(), procedure.capture());
    assertEquals("Message type", UpdateElectionTerm.class, message.getValue().getClass());
    UpdateElectionTerm update = (UpdateElectionTerm) message.getValue();
    assertEquals("getCurrentTerm", 10, update.getCurrentTerm());
    assertEquals("getVotedFor", "member-1", update.getVotedFor());
    procedure.getValue().apply(null);
}
Also used : Procedure(akka.japi.Procedure) UpdateElectionTerm(org.opendaylight.controller.cluster.raft.persisted.UpdateElectionTerm) Test(org.junit.Test)

Example 2 with Procedure

use of akka.japi.Procedure in project controller by opendaylight.

the class RaftActorTest method testReplicateWithPersistencePending.

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testReplicateWithPersistencePending() throws Exception {
    final String leaderId = factory.generateActorId("leader-");
    final String followerId = factory.generateActorId("follower-");
    final ActorRef followerActor = factory.createActor(MessageCollectorActor.props());
    DefaultConfigParamsImpl config = new DefaultConfigParamsImpl();
    config.setHeartBeatInterval(new FiniteDuration(1, TimeUnit.DAYS));
    config.setIsolatedLeaderCheckInterval(new FiniteDuration(1, TimeUnit.DAYS));
    DataPersistenceProvider mockPersistenceProvider = mock(DataPersistenceProvider.class);
    doReturn(true).when(mockPersistenceProvider).isRecoveryApplicable();
    TestActorRef<MockRaftActor> leaderActorRef = factory.createTestActor(MockRaftActor.props(leaderId, ImmutableMap.of(followerId, followerActor.path().toString()), config, mockPersistenceProvider), leaderId);
    MockRaftActor leaderActor = leaderActorRef.underlyingActor();
    leaderActor.waitForInitializeBehaviorComplete();
    leaderActor.getRaftActorContext().getTermInformation().update(1, leaderId);
    Leader leader = new Leader(leaderActor.getRaftActorContext());
    leaderActor.setCurrentBehavior(leader);
    leaderActor.persistData(leaderActorRef, new MockIdentifier("1"), new MockRaftActorContext.MockPayload("1"), false);
    ReplicatedLogEntry logEntry = leaderActor.getReplicatedLog().get(0);
    assertNotNull("ReplicatedLogEntry not found", logEntry);
    assertEquals("isPersistencePending", true, logEntry.isPersistencePending());
    assertEquals("getCommitIndex", -1, leaderActor.getRaftActorContext().getCommitIndex());
    leaderActor.onReceiveCommand(new AppendEntriesReply(followerId, 1, true, 0, 1, (short) 0));
    assertEquals("getCommitIndex", -1, leaderActor.getRaftActorContext().getCommitIndex());
    ArgumentCaptor<Procedure> callbackCaptor = ArgumentCaptor.forClass(Procedure.class);
    verify(mockPersistenceProvider).persistAsync(eq(logEntry), callbackCaptor.capture());
    callbackCaptor.getValue().apply(logEntry);
    assertEquals("getCommitIndex", 0, leaderActor.getRaftActorContext().getCommitIndex());
    assertEquals("getLastApplied", 0, leaderActor.getRaftActorContext().getLastApplied());
}
Also used : Leader(org.opendaylight.controller.cluster.raft.behaviors.Leader) ActorRef(akka.actor.ActorRef) TestActorRef(akka.testkit.TestActorRef) FiniteDuration(scala.concurrent.duration.FiniteDuration) MockPayload(org.opendaylight.controller.cluster.raft.MockRaftActorContext.MockPayload) ByteString(com.google.protobuf.ByteString) SimpleReplicatedLogEntry(org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) DataPersistenceProvider(org.opendaylight.controller.cluster.DataPersistenceProvider) Procedure(akka.japi.Procedure) Test(org.junit.Test)

Example 3 with Procedure

use of akka.japi.Procedure in project controller by opendaylight.

the class Follower method processNewEntries.

private boolean processNewEntries(final AppendEntries appendEntries, final ActorRef sender) {
    int numLogEntries = appendEntries.getEntries().size();
    if (numLogEntries == 0) {
        return true;
    }
    log.debug("{}: Number of entries to be appended = {}", logName(), numLogEntries);
    long lastIndex = lastIndex();
    int addEntriesFrom = 0;
    // term), delete the existing entry and all that follow it (ยง5.3)
    if (context.getReplicatedLog().size() > 0) {
        // Find the entry up until the one that is not in the follower's log
        for (int i = 0; i < numLogEntries; i++, addEntriesFrom++) {
            ReplicatedLogEntry matchEntry = appendEntries.getEntries().get(i);
            if (!isLogEntryPresent(matchEntry.getIndex())) {
                // newEntry not found in the log
                break;
            }
            long existingEntryTerm = getLogEntryTerm(matchEntry.getIndex());
            log.debug("{}: matchEntry {} is present: existingEntryTerm: {}", logName(), matchEntry, existingEntryTerm);
            // what the term was so we'll assume it matches.
            if (existingEntryTerm == -1 || existingEntryTerm == matchEntry.getTerm()) {
                continue;
            }
            if (!context.getRaftPolicy().applyModificationToStateBeforeConsensus()) {
                log.info("{}: Removing entries from log starting at {}, commitIndex: {}, lastApplied: {}", logName(), matchEntry.getIndex(), context.getCommitIndex(), context.getLastApplied());
                // been applied to the state yet.
                if (matchEntry.getIndex() <= context.getLastApplied() || !context.getReplicatedLog().removeFromAndPersist(matchEntry.getIndex())) {
                    // Could not remove the entries - this means the matchEntry index must be in the
                    // snapshot and not the log. In this case the prior entries are part of the state
                    // so we must send back a reply to force a snapshot to completely re-sync the
                    // follower's log and state.
                    log.info("{}: Could not remove entries - sending reply to force snapshot", logName());
                    sender.tell(new AppendEntriesReply(context.getId(), currentTerm(), false, lastIndex, lastTerm(), context.getPayloadVersion(), true), actor());
                    return false;
                }
                break;
            } else {
                sender.tell(new AppendEntriesReply(context.getId(), currentTerm(), false, lastIndex, lastTerm(), context.getPayloadVersion(), true), actor());
                return false;
            }
        }
    }
    lastIndex = lastIndex();
    log.debug("{}: After cleanup, lastIndex: {}, entries to be added from: {}", logName(), lastIndex, addEntriesFrom);
    // When persistence successfully completes for each new log entry appended, we need to determine if we
    // should capture a snapshot to compact the persisted log. shouldCaptureSnapshot tracks whether or not
    // one of the log entries has exceeded the log size threshold whereby a snapshot should be taken. However
    // we don't initiate the snapshot at that log entry but rather after the last log entry has been persisted.
    // This is done because subsequent log entries after the one that tripped the threshold may have been
    // applied to the state already, as the persistence callback occurs async, and we want those entries
    // purged from the persisted log as well.
    final AtomicBoolean shouldCaptureSnapshot = new AtomicBoolean(false);
    final Procedure<ReplicatedLogEntry> appendAndPersistCallback = logEntry -> {
        final List<ReplicatedLogEntry> entries = appendEntries.getEntries();
        final ReplicatedLogEntry lastEntryToAppend = entries.get(entries.size() - 1);
        if (shouldCaptureSnapshot.get() && logEntry == lastEntryToAppend) {
            context.getSnapshotManager().capture(context.getReplicatedLog().last(), getReplicatedToAllIndex());
        }
    };
    // Append any new entries not already in the log
    for (int i = addEntriesFrom; i < numLogEntries; i++) {
        ReplicatedLogEntry entry = appendEntries.getEntries().get(i);
        log.debug("{}: Append entry to log {}", logName(), entry.getData());
        context.getReplicatedLog().appendAndPersist(entry, appendAndPersistCallback, false);
        shouldCaptureSnapshot.compareAndSet(false, context.getReplicatedLog().shouldCaptureSnapshot(entry.getIndex()));
        if (entry.getData() instanceof ServerConfigurationPayload) {
            context.updatePeerIds((ServerConfigurationPayload) entry.getData());
        }
    }
    log.debug("{}: Log size is now {}", logName(), context.getReplicatedLog().size());
    return true;
}
Also used : Stopwatch(com.google.common.base.Stopwatch) RaftActorContext(org.opendaylight.controller.cluster.raft.RaftActorContext) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) TimeoutNow(org.opendaylight.controller.cluster.raft.base.messages.TimeoutNow) ActorSelection(akka.actor.ActorSelection) ElectionTimeout(org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout) ArrayList(java.util.ArrayList) RaftRPC(org.opendaylight.controller.cluster.raft.messages.RaftRPC) ApplySnapshot(org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot) ActorRef(akka.actor.ActorRef) Cluster(akka.cluster.Cluster) Member(akka.cluster.Member) MemberStatus(akka.cluster.MemberStatus) Nullable(javax.annotation.Nullable) Address(akka.actor.Address) AppendEntries(org.opendaylight.controller.cluster.raft.messages.AppendEntries) ServerConfigurationPayload(org.opendaylight.controller.cluster.raft.persisted.ServerConfigurationPayload) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) Set(java.util.Set) RaftState(org.opendaylight.controller.cluster.raft.RaftState) IOException(java.io.IOException) RequestVote(org.opendaylight.controller.cluster.raft.messages.RequestVote) RequestVoteReply(org.opendaylight.controller.cluster.raft.messages.RequestVoteReply) TimeUnit(java.util.concurrent.TimeUnit) List(java.util.List) Snapshot(org.opendaylight.controller.cluster.raft.persisted.Snapshot) InstallSnapshotReply(org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply) CurrentClusterState(akka.cluster.ClusterEvent.CurrentClusterState) Optional(java.util.Optional) MessageAssembler(org.opendaylight.controller.cluster.messaging.MessageAssembler) Procedure(akka.japi.Procedure) VisibleForTesting(com.google.common.annotations.VisibleForTesting) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) InstallSnapshot(org.opendaylight.controller.cluster.raft.messages.InstallSnapshot) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ReplicatedLogEntry(org.opendaylight.controller.cluster.raft.ReplicatedLogEntry) ServerConfigurationPayload(org.opendaylight.controller.cluster.raft.persisted.ServerConfigurationPayload) AppendEntriesReply(org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply) ArrayList(java.util.ArrayList) List(java.util.List)

Aggregations

Procedure (akka.japi.Procedure)3 ActorRef (akka.actor.ActorRef)2 Test (org.junit.Test)2 AppendEntriesReply (org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply)2 ActorSelection (akka.actor.ActorSelection)1 Address (akka.actor.Address)1 Cluster (akka.cluster.Cluster)1 CurrentClusterState (akka.cluster.ClusterEvent.CurrentClusterState)1 Member (akka.cluster.Member)1 MemberStatus (akka.cluster.MemberStatus)1 TestActorRef (akka.testkit.TestActorRef)1 VisibleForTesting (com.google.common.annotations.VisibleForTesting)1 Stopwatch (com.google.common.base.Stopwatch)1 ByteString (com.google.protobuf.ByteString)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 List (java.util.List)1 Optional (java.util.Optional)1 Set (java.util.Set)1 TimeUnit (java.util.concurrent.TimeUnit)1