Search in sources :

Example 16 with SnapshotInfo

use of org.apache.ratis.statemachine.SnapshotInfo in project incubator-ratis by apache.

the class LogAppender method shouldInstallSnapshot.

/**
 * Should this {@link LogAppender} send a snapshot to the follower?
 *
 * @return the snapshot if it should install a snapshot; otherwise, return null.
 */
default SnapshotInfo shouldInstallSnapshot() {
    // we should install snapshot if the follower needs to catch up and:
    // 1. there is no local log entry but there is snapshot
    // 2. or the follower's next index is smaller than the log start index
    // 3. or the follower is bootstrapping and has not installed any snapshot yet
    final FollowerInfo follower = getFollower();
    final boolean isFollowerBootstrapping = getLeaderState().isFollowerBootstrapping(follower);
    final SnapshotInfo snapshot = getServer().getStateMachine().getLatestSnapshot();
    if (isFollowerBootstrapping && !follower.hasAttemptedToInstallSnapshot()) {
        if (snapshot == null) {
            // Leader cannot send null snapshot to follower. Hence, acknowledge InstallSnapshot attempt (even though it
            // was not attempted) so that follower can come out of staging state after appending log entries.
            follower.setAttemptedToInstallSnapshot();
        } else {
            return snapshot;
        }
    }
    final long followerNextIndex = getFollower().getNextIndex();
    if (followerNextIndex < getRaftLog().getNextIndex()) {
        final long logStartIndex = getRaftLog().getStartIndex();
        if (followerNextIndex < logStartIndex || (logStartIndex == RaftLog.INVALID_LOG_INDEX && snapshot != null)) {
            return snapshot;
        }
    }
    return null;
}
Also used : SnapshotInfo(org.apache.ratis.statemachine.SnapshotInfo)

Example 17 with SnapshotInfo

use of org.apache.ratis.statemachine.SnapshotInfo in project incubator-ratis by apache.

the class SnapshotManager method installSnapshot.

@SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
public void installSnapshot(StateMachine stateMachine, InstallSnapshotRequestProto request) throws IOException {
    final InstallSnapshotRequestProto.SnapshotChunkProto snapshotChunkRequest = request.getSnapshotChunk();
    final long lastIncludedIndex = snapshotChunkRequest.getTermIndex().getIndex();
    final RaftStorageDirectory dir = storage.getStorageDir();
    // create a unique temporary directory
    final File tmpDir = new File(dir.getTmpDir(), UUID.randomUUID().toString());
    FileUtils.createDirectories(tmpDir);
    tmpDir.deleteOnExit();
    LOG.info("Installing snapshot:{}, to tmp dir:{}", request, tmpDir);
    for (FileChunkProto chunk : snapshotChunkRequest.getFileChunksList()) {
        SnapshotInfo pi = stateMachine.getLatestSnapshot();
        if (pi != null && pi.getTermIndex().getIndex() >= lastIncludedIndex) {
            throw new IOException("There exists snapshot file " + pi.getFiles() + " in " + selfId + " with endIndex >= lastIncludedIndex " + lastIncludedIndex);
        }
        // this is relative to the root dir
        String fileName = chunk.getFilename();
        // TODO: assumes flat layout inside SM dir
        File tmpSnapshotFile = new File(tmpDir, new File(dir.getRoot(), fileName).getName());
        FileOutputStream out = null;
        try {
            // same last index.
            if (chunk.getOffset() == 0) {
                if (tmpSnapshotFile.exists()) {
                    FileUtils.deleteFully(tmpSnapshotFile);
                }
                // create the temp snapshot file and put padding inside
                out = new FileOutputStream(tmpSnapshotFile);
            } else {
                Preconditions.assertTrue(tmpSnapshotFile.exists());
                out = new FileOutputStream(tmpSnapshotFile, true);
                FileChannel fc = out.getChannel();
                fc.position(chunk.getOffset());
            }
            // write data to the file
            out.write(chunk.getData().toByteArray());
        } finally {
            IOUtils.cleanup(null, out);
        }
        // the md5 digest and create the md5 meta-file.
        if (chunk.getDone()) {
            final MD5Hash expectedDigest = new MD5Hash(chunk.getFileDigest().toByteArray());
            // calculate the checksum of the snapshot file and compare it with the
            // file digest in the request
            MD5Hash digest = MD5FileUtil.computeMd5ForFile(tmpSnapshotFile);
            if (!digest.equals(expectedDigest)) {
                LOG.warn("The snapshot md5 digest {} does not match expected {}", digest, expectedDigest);
                // rename the temp snapshot file to .corrupt
                FileUtils.renameFileToCorrupt(tmpSnapshotFile);
                throw new CorruptedFileException(tmpSnapshotFile, "MD5 mismatch for snapshot-" + lastIncludedIndex + " installation");
            } else {
                MD5FileUtil.saveMD5File(tmpSnapshotFile, digest);
            }
        }
    }
    if (snapshotChunkRequest.getDone()) {
        LOG.info("Install snapshot is done, renaming tnp dir:{} to:{}", tmpDir, dir.getStateMachineDir());
        dir.getStateMachineDir().delete();
        tmpDir.renameTo(dir.getStateMachineDir());
    }
}
Also used : FileChannel(java.nio.channels.FileChannel) FileChunkProto(org.apache.ratis.proto.RaftProtos.FileChunkProto) IOException(java.io.IOException) SnapshotInfo(org.apache.ratis.statemachine.SnapshotInfo) CorruptedFileException(org.apache.ratis.io.CorruptedFileException) FileOutputStream(java.io.FileOutputStream) MD5Hash(org.apache.ratis.io.MD5Hash) InstallSnapshotRequestProto(org.apache.ratis.proto.RaftProtos.InstallSnapshotRequestProto) File(java.io.File) SuppressFBWarnings(edu.umd.cs.findbugs.annotations.SuppressFBWarnings)

Example 18 with SnapshotInfo

use of org.apache.ratis.statemachine.SnapshotInfo in project incubator-ratis by apache.

the class InstallSnapshotNotificationTests method testAddNewFollowers.

private void testAddNewFollowers(CLUSTER cluster) throws Exception {
    leaderSnapshotInfoRef.set(null);
    final List<LogSegmentPath> logs;
    int i = 0;
    try {
        RaftTestUtil.waitForLeader(cluster);
        final RaftPeerId leaderId = cluster.getLeader().getId();
        try (final RaftClient client = cluster.createClient(leaderId)) {
            for (; i < SNAPSHOT_TRIGGER_THRESHOLD * 2 - 1; i++) {
                RaftClientReply reply = client.io().send(new RaftTestUtil.SimpleMessage("m" + i));
                Assert.assertTrue(reply.isSuccess());
            }
        }
        // wait for the snapshot to be done
        final RaftServer.Division leader = cluster.getLeader();
        final long nextIndex = leader.getRaftLog().getNextIndex();
        LOG.info("nextIndex = {}", nextIndex);
        final List<File> snapshotFiles = RaftSnapshotBaseTest.getSnapshotFiles(cluster, nextIndex - SNAPSHOT_TRIGGER_THRESHOLD, nextIndex);
        JavaUtils.attemptRepeatedly(() -> {
            Assert.assertTrue(snapshotFiles.stream().anyMatch(RaftSnapshotBaseTest::exists));
            return null;
        }, 10, ONE_SECOND, "snapshotFile.exist", LOG);
        logs = LogSegmentPath.getLogSegmentPaths(leader.getRaftStorage());
    } finally {
        cluster.shutdown();
    }
    // delete the log segments from the leader
    LOG.info("Delete logs {}", logs);
    for (LogSegmentPath path : logs) {
        // the log may be already puged
        FileUtils.deleteFully(path.getPath());
    }
    // restart the peer
    LOG.info("Restarting the cluster");
    cluster.restart(false);
    try {
        RaftSnapshotBaseTest.assertLeaderContent(cluster);
        // generate some more traffic
        try (final RaftClient client = cluster.createClient(cluster.getLeader().getId())) {
            Assert.assertTrue(client.io().send(new RaftTestUtil.SimpleMessage("m" + i)).isSuccess());
        }
        final SnapshotInfo leaderSnapshotInfo = cluster.getLeader().getStateMachine().getLatestSnapshot();
        final boolean set = leaderSnapshotInfoRef.compareAndSet(null, leaderSnapshotInfo);
        Assert.assertTrue(set);
        // add two more peers
        final MiniRaftCluster.PeerChanges change = cluster.addNewPeers(2, true, true);
        // trigger setConfiguration
        cluster.setConfiguration(change.allPeersInNewConf);
        RaftServerTestUtil.waitAndCheckNewConf(cluster, change.allPeersInNewConf, 0, null);
        // leader snapshot.
        for (RaftServer.Division follower : cluster.getFollowers()) {
            Assert.assertEquals(leaderSnapshotInfo.getIndex(), RaftServerTestUtil.getLatestInstalledSnapshotIndex(follower));
        }
        // restart the peer and check if it can correctly handle conf change
        cluster.restartServer(cluster.getLeader().getId(), false);
        RaftSnapshotBaseTest.assertLeaderContent(cluster);
    } finally {
        cluster.shutdown();
    }
}
Also used : RaftServer(org.apache.ratis.server.RaftServer) LogSegmentPath(org.apache.ratis.server.raftlog.segmented.LogSegmentPath) SnapshotInfo(org.apache.ratis.statemachine.SnapshotInfo) SingleFileSnapshotInfo(org.apache.ratis.statemachine.impl.SingleFileSnapshotInfo) MiniRaftCluster(org.apache.ratis.server.impl.MiniRaftCluster) RaftClientReply(org.apache.ratis.protocol.RaftClientReply) RaftPeerId(org.apache.ratis.protocol.RaftPeerId) File(java.io.File) RaftClient(org.apache.ratis.client.RaftClient)

Example 19 with SnapshotInfo

use of org.apache.ratis.statemachine.SnapshotInfo in project incubator-ratis by apache.

the class InstallSnapshotNotificationTests method testInstallSnapshotNotificationCount.

private void testInstallSnapshotNotificationCount(CLUSTER cluster) throws Exception {
    leaderSnapshotInfoRef.set(null);
    numSnapshotRequests.set(0);
    int i = 0;
    try {
        RaftTestUtil.waitForLeader(cluster);
        final RaftPeerId leaderId = cluster.getLeader().getId();
        // Let a few heartbeats pass.
        ONE_SECOND.sleep();
        Assert.assertEquals(0, numSnapshotRequests.get());
        // Generate data.
        try (final RaftClient client = cluster.createClient(leaderId)) {
            for (; i < 10; i++) {
                RaftClientReply reply = client.io().send(new RaftTestUtil.SimpleMessage("m" + i));
                Assert.assertTrue(reply.isSuccess());
            }
        }
        // Wait until index has been updated
        RaftTestUtil.waitFor(() -> cluster.getLeader().getStateMachine().getLastAppliedTermIndex().getIndex() == 20, 300, 15000);
        // Take snapshot and check result.
        long snapshotIndex = cluster.getLeader().getStateMachine().takeSnapshot();
        Assert.assertEquals(20, snapshotIndex);
        final SnapshotInfo leaderSnapshotInfo = cluster.getLeader().getStateMachine().getLatestSnapshot();
        Assert.assertEquals(20, leaderSnapshotInfo.getIndex());
        final boolean set = leaderSnapshotInfoRef.compareAndSet(null, leaderSnapshotInfo);
        Assert.assertTrue(set);
        // Wait for the snapshot to be done.
        final RaftServer.Division leader = cluster.getLeader();
        final long nextIndex = leader.getRaftLog().getNextIndex();
        Assert.assertEquals(21, nextIndex);
        // End index is exclusive.
        final List<File> snapshotFiles = RaftSnapshotBaseTest.getSnapshotFiles(cluster, 0, nextIndex);
        JavaUtils.attemptRepeatedly(() -> {
            Assert.assertTrue(snapshotFiles.stream().anyMatch(RaftSnapshotBaseTest::exists));
            return null;
        }, 10, ONE_SECOND, "snapshotFile.exist", LOG);
        // Clear all log files and reset cached log start index.
        long snapshotInstallIndex = leader.getRaftLog().onSnapshotInstalled(leader.getRaftLog().getLastCommittedIndex()).get();
        Assert.assertEquals(20, snapshotInstallIndex);
        // Check that logs are gone.
        Assert.assertEquals(0, LogSegmentPath.getLogSegmentPaths(leader.getRaftStorage()).size());
        Assert.assertEquals(RaftLog.INVALID_LOG_INDEX, leader.getRaftLog().getStartIndex());
        // Allow some heartbeats to go through, then make sure none of them had
        // snapshot requests.
        ONE_SECOND.sleep();
        Assert.assertEquals(0, numSnapshotRequests.get());
        // Make sure leader and followers are still up to date.
        for (RaftServer.Division follower : cluster.getFollowers()) {
            Assert.assertEquals(leader.getRaftLog().getNextIndex(), follower.getRaftLog().getNextIndex());
        }
        // Add two more peers who will need snapshots from the leader.
        final MiniRaftCluster.PeerChanges change = cluster.addNewPeers(2, true, true);
        // trigger setConfiguration
        cluster.setConfiguration(change.allPeersInNewConf);
        RaftServerTestUtil.waitAndCheckNewConf(cluster, change.allPeersInNewConf, 0, null);
        // Generate more data.
        try (final RaftClient client = cluster.createClient(leader.getId())) {
            Assert.assertTrue(client.io().send(new RaftTestUtil.SimpleMessage("m" + i)).isSuccess());
        }
        // Make sure leader and followers are still up to date.
        for (RaftServer.Division follower : cluster.getFollowers()) {
            // Give follower slightly time to catch up
            RaftTestUtil.waitFor(() -> leader.getRaftLog().getNextIndex() == follower.getRaftLog().getNextIndex(), 300, 15000);
        }
        // Make sure each new peer got one snapshot notification.
        Assert.assertEquals(2, numSnapshotRequests.get());
    } finally {
        cluster.shutdown();
    }
}
Also used : RaftServer(org.apache.ratis.server.RaftServer) SnapshotInfo(org.apache.ratis.statemachine.SnapshotInfo) SingleFileSnapshotInfo(org.apache.ratis.statemachine.impl.SingleFileSnapshotInfo) MiniRaftCluster(org.apache.ratis.server.impl.MiniRaftCluster) RaftClientReply(org.apache.ratis.protocol.RaftClientReply) RaftPeerId(org.apache.ratis.protocol.RaftPeerId) File(java.io.File) RaftClient(org.apache.ratis.client.RaftClient)

Example 20 with SnapshotInfo

use of org.apache.ratis.statemachine.SnapshotInfo in project alluxio by Alluxio.

the class SnapshotReplicationManager method handleRequest.

/**
 * Handles snapshot requests.
 *
 * @param queryRequest the query request
 * @return the response message, or null if the request is not handled
 * @throws IOException if any error occurred while handling the request
 */
public Message handleRequest(JournalQueryRequest queryRequest) throws IOException {
    if (queryRequest.hasSnapshotInfoRequest()) {
        SnapshotInfo latestSnapshot = mStorage.getLatestSnapshot();
        if (latestSnapshot == null) {
            LOG.debug("No snapshot to send");
            return toMessage(GetSnapshotInfoResponse.getDefaultInstance());
        }
        JournalQueryResponse response = JournalQueryResponse.newBuilder().setSnapshotInfoResponse(GetSnapshotInfoResponse.newBuilder().setLatest(toSnapshotMetadata(latestSnapshot.getTermIndex()))).build();
        LOG.debug("Sent snapshot info response {}", response);
        return toMessage(response);
    }
    if (queryRequest.hasSnapshotRequest()) {
        LOG.debug("Start sending snapshot to leader");
        sendSnapshotToLeader();
        return Message.EMPTY;
    }
    return null;
}
Also used : SnapshotInfo(org.apache.ratis.statemachine.SnapshotInfo) SingleFileSnapshotInfo(org.apache.ratis.statemachine.impl.SingleFileSnapshotInfo) JournalQueryResponse(alluxio.grpc.JournalQueryResponse)

Aggregations

SnapshotInfo (org.apache.ratis.statemachine.SnapshotInfo)21 SingleFileSnapshotInfo (org.apache.ratis.statemachine.impl.SingleFileSnapshotInfo)7 TermIndex (org.apache.ratis.server.protocol.TermIndex)6 File (java.io.File)5 RaftClient (org.apache.ratis.client.RaftClient)3 RaftClientReply (org.apache.ratis.protocol.RaftClientReply)3 RaftPeerId (org.apache.ratis.protocol.RaftPeerId)3 RaftServer (org.apache.ratis.server.RaftServer)3 MiniRaftCluster (org.apache.ratis.server.impl.MiniRaftCluster)3 NotFoundException (alluxio.exception.status.NotFoundException)2 FileNotFoundException (java.io.FileNotFoundException)2 IOException (java.io.IOException)2 AbortedException (alluxio.exception.status.AbortedException)1 AlluxioStatusException (alluxio.exception.status.AlluxioStatusException)1 DownloadSnapshotPRequest (alluxio.grpc.DownloadSnapshotPRequest)1 DownloadSnapshotPResponse (alluxio.grpc.DownloadSnapshotPResponse)1 JournalQueryResponse (alluxio.grpc.JournalQueryResponse)1 UploadSnapshotPRequest (alluxio.grpc.UploadSnapshotPRequest)1 UploadSnapshotPResponse (alluxio.grpc.UploadSnapshotPResponse)1 Timer (com.codahale.metrics.Timer)1