Search in sources :

Example 16 with LogEntry

use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.

the class AppendRequestHandlerTask method innerRun.

@Override
@SuppressWarnings({ "checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity", "checkstyle:methodlength", "checkstyle:nestedifdepth" })
protected // Justification: It is easier to follow the AppendEntriesRPC logic in a single method
void innerRun() {
    if (logger.isFineEnabled()) {
        logger.fine("Received " + req);
    }
    RaftState state = raftNode.state();
    // Reply false if term < currentTerm (§5.1)
    if (req.term() < state.term()) {
        if (logger.isFineEnabled()) {
            logger.warning("Stale " + req + " received in current term: " + state.term());
        }
        raftNode.send(createFailureResponse(state.term()), req.leader());
        return;
    }
    // Transform into follower if a newer term is seen or another node wins the election of the current term
    if (req.term() > state.term() || state.role() != FOLLOWER) {
        // If RPC request or response contains term T > currentTerm: set currentTerm = T, convert to follower (§5.1)
        logger.info("Demoting to FOLLOWER from current role: " + state.role() + ", term: " + state.term() + " to new term: " + req.term() + " and leader: " + req.leader());
        raftNode.toFollower(req.term());
    }
    if (!req.leader().equals(state.leader())) {
        logger.info("Setting leader: " + req.leader());
        raftNode.leader(req.leader());
    }
    RaftLog raftLog = state.log();
    // Verify the last log entry
    if (req.prevLogIndex() > 0) {
        long lastLogIndex = raftLog.lastLogOrSnapshotIndex();
        int lastLogTerm = raftLog.lastLogOrSnapshotTerm();
        int prevLogTerm;
        if (req.prevLogIndex() == lastLogIndex) {
            prevLogTerm = lastLogTerm;
        } else {
            // Reply false if log does not contain an entry at prevLogIndex whose term matches prevLogTerm (§5.3)
            LogEntry prevLog = raftLog.getLogEntry(req.prevLogIndex());
            if (prevLog == null) {
                if (logger.isFineEnabled()) {
                    logger.warning("Failed to get previous log index for " + req + ", last log index: " + lastLogIndex);
                }
                raftNode.send(createFailureResponse(req.term()), req.leader());
                return;
            }
            prevLogTerm = prevLog.term();
        }
        if (req.prevLogTerm() != prevLogTerm) {
            if (logger.isFineEnabled()) {
                logger.warning("Previous log term of " + req + " is different than ours: " + prevLogTerm);
            }
            raftNode.send(createFailureResponse(req.term()), req.leader());
            return;
        }
    }
    int truncatedAppendRequestEntryCount = 0;
    LogEntry[] newEntries = null;
    // Process any new entries
    if (req.entryCount() > 0) {
        // Delete any conflicting entries, skip any duplicates
        long lastLogIndex = raftLog.lastLogOrSnapshotIndex();
        for (int i = 0; i < req.entryCount(); i++) {
            LogEntry reqEntry = req.entries()[i];
            if (reqEntry.index() > lastLogIndex) {
                newEntries = Arrays.copyOfRange(req.entries(), i, req.entryCount());
                break;
            }
            LogEntry localEntry = raftLog.getLogEntry(reqEntry.index());
            assert localEntry != null : "Entry not found on log index: " + reqEntry.index() + " for " + req;
            // delete the existing entry and all that follow it (§5.3)
            if (reqEntry.term() != localEntry.term()) {
                List<LogEntry> truncatedEntries = raftLog.deleteEntriesFrom(reqEntry.index());
                if (logger.isFineEnabled()) {
                    logger.warning("Truncated " + truncatedEntries.size() + " entries from entry index: " + reqEntry.index() + " => " + truncatedEntries);
                } else {
                    logger.warning("Truncated " + truncatedEntries.size() + " entries from entry index: " + reqEntry.index());
                }
                raftNode.invalidateFuturesFrom(reqEntry.index());
                revertPreAppliedRaftGroupCmd(truncatedEntries);
                newEntries = Arrays.copyOfRange(req.entries(), i, req.entryCount());
                raftLog.flush();
                break;
            }
        }
        if (newEntries != null && newEntries.length > 0) {
            if (raftLog.availableCapacity() < newEntries.length) {
                if (logger.isFineEnabled()) {
                    logger.warning("Truncating " + newEntries.length + " entries to " + raftLog.availableCapacity() + " to fit into the available capacity of the Raft log");
                }
                truncatedAppendRequestEntryCount = newEntries.length - raftLog.availableCapacity();
                newEntries = Arrays.copyOf(newEntries, raftLog.availableCapacity());
            }
            // Append any new entries not already in the log
            if (logger.isFineEnabled()) {
                logger.fine("Appending " + newEntries.length + " entries: " + Arrays.toString(newEntries));
            }
            raftLog.appendEntries(newEntries);
            raftLog.flush();
        }
    }
    // I cannot use raftLog.lastLogOrSnapshotIndex() for lastLogIndex because my log may contain
    // some uncommitted entries from the previous leader and those entries will be truncated soon
    // I can only send a response based on how many entries I have appended from this append request
    long lastLogIndex = req.prevLogIndex() + req.entryCount() - truncatedAppendRequestEntryCount;
    long oldCommitIndex = state.commitIndex();
    // Update the commit index
    if (req.leaderCommitIndex() > oldCommitIndex) {
        // If leaderCommit > commitIndex, set commitIndex = min(leaderCommit, index of last new entry)
        long newCommitIndex = min(req.leaderCommitIndex(), lastLogIndex);
        if (logger.isFineEnabled()) {
            logger.fine("Setting commit index: " + newCommitIndex);
        }
        state.commitIndex(newCommitIndex);
    }
    raftNode.updateLastAppendEntriesTimestamp();
    try {
        AppendSuccessResponse resp = new AppendSuccessResponse(localMember(), state.term(), lastLogIndex, req.queryRound());
        raftNode.send(resp, req.leader());
    } finally {
        if (state.commitIndex() > oldCommitIndex) {
            raftNode.applyLogEntries();
        }
        if (newEntries != null) {
            preApplyRaftGroupCmd(newEntries, state.commitIndex());
        }
    }
}
Also used : AppendSuccessResponse(com.hazelcast.cp.internal.raft.impl.dto.AppendSuccessResponse) RaftState(com.hazelcast.cp.internal.raft.impl.state.RaftState) LogEntry(com.hazelcast.cp.internal.raft.impl.log.LogEntry) RaftLog(com.hazelcast.cp.internal.raft.impl.log.RaftLog)

Example 17 with LogEntry

use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.

the class TriggerLeaderElectionHandlerTask method innerRun.

@Override
protected void innerRun() {
    if (logger.isFineEnabled()) {
        logger.fine("Received " + req);
    }
    RaftState state = raftNode.state();
    // with the leader's log.
    if (!(req.term() == state.term() && req.leader().equals(state.leader()))) {
        if (logger.isFineEnabled()) {
            logger.fine("Ignoring " + req + " since term: " + state.term() + " and leader: " + state.leader());
        }
        return;
    }
    // Verify the last log entry
    LogEntry entry = state.log().lastLogOrSnapshotEntry();
    if (!(entry.index() == req.lastLogIndex() && entry.term() == req.lastLogTerm())) {
        if (logger.isFineEnabled()) {
            logger.fine("Could not accept leadership transfer because local Raft log is not same with the current leader. " + "Last log entry: " + entry + ", request: " + req);
        }
        return;
    }
    // I will send a disruptive VoteRequest to bypass leader stickiness
    logger.info("Starting a new leader election since the current leader: " + req.leader() + " in term: " + req.term() + " asked for a leadership transfer!");
    state.leader(null);
    new LeaderElectionTask(raftNode, true).run();
}
Also used : RaftState(com.hazelcast.cp.internal.raft.impl.state.RaftState) LeaderElectionTask(com.hazelcast.cp.internal.raft.impl.task.LeaderElectionTask) LogEntry(com.hazelcast.cp.internal.raft.impl.log.LogEntry)

Example 18 with LogEntry

use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.

the class FencedLockAdvancedTest method testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot.

@Test
public void testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot() throws ExecutionException, InterruptedException {
    long fence = this.lock.lockAndGetFence();
    assertTrue(fence > 0);
    spawn(() -> {
        lock.tryLock(10, MINUTES);
    });
    CPGroupId groupId = this.lock.getGroupId();
    assertTrueEventually(() -> {
        HazelcastInstance leader = getLeaderInstance(instances, groupId);
        LockService service = getNodeEngineImpl(leader).getService(LockService.SERVICE_NAME);
        ResourceRegistry registry = service.getRegistryOrNull(groupId);
        assertNotNull(registry);
        assertFalse(registry.getWaitTimeouts().isEmpty());
    });
    spawn(() -> {
        for (int i = 0; i < LOG_ENTRY_COUNT_TO_SNAPSHOT; i++) {
            lock.isLocked();
        }
    });
    assertTrueEventually(() -> {
        for (HazelcastInstance instance : instances) {
            RaftNodeImpl raftNode = getRaftNode(instance, groupId);
            assertNotNull(raftNode);
            LogEntry snapshotEntry = getSnapshotEntry(raftNode);
            assertTrue(snapshotEntry.index() > 0);
            List<RestoreSnapshotOp> ops = (List<RestoreSnapshotOp>) snapshotEntry.operation();
            for (RestoreSnapshotOp op : ops) {
                if (op.getServiceName().equals(LockService.SERVICE_NAME)) {
                    ResourceRegistry registry = (ResourceRegistry) op.getSnapshot();
                    assertFalse(registry.getWaitTimeouts().isEmpty());
                    return;
                }
            }
            fail();
        }
    });
    HazelcastInstance instanceToShutdown = (instances[0] == proxyInstance) ? instances[1] : instances[0];
    instanceToShutdown.shutdown();
    HazelcastInstance newInstance = factory.newHazelcastInstance(createConfig(groupSize, groupSize));
    newInstance.getCPSubsystem().getCPSubsystemManagementService().promoteToCPMember().toCompletableFuture().get();
    assertTrueEventually(() -> {
        RaftNodeImpl raftNode = getRaftNode(newInstance, groupId);
        assertNotNull(raftNode);
        assertTrue(getSnapshotEntry(raftNode).index() > 0);
        LockService service = getNodeEngineImpl(newInstance).getService(LockService.SERVICE_NAME);
        LockRegistry registry = service.getRegistryOrNull(groupId);
        assertNotNull(registry);
        assertFalse(registry.getWaitTimeouts().isEmpty());
        LockOwnershipState ownership = registry.getLockOwnershipState(objectName);
        assertTrue(ownership.isLocked());
        assertTrue(ownership.getLockCount() > 0);
        assertEquals(fence, ownership.getFence());
    });
}
Also used : RaftNodeImpl(com.hazelcast.cp.internal.raft.impl.RaftNodeImpl) RestoreSnapshotOp(com.hazelcast.cp.internal.raftop.snapshot.RestoreSnapshotOp) ResourceRegistry(com.hazelcast.cp.internal.datastructures.spi.blocking.ResourceRegistry) CPGroupId(com.hazelcast.cp.CPGroupId) HazelcastInstance(com.hazelcast.core.HazelcastInstance) List(java.util.List) LogEntry(com.hazelcast.cp.internal.raft.impl.log.LogEntry) ParallelJVMTest(com.hazelcast.test.annotation.ParallelJVMTest) QuickTest(com.hazelcast.test.annotation.QuickTest) Test(org.junit.Test)

Example 19 with LogEntry

use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.

the class CountDownLatchAdvancedTest method testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot.

@Test
public void testNewRaftGroupMemberSchedulesTimeoutsWithSnapshot() throws ExecutionException, InterruptedException {
    latch.trySetCount(1);
    spawn(() -> {
        try {
            latch.await(10, MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    CPGroupId groupId = getGroupId(latch);
    assertTrueEventually(() -> {
        HazelcastInstance leader = getLeaderInstance(instances, groupId);
        CountDownLatchService service = getNodeEngineImpl(leader).getService(CountDownLatchService.SERVICE_NAME);
        ResourceRegistry registry = service.getRegistryOrNull(groupId);
        assertFalse(registry.getWaitTimeouts().isEmpty());
    });
    for (int i = 0; i < LOG_ENTRY_COUNT_TO_SNAPSHOT; i++) {
        latch.trySetCount(1);
    }
    assertTrueEventually(() -> {
        for (HazelcastInstance instance : instances) {
            RaftNodeImpl raftNode = getRaftNode(instance, groupId);
            assertNotNull(raftNode);
            LogEntry snapshotEntry = getSnapshotEntry(raftNode);
            assertTrue(snapshotEntry.index() > 0);
            List<RestoreSnapshotOp> ops = (List<RestoreSnapshotOp>) snapshotEntry.operation();
            for (RestoreSnapshotOp op : ops) {
                if (op.getServiceName().equals(CountDownLatchService.SERVICE_NAME)) {
                    ResourceRegistry registry = (ResourceRegistry) op.getSnapshot();
                    assertFalse(registry.getWaitTimeouts().isEmpty());
                    return;
                }
            }
            fail();
        }
    });
    instances[1].shutdown();
    HazelcastInstance newInstance = factory.newHazelcastInstance(createConfig(groupSize, groupSize));
    newInstance.getCPSubsystem().getCPSubsystemManagementService().promoteToCPMember().toCompletableFuture().get();
    assertTrueEventually(() -> {
        CountDownLatchService service = getNodeEngineImpl(newInstance).getService(CountDownLatchService.SERVICE_NAME);
        CountDownLatchRegistry registry = service.getRegistryOrNull(groupId);
        assertNotNull(registry);
        assertFalse(registry.getWaitTimeouts().isEmpty());
        Assert.assertEquals(1, registry.getRemainingCount(objectName));
    });
}
Also used : CPGroupId(com.hazelcast.cp.CPGroupId) HazelcastInstance(com.hazelcast.core.HazelcastInstance) RaftNodeImpl(com.hazelcast.cp.internal.raft.impl.RaftNodeImpl) RestoreSnapshotOp(com.hazelcast.cp.internal.raftop.snapshot.RestoreSnapshotOp) List(java.util.List) ResourceRegistry(com.hazelcast.cp.internal.datastructures.spi.blocking.ResourceRegistry) LogEntry(com.hazelcast.cp.internal.raft.impl.log.LogEntry) ParallelJVMTest(com.hazelcast.test.annotation.ParallelJVMTest) QuickTest(com.hazelcast.test.annotation.QuickTest) Test(org.junit.Test)

Example 20 with LogEntry

use of com.hazelcast.cp.internal.raft.impl.log.LogEntry in project hazelcast by hazelcast.

the class PersistenceTest method when_leaderAppendEntriesInMinoritySplit_then_itTruncatesEntriesOnStore.

@Test
public void when_leaderAppendEntriesInMinoritySplit_then_itTruncatesEntriesOnStore() throws ExecutionException, InterruptedException {
    group = new LocalRaftGroupBuilder(3).setRaftStateStoreFactory(RAFT_STATE_STORE_FACTORY).build();
    group.start();
    final RaftNodeImpl leader = group.waitUntilLeaderElected();
    leader.replicate(new ApplyRaftRunnable("val1")).get();
    assertTrueEventually(() -> {
        for (RaftNodeImpl raftNode : group.getNodes()) {
            assertEquals(1, getCommitIndex(raftNode));
        }
    });
    final RaftNodeImpl[] followers = group.getNodesExcept(leader.getLocalMember());
    group.split(leader.getLocalMember());
    assertTrueEventually(() -> {
        for (RaftNodeImpl raftNode : followers) {
            RaftEndpoint leaderEndpoint = getLeaderMember(raftNode);
            assertNotNull(leaderEndpoint);
            assertNotEquals(leader.getLocalMember(), leaderEndpoint);
        }
    });
    for (int i = 0; i < 10; i++) {
        leader.replicate(new ApplyRaftRunnable("isolated" + i));
    }
    assertTrueEventually(() -> {
        RestoredRaftState restoredState = getRestoredState(leader);
        LogEntry[] entries = restoredState.entries();
        assertEquals(11, entries.length);
        assertEquals("val1", ((ApplyRaftRunnable) entries[0].operation()).getVal());
        for (int i = 1; i < 11; i++) {
            assertEquals("isolated" + (i - 1), ((ApplyRaftRunnable) entries[i].operation()).getVal());
        }
    });
    RaftNodeImpl newLeader = group.getNode(getLeaderMember(followers[0]));
    for (int i = 0; i < 10; i++) {
        newLeader.replicate(new ApplyRaftRunnable("valNew" + i)).get();
    }
    assertTrueEventually(() -> {
        for (RaftNodeImpl raftNode : followers) {
            assertEquals(11, getCommitIndex(raftNode));
        }
    });
    group.merge();
    RaftNodeImpl finalLeader = group.waitUntilLeaderElected();
    assertNotEquals(leader.getLocalMember(), finalLeader.getLocalMember());
    assertTrueEventually(() -> {
        RestoredRaftState state = getRestoredState(leader);
        LogEntry[] entries = state.entries();
        assertEquals(11, entries.length);
        assertEquals("val1", ((ApplyRaftRunnable) entries[0].operation()).getVal());
        for (int i = 1; i < 11; i++) {
            assertEquals("valNew" + (i - 1), ((ApplyRaftRunnable) entries[i].operation()).getVal());
        }
    });
}
Also used : RestoredRaftState(com.hazelcast.cp.internal.raft.impl.persistence.RestoredRaftState) ApplyRaftRunnable(com.hazelcast.cp.internal.raft.impl.dataservice.ApplyRaftRunnable) LocalRaftGroupBuilder(com.hazelcast.cp.internal.raft.impl.testing.LocalRaftGroup.LocalRaftGroupBuilder) LogEntry(com.hazelcast.cp.internal.raft.impl.log.LogEntry) ParallelJVMTest(com.hazelcast.test.annotation.ParallelJVMTest) QuickTest(com.hazelcast.test.annotation.QuickTest) Test(org.junit.Test)

Aggregations

LogEntry (com.hazelcast.cp.internal.raft.impl.log.LogEntry)20 RaftLog (com.hazelcast.cp.internal.raft.impl.log.RaftLog)10 ParallelJVMTest (com.hazelcast.test.annotation.ParallelJVMTest)8 QuickTest (com.hazelcast.test.annotation.QuickTest)8 Test (org.junit.Test)8 ApplyRaftRunnable (com.hazelcast.cp.internal.raft.impl.dataservice.ApplyRaftRunnable)4 RaftState (com.hazelcast.cp.internal.raft.impl.state.RaftState)4 List (java.util.List)4 HazelcastInstance (com.hazelcast.core.HazelcastInstance)3 CPGroupId (com.hazelcast.cp.CPGroupId)3 ResourceRegistry (com.hazelcast.cp.internal.datastructures.spi.blocking.ResourceRegistry)3 RaftEndpoint (com.hazelcast.cp.internal.raft.impl.RaftEndpoint)3 RaftNodeImpl (com.hazelcast.cp.internal.raft.impl.RaftNodeImpl)3 UpdateRaftGroupMembersCmd (com.hazelcast.cp.internal.raft.impl.command.UpdateRaftGroupMembersCmd)3 RestoredRaftState (com.hazelcast.cp.internal.raft.impl.persistence.RestoredRaftState)3 LocalRaftGroupBuilder (com.hazelcast.cp.internal.raft.impl.testing.LocalRaftGroup.LocalRaftGroupBuilder)3 DestroyRaftGroupCmd (com.hazelcast.cp.internal.raft.command.DestroyRaftGroupCmd)2 RaftGroupCmd (com.hazelcast.cp.internal.raft.command.RaftGroupCmd)2 AppendRequest (com.hazelcast.cp.internal.raft.impl.dto.AppendRequest)2 AppendSuccessResponse (com.hazelcast.cp.internal.raft.impl.dto.AppendSuccessResponse)2