|
55 | 55 | import org.elasticsearch.threadpool.ThreadPool;
|
56 | 56 | import org.elasticsearch.transport.TransportException;
|
57 | 57 | import org.elasticsearch.transport.TransportResponse;
|
| 58 | +import org.elasticsearch.transport.TransportResponse.Empty; |
58 | 59 | import org.elasticsearch.transport.TransportResponseHandler;
|
59 | 60 |
|
60 | 61 | import java.io.IOException;
|
@@ -136,6 +137,7 @@ public class Legislator extends AbstractComponent {
|
136 | 137 | private final Map<DiscoveryNode, MembershipAction.JoinCallback> joinRequestAccumulator = new HashMap<>();
|
137 | 138 |
|
138 | 139 | private Optional<Publication> currentPublication = Optional.empty();
|
| 140 | + private long laggingUntilCommittedVersionExceeds; |
139 | 141 |
|
140 | 142 | public Legislator(Settings settings, ConsensusState.PersistedState persistedState,
|
141 | 143 | Transport transport, MasterService masterService, DiscoveryNode localNode, LongSupplier currentTimeSupplier,
|
@@ -279,35 +281,16 @@ private void startSeekingJoins() {
|
279 | 281 | private Join joinLeaderInTerm(DiscoveryNode sourceNode, long term) {
|
280 | 282 | logger.debug("joinLeaderInTerm: from [{}] with term {}", sourceNode, term);
|
281 | 283 | Join join = consensusState.handleStartJoin(sourceNode, term);
|
| 284 | + lastJoin = Optional.of(join); |
282 | 285 | if (mode != Mode.CANDIDATE) {
|
283 | 286 | becomeCandidate("joinLeaderInTerm");
|
284 | 287 | }
|
285 | 288 | return join;
|
286 | 289 | }
|
287 | 290 |
|
288 | 291 | public void handleStartJoin(DiscoveryNode sourceNode, StartJoinRequest startJoinRequest) {
|
289 |
| - Join join = joinLeaderInTerm(sourceNode, startJoinRequest.getTerm()); |
290 |
| - |
291 |
| - transport.sendJoin(sourceNode, join, new TransportResponseHandler<TransportResponse.Empty>() { |
292 |
| - @Override |
293 |
| - public void handleResponse(TransportResponse.Empty response) { |
294 |
| - logger.debug("SendJoinResponseHandler: successfully joined {}", sourceNode); |
295 |
| - } |
296 |
| - |
297 |
| - @Override |
298 |
| - public void handleException(TransportException exp) { |
299 |
| - if (exp.getRootCause() instanceof ConsensusMessageRejectedException) { |
300 |
| - logger.debug("SendJoinResponseHandler: [{}] failed: {}", sourceNode, exp.getRootCause().getMessage()); |
301 |
| - } else { |
302 |
| - logger.debug(() -> new ParameterizedMessage("SendJoinResponseHandler: [{}] failed", sourceNode), exp); |
303 |
| - } |
304 |
| - } |
305 |
| - |
306 |
| - @Override |
307 |
| - public String executor() { |
308 |
| - return ThreadPool.Names.SAME; |
309 |
| - } |
310 |
| - }); |
| 292 | + final Join join = joinLeaderInTerm(sourceNode, startJoinRequest.getTerm()); |
| 293 | + sendJoin(sourceNode, join); |
311 | 294 | }
|
312 | 295 |
|
313 | 296 | private Optional<Join> ensureTermAtLeast(DiscoveryNode sourceNode, long targetTerm) {
|
@@ -899,11 +882,7 @@ public LegislatorPublishResponse handlePublishRequest(DiscoveryNode sourceNode,
|
899 | 882 | DiscoveryNodes.builder(publishRequest.getAcceptedState().nodes()).localNodeId(getLocalNode().getId()).build()).build();
|
900 | 883 | publishRequest = new PublishRequest(clusterState);
|
901 | 884 |
|
902 |
| - final Optional<Join> optionalJoin = ensureTermAtLeast(sourceNode, publishRequest.getAcceptedState().term()); |
903 |
| - |
904 |
| - if (optionalJoin.isPresent()) { |
905 |
| - lastJoin = optionalJoin; |
906 |
| - } |
| 885 | + ensureTermAtLeast(sourceNode, publishRequest.getAcceptedState().term()); |
907 | 886 |
|
908 | 887 | logger.trace("handlePublishRequest: handling [{}] from [{}]", publishRequest, sourceNode);
|
909 | 888 |
|
@@ -931,36 +910,46 @@ public HeartbeatResponse handleHeartbeatRequest(DiscoveryNode sourceNode, Heartb
|
931 | 910 | }
|
932 | 911 |
|
933 | 912 | ensureTermAtLeast(sourceNode, heartbeatRequest.getTerm()).ifPresent(join -> {
|
934 |
| - logger.debug("handleHeartbeatRequest: sending join [{}] for term [{}] to {}", |
935 |
| - join, heartbeatRequest.getTerm(), sourceNode); |
936 |
| - |
937 |
| - transport.sendJoin(sourceNode, join, new TransportResponseHandler<TransportResponse.Empty>() { |
938 |
| - @Override |
939 |
| - public void handleResponse(TransportResponse.Empty response) { |
940 |
| - logger.debug("SendJoinResponseHandler: successfully joined {}", sourceNode); |
941 |
| - } |
942 |
| - |
943 |
| - @Override |
944 |
| - public void handleException(TransportException exp) { |
945 |
| - if (exp.getRootCause() instanceof ConsensusMessageRejectedException) { |
946 |
| - logger.debug("SendJoinResponseHandler: [{}] failed: {}", sourceNode, exp.getRootCause().getMessage()); |
947 |
| - } else { |
948 |
| - logger.debug(() -> new ParameterizedMessage("SendJoinResponseHandler: [{}] failed", sourceNode), exp); |
949 |
| - } |
950 |
| - } |
951 |
| - |
952 |
| - @Override |
953 |
| - public String executor() { |
954 |
| - return ThreadPool.Names.SAME; |
955 |
| - } |
956 |
| - }); |
| 913 | + logger.debug("handleHeartbeatRequest: sending join [{}] for term [{}] to {}", join, heartbeatRequest.getTerm(), sourceNode); |
| 914 | + sendJoin(sourceNode, join); |
957 | 915 | });
|
958 | 916 |
|
| 917 | + if (laggingUntilCommittedVersionExceeds > 0 |
| 918 | + && (lastCommittedState.isPresent() == false || lastCommittedState.get().version() <= laggingUntilCommittedVersionExceeds)) { |
| 919 | + logger.debug("handleHeartbeatRequest: rejecting [{}] from [{}] due to lag at version [{}]", |
| 920 | + heartbeatRequest, sourceNode, laggingUntilCommittedVersionExceeds); |
| 921 | + throw new ConsensusMessageRejectedException("HeartbeatRequest rejected: lagging at version [{}]", |
| 922 | + laggingUntilCommittedVersionExceeds); |
| 923 | + } |
| 924 | + |
959 | 925 | becomeFollower("handleHeartbeatRequest", sourceNode);
|
960 | 926 |
|
961 | 927 | return new HeartbeatResponse(consensusState.getLastAcceptedVersion(), consensusState.getCurrentTerm());
|
962 | 928 | }
|
963 | 929 |
|
| 930 | + private void sendJoin(DiscoveryNode sourceNode, Join join) { |
| 931 | + transport.sendJoin(sourceNode, join, new TransportResponseHandler<Empty>() { |
| 932 | + @Override |
| 933 | + public void handleResponse(Empty response) { |
| 934 | + logger.debug("SendJoinResponseHandler: successfully joined {}", sourceNode); |
| 935 | + } |
| 936 | + |
| 937 | + @Override |
| 938 | + public void handleException(TransportException exp) { |
| 939 | + if (exp.getRootCause() instanceof ConsensusMessageRejectedException) { |
| 940 | + logger.debug("SendJoinResponseHandler: [{}] failed: {}", sourceNode, exp.getRootCause().getMessage()); |
| 941 | + } else { |
| 942 | + logger.debug(() -> new ParameterizedMessage("SendJoinResponseHandler: [{}] failed", sourceNode), exp); |
| 943 | + } |
| 944 | + } |
| 945 | + |
| 946 | + @Override |
| 947 | + public String executor() { |
| 948 | + return ThreadPool.Names.SAME; |
| 949 | + } |
| 950 | + }); |
| 951 | + } |
| 952 | + |
964 | 953 | public void handleApplyCommit(DiscoveryNode sourceNode, ApplyCommit applyCommit) {
|
965 | 954 | logger.trace("handleApplyCommit: applying {} from [{}]", applyCommit, sourceNode);
|
966 | 955 | consensusState.handleCommit(applyCommit);
|
@@ -1004,6 +993,9 @@ public OfferJoin handleSeekJoins(DiscoveryNode sender, SeekJoins seekJoins) {
|
1004 | 993 | consensusState.getCurrentTerm(), seekJoins, sender, newTerm);
|
1005 | 994 | sendStartJoin(new StartJoinRequest(newTerm));
|
1006 | 995 | throw new ConsensusMessageRejectedException("I'm still a leader");
|
| 996 | + |
| 997 | + // TODO what about a node that sent a join to a different node in our term? Is it now stuck until the next term? |
| 998 | + |
1007 | 999 | } else {
|
1008 | 1000 | // TODO: remove this once we have a discovery layer. If a node finds an active master node during discovery,
|
1009 | 1001 | // it will try to join that one, and not start seeking joins.
|
@@ -1464,15 +1456,15 @@ public void handleResponse(LeaderCheckResponse leaderCheckResponse) {
|
1464 | 1456 |
|
1465 | 1457 | final long leaderVersion = leaderCheckResponse.getVersion();
|
1466 | 1458 | long localVersion = getLastCommittedState().map(ClusterState::getVersion).orElse(-1L);
|
1467 |
| - if (leaderVersion > localVersion) { |
| 1459 | + if (leaderVersion > localVersion && running) { |
1468 | 1460 | logger.trace("LeaderCheck.handleResponse: heartbeat for version {} > local version {}, starting lag detector",
|
1469 | 1461 | leaderVersion, localVersion);
|
1470 | 1462 | futureExecutor.schedule(publishTimeout, "LeaderCheck#lagDetection", () -> {
|
1471 | 1463 | long localVersion2 = getLastCommittedState().map(ClusterState::getVersion).orElse(-1L);
|
1472 |
| - if (leaderVersion > localVersion2) { |
| 1464 | + if (leaderVersion > localVersion2 && running) { |
1473 | 1465 | logger.debug("LeaderCheck.handleResponse: lag detected: local version {} < leader version {} after {}",
|
1474 | 1466 | localVersion2, leaderVersion, publishTimeout);
|
1475 |
| - becomeCandidate("LeaderCheck.handleResponse"); |
| 1467 | + laggingUntilCommittedVersionExceeds = localVersion2; |
1476 | 1468 | }
|
1477 | 1469 | });
|
1478 | 1470 | }
|
|
0 commit comments