|
26 | 26 | import org.apache.logging.log4j.message.ParameterizedMessage; |
27 | 27 | import org.elasticsearch.ElasticsearchException; |
28 | 28 | import org.elasticsearch.Version; |
| 29 | +import org.elasticsearch.cluster.AbstractDiffable; |
29 | 30 | import org.elasticsearch.cluster.ClusterModule; |
30 | 31 | import org.elasticsearch.cluster.ClusterState; |
31 | 32 | import org.elasticsearch.cluster.ClusterStateTaskListener; |
|
55 | 56 | import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; |
56 | 57 | import org.elasticsearch.common.io.stream.NamedWriteableRegistry; |
57 | 58 | import org.elasticsearch.common.io.stream.StreamInput; |
| 59 | +import org.elasticsearch.common.io.stream.StreamOutput; |
58 | 60 | import org.elasticsearch.common.logging.Loggers; |
59 | 61 | import org.elasticsearch.common.settings.ClusterSettings; |
60 | 62 | import org.elasticsearch.common.settings.Setting; |
|
64 | 66 | import org.elasticsearch.common.unit.TimeValue; |
65 | 67 | import org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor; |
66 | 68 | import org.elasticsearch.common.util.set.Sets; |
| 69 | +import org.elasticsearch.common.xcontent.XContentBuilder; |
67 | 70 | import org.elasticsearch.discovery.DiscoveryModule; |
68 | 71 | import org.elasticsearch.discovery.SeedHostsProvider.HostsResolver; |
69 | 72 | import org.elasticsearch.discovery.zen.PublishClusterStateStats; |
|
95 | 98 | import java.util.Optional; |
96 | 99 | import java.util.Set; |
97 | 100 | import java.util.concurrent.Callable; |
| 101 | +import java.util.concurrent.atomic.AtomicBoolean; |
98 | 102 | import java.util.concurrent.atomic.AtomicInteger; |
99 | 103 | import java.util.function.BiConsumer; |
100 | 104 | import java.util.function.Consumer; |
|
137 | 141 | import static org.hamcrest.Matchers.greaterThanOrEqualTo; |
138 | 142 | import static org.hamcrest.Matchers.hasItem; |
139 | 143 | import static org.hamcrest.Matchers.hasSize; |
| 144 | +import static org.hamcrest.Matchers.instanceOf; |
140 | 145 | import static org.hamcrest.Matchers.is; |
141 | 146 | import static org.hamcrest.Matchers.lessThanOrEqualTo; |
142 | 147 | import static org.hamcrest.Matchers.not; |
@@ -1151,6 +1156,67 @@ public void testSingleNodeDiscoveryWithQuorum() { |
1151 | 1156 | cluster.stabilise(); |
1152 | 1157 | } |
1153 | 1158 |
|
| 1159 | + private static class BrokenCustom extends AbstractDiffable<ClusterState.Custom> implements ClusterState.Custom { |
| 1160 | + |
| 1161 | + static final String EXCEPTION_MESSAGE = "simulated"; |
| 1162 | + |
| 1163 | + @Override |
| 1164 | + public String getWriteableName() { |
| 1165 | + return "broken"; |
| 1166 | + } |
| 1167 | + |
| 1168 | + @Override |
| 1169 | + public Version getMinimalSupportedVersion() { |
| 1170 | + return Version.V_EMPTY; |
| 1171 | + } |
| 1172 | + |
| 1173 | + @Override |
| 1174 | + public void writeTo(StreamOutput out) throws IOException { |
| 1175 | + throw new ElasticsearchException(EXCEPTION_MESSAGE); |
| 1176 | + } |
| 1177 | + |
| 1178 | + @Override |
| 1179 | + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { |
| 1180 | + return builder; |
| 1181 | + } |
| 1182 | + } |
| 1183 | + |
| 1184 | + public void testClusterRecoversAfterExceptionDuringSerialization() { |
| 1185 | + final Cluster cluster = new Cluster(randomIntBetween(2, 5)); // 1-node cluster doesn't do any serialization |
| 1186 | + cluster.runRandomly(); |
| 1187 | + cluster.stabilise(); |
| 1188 | + |
| 1189 | + final ClusterNode leader1 = cluster.getAnyLeader(); |
| 1190 | + |
| 1191 | + logger.info("--> submitting broken task to [{}]", leader1); |
| 1192 | + |
| 1193 | + final AtomicBoolean failed = new AtomicBoolean(); |
| 1194 | + leader1.submitUpdateTask("broken-task", |
| 1195 | + cs -> ClusterState.builder(cs).putCustom("broken", new BrokenCustom()).build(), |
| 1196 | + (source, e) -> { |
| 1197 | + assertThat(e.getCause(), instanceOf(ElasticsearchException.class)); |
| 1198 | + assertThat(e.getCause().getMessage(), equalTo(BrokenCustom.EXCEPTION_MESSAGE)); |
| 1199 | + failed.set(true); |
| 1200 | + }); |
| 1201 | + cluster.runFor(DEFAULT_DELAY_VARIABILITY + 1, "processing broken task"); |
| 1202 | + assertTrue(failed.get()); |
| 1203 | + |
| 1204 | + cluster.stabilise(); |
| 1205 | + |
| 1206 | + final ClusterNode leader2 = cluster.getAnyLeader(); |
| 1207 | + long finalValue = randomLong(); |
| 1208 | + |
| 1209 | + logger.info("--> submitting value [{}] to [{}]", finalValue, leader2); |
| 1210 | + leader2.submitValue(finalValue); |
| 1211 | + cluster.stabilise(DEFAULT_CLUSTER_STATE_UPDATE_DELAY); |
| 1212 | + |
| 1213 | + for (final ClusterNode clusterNode : cluster.clusterNodes) { |
| 1214 | + final String nodeId = clusterNode.getId(); |
| 1215 | + final ClusterState appliedState = clusterNode.getLastAppliedClusterState(); |
| 1216 | + assertThat(nodeId + " has the applied value", value(appliedState), is(finalValue)); |
| 1217 | + } |
| 1218 | + } |
| 1219 | + |
1154 | 1220 | private static long defaultMillis(Setting<TimeValue> setting) { |
1155 | 1221 | return setting.get(Settings.EMPTY).millis() + Cluster.DEFAULT_DELAY_VARIABILITY; |
1156 | 1222 | } |
|
0 commit comments