|
18 | 18 | package org.apache.ratis.grpc; |
19 | 19 |
|
20 | 20 | import org.apache.ratis.LogAppenderTests; |
| 21 | +import org.apache.ratis.grpc.server.GrpcServicesImpl; |
21 | 22 | import org.apache.ratis.proto.RaftProtos; |
| 23 | +import org.apache.ratis.protocol.RaftGroupId; |
| 24 | +import org.apache.ratis.protocol.RaftPeerId; |
22 | 25 | import org.apache.ratis.server.impl.MiniRaftCluster; |
23 | 26 | import org.apache.ratis.RaftTestUtil; |
24 | 27 | import org.apache.ratis.client.RaftClient; |
|
29 | 32 | import org.apache.ratis.server.RaftServerConfigKeys; |
30 | 33 | import org.apache.ratis.server.leader.FollowerInfo; |
31 | 34 | import org.apache.ratis.server.impl.RaftServerTestUtil; |
| 35 | +import org.apache.ratis.server.leader.LogAppender; |
32 | 36 | import org.apache.ratis.statemachine.impl.SimpleStateMachine4Testing; |
33 | 37 | import org.apache.ratis.statemachine.StateMachine; |
| 38 | +import org.apache.ratis.util.CodeInjectionForTesting; |
34 | 39 | import org.apache.ratis.util.JavaUtils; |
35 | 40 | import org.apache.ratis.util.Slf4jUtils; |
36 | 41 | import org.junit.jupiter.api.Assertions; |
| 42 | +import org.junit.jupiter.api.Test; |
37 | 43 | import org.junit.jupiter.params.ParameterizedTest; |
38 | 44 | import org.junit.jupiter.params.provider.MethodSource; |
39 | 45 | import org.slf4j.event.Level; |
|
42 | 48 | import java.util.ArrayList; |
43 | 49 | import java.util.Arrays; |
44 | 50 | import java.util.Collection; |
| 51 | +import java.util.Set; |
45 | 52 | import java.util.concurrent.CompletableFuture; |
| 53 | +import java.util.concurrent.atomic.AtomicInteger; |
| 54 | +import java.util.stream.Collectors; |
46 | 55 |
|
47 | 56 | import static org.apache.ratis.RaftTestUtil.waitForLeader; |
48 | 57 |
|
@@ -148,4 +157,58 @@ private void runTestRestartLogAppender(MiniRaftClusterWithGrpc cluster) throws E |
148 | 157 | Assertions.assertTrue(newleaderMetrics.getRegistry().counter(counter).getCount() >= 1L); |
149 | 158 | } |
150 | 159 | } |
| 160 | + |
| 161 | + @Test |
| 162 | + public void testLogAppenderAutoRestartOnException() throws Exception { |
| 163 | + runWithNewCluster(3, this::runTestAutoRestartOnException); |
| 164 | + } |
| 165 | + |
| 166 | + private void runTestAutoRestartOnException(MiniRaftClusterWithGrpc cluster) throws Exception { |
| 167 | + final RaftServer.Division leader = waitForLeader(cluster); |
| 168 | + final RaftPeerId leaderId = leader.getId(); |
| 169 | + |
| 170 | + try (RaftClient client = cluster.createClient(leaderId)) { |
| 171 | + for (int i = 0; i < 5; i++) { |
| 172 | + Assertions.assertTrue(client.io().send(new RaftTestUtil.SimpleMessage("init-" + i)).isSuccess()); |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + final Set<LogAppender> before = RaftServerTestUtil.getLogAppenders(leader).collect(Collectors.toSet()); |
| 177 | + Assertions.assertEquals(2, before.size()); |
| 178 | + |
| 179 | + // Inject a one-time IllegalStateException into the leader's AppendEntries send path. |
| 180 | + // This causes the LogAppenderDaemon to enter EXCEPTION state and call restart(). |
| 181 | + final RaftGroupId groupId = cluster.getGroupId(); |
| 182 | + final AtomicInteger failCount = new AtomicInteger(0); |
| 183 | + try { |
| 184 | + CodeInjectionForTesting.put(GrpcServicesImpl.GRPC_SEND_SERVER_REQUEST, (localId, remoteId, args) -> { |
| 185 | + if (leaderId.equals(localId) |
| 186 | + && args.length > 0 && args[0] instanceof RaftProtos.AppendEntriesRequestProto) { |
| 187 | + final RaftProtos.AppendEntriesRequestProto proto = (RaftProtos.AppendEntriesRequestProto) args[0]; |
| 188 | + if (RaftGroupId.valueOf(proto.getServerRequest().getRaftGroupId().getId()).equals(groupId) |
| 189 | + && failCount.getAndIncrement() < 1) { |
| 190 | + throw new IllegalStateException("Injected failure for restart test"); |
| 191 | + } |
| 192 | + } |
| 193 | + return false; |
| 194 | + }); |
| 195 | + |
| 196 | + JavaUtils.attempt(() -> { |
| 197 | + final Set<LogAppender> current = RaftServerTestUtil.getLogAppenders(leader) |
| 198 | + .collect(Collectors.toSet()); |
| 199 | + Assertions.assertEquals(2, current.size()); |
| 200 | + Assertions.assertTrue(current.stream().anyMatch(a -> !before.contains(a)), |
| 201 | + "Expected at least one new LogAppender instance after daemon exception restart"); |
| 202 | + }, 30, ONE_SECOND, "LogAppender auto-restart after exception", LOG); |
| 203 | + } finally { |
| 204 | + CodeInjectionForTesting.remove(GrpcServicesImpl.GRPC_SEND_SERVER_REQUEST); |
| 205 | + } |
| 206 | + |
| 207 | + try (RaftClient client = cluster.createClient(leaderId)) { |
| 208 | + for (int i = 0; i < 5; i++) { |
| 209 | + Assertions.assertTrue( |
| 210 | + client.io().send(new RaftTestUtil.SimpleMessage("after-restart-" + i)).isSuccess()); |
| 211 | + } |
| 212 | + } |
| 213 | + } |
151 | 214 | } |
0 commit comments