|
30 | 30 | import org.apache.ratis.server.protocol.TermIndex; |
31 | 31 | import org.apache.ratis.server.raftlog.RaftLog; |
32 | 32 | import org.apache.ratis.server.raftlog.segmented.LogSegmentPath; |
33 | | -import org.apache.ratis.server.storage.RaftStorage; |
34 | 33 | import org.apache.ratis.statemachine.RaftSnapshotBaseTest; |
35 | 34 | import org.apache.ratis.statemachine.SimpleStateMachine4Testing; |
36 | 35 | import org.apache.ratis.statemachine.SnapshotInfo; |
|
51 | 50 | import java.nio.file.Path; |
52 | 51 | import java.util.List; |
53 | 52 | import java.util.concurrent.CompletableFuture; |
| 53 | +import java.util.concurrent.atomic.AtomicInteger; |
54 | 54 | import java.util.concurrent.atomic.AtomicReference; |
55 | 55 |
|
56 | 56 | public abstract class InstallSnapshotNotificationTests<CLUSTER extends MiniRaftCluster> |
@@ -79,11 +79,16 @@ public abstract class InstallSnapshotNotificationTests<CLUSTER extends MiniRaftC |
79 | 79 | private static final int PURGE_GAP = 8; |
80 | 80 | private static final AtomicReference<SnapshotInfo> leaderSnapshotInfoRef = new AtomicReference<>(); |
81 | 81 |
|
| 82 | + private static final AtomicInteger numSnapshotRequests = new AtomicInteger(); |
| 83 | + |
82 | 84 | private static class StateMachine4InstallSnapshotNotificationTests extends SimpleStateMachine4Testing { |
83 | 85 | @Override |
84 | 86 | public CompletableFuture<TermIndex> notifyInstallSnapshotFromLeader( |
85 | 87 | RaftProtos.RoleInfoProto roleInfoProto, |
86 | 88 | TermIndex termIndex) { |
| 89 | + |
| 90 | + numSnapshotRequests.incrementAndGet(); |
| 91 | + |
87 | 92 | final SingleFileSnapshotInfo leaderSnapshotInfo = (SingleFileSnapshotInfo) leaderSnapshotInfoRef.get(); |
88 | 93 | LOG.info("{}: leaderSnapshotInfo = {}", getId(), leaderSnapshotInfo); |
89 | 94 | if (leaderSnapshotInfo == null) { |
@@ -236,6 +241,102 @@ private void testRestartFollower(CLUSTER cluster) throws Exception { |
236 | 241 | Assert.assertTrue(newLeaderNextIndex > oldLeaderNextIndex); |
237 | 242 | Assert.assertEquals(newLeaderNextIndex, follower.getRaftLog().getNextIndex()); |
238 | 243 | }, 10, ONE_SECOND, "followerNextIndex", LOG); |
| 244 | + } |
| 245 | + |
| 246 | + @Test |
| 247 | + public void testInstallSnapshotNotificationCount() throws Exception { |
| 248 | + runWithNewCluster(3, this::testInstallSnapshotNotificationCount); |
| 249 | + } |
| 250 | + |
| 251 | + |
| 252 | + private void testInstallSnapshotNotificationCount(CLUSTER cluster) throws Exception { |
| 253 | + leaderSnapshotInfoRef.set(null); |
| 254 | + numSnapshotRequests.set(0); |
| 255 | + |
| 256 | + int i = 0; |
| 257 | + try { |
| 258 | + RaftTestUtil.waitForLeader(cluster); |
| 259 | + final RaftPeerId leaderId = cluster.getLeader().getId(); |
239 | 260 |
|
| 261 | + // Let a few heartbeats pass. |
| 262 | + ONE_SECOND.sleep(); |
| 263 | + Assert.assertEquals(0, numSnapshotRequests.get()); |
| 264 | + |
| 265 | + // Generate data. |
| 266 | + try(final RaftClient client = cluster.createClient(leaderId)) { |
| 267 | + for (; i < 10; i++) { |
| 268 | + RaftClientReply |
| 269 | + reply = client.io().send(new RaftTestUtil.SimpleMessage("m" + i)); |
| 270 | + Assert.assertTrue(reply.isSuccess()); |
| 271 | + } |
| 272 | + } |
| 273 | + |
| 274 | + // Take snapshot and check result. |
| 275 | + long snapshotIndex = cluster.getLeader().getStateMachine().takeSnapshot(); |
| 276 | + Assert.assertEquals(20, snapshotIndex); |
| 277 | + final SnapshotInfo leaderSnapshotInfo = cluster.getLeader().getStateMachine().getLatestSnapshot(); |
| 278 | + Assert.assertEquals(20, leaderSnapshotInfo.getIndex()); |
| 279 | + final boolean set = leaderSnapshotInfoRef.compareAndSet(null, leaderSnapshotInfo); |
| 280 | + Assert.assertTrue(set); |
| 281 | + |
| 282 | + // Wait for the snapshot to be done. |
| 283 | + final RaftServer.Division leader = cluster.getLeader(); |
| 284 | + final long nextIndex = leader.getRaftLog().getNextIndex(); |
| 285 | + Assert.assertEquals(21, nextIndex); |
| 286 | + // End index is exclusive. |
| 287 | + final List<File> snapshotFiles = RaftSnapshotBaseTest.getSnapshotFiles(cluster, |
| 288 | + 0, nextIndex); |
| 289 | + JavaUtils.attemptRepeatedly(() -> { |
| 290 | + Assert.assertTrue(snapshotFiles.stream().anyMatch(RaftSnapshotBaseTest::exists)); |
| 291 | + return null; |
| 292 | + }, 10, ONE_SECOND, "snapshotFile.exist", LOG); |
| 293 | + |
| 294 | + // Clear all log files and reset cached log start index. |
| 295 | + long snapshotInstallIndex = |
| 296 | + leader.getRaftLog().onSnapshotInstalled(leader.getRaftLog().getLastCommittedIndex()).get(); |
| 297 | + Assert.assertEquals(20, snapshotInstallIndex); |
| 298 | + |
| 299 | + // Check that logs are gone. |
| 300 | + Assert.assertEquals(0, |
| 301 | + LogSegmentPath.getLogSegmentPaths(leader.getRaftStorage()).size()); |
| 302 | + Assert.assertEquals(RaftLog.INVALID_LOG_INDEX, leader.getRaftLog().getStartIndex()); |
| 303 | + |
| 304 | + // Allow some heartbeats to go through, then make sure none of them had |
| 305 | + // snapshot requests. |
| 306 | + ONE_SECOND.sleep(); |
| 307 | + Assert.assertEquals(0, numSnapshotRequests.get()); |
| 308 | + |
| 309 | + // Make sure leader and followers are still up to date. |
| 310 | + for (RaftServer.Division follower : cluster.getFollowers()) { |
| 311 | + Assert.assertEquals( |
| 312 | + leader.getRaftLog().getNextIndex(), |
| 313 | + follower.getRaftLog().getNextIndex()); |
| 314 | + } |
| 315 | + |
| 316 | + // Add two more peers who will need snapshots from the leader. |
| 317 | + final MiniRaftCluster.PeerChanges change = cluster.addNewPeers(2, true); |
| 318 | + // trigger setConfiguration |
| 319 | + cluster.setConfiguration(change.allPeersInNewConf); |
| 320 | + RaftServerTestUtil |
| 321 | + .waitAndCheckNewConf(cluster, change.allPeersInNewConf, 0, null); |
| 322 | + |
| 323 | + // Generate more data. |
| 324 | + try (final RaftClient client = cluster.createClient(leader.getId())) { |
| 325 | + Assert.assertTrue(client.io().send(new RaftTestUtil.SimpleMessage("m" + i)).isSuccess()); |
| 326 | + } |
| 327 | + |
| 328 | + // Make sure leader and followers are still up to date. |
| 329 | + for (RaftServer.Division follower : cluster.getFollowers()) { |
| 330 | + Assert.assertEquals( |
| 331 | + leader.getRaftLog().getNextIndex(), |
| 332 | + follower.getRaftLog().getNextIndex()); |
| 333 | + } |
| 334 | + |
| 335 | + // Make sure each new peer got one snapshot notification. |
| 336 | + Assert.assertEquals(2, numSnapshotRequests.get()); |
| 337 | + |
| 338 | + } finally { |
| 339 | + cluster.shutdown(); |
| 340 | + } |
240 | 341 | } |
241 | 342 | } |
0 commit comments