Skip to content

Commit 03bd55d

Browse files
authored
[7.x] Deprecate returning 408 for a server timeout on _cluster/health (#78180) (#78940)
Backports #78180 to 7.x. Add a deprecation warning and a system property es.cluster_health.request_timeout_200 to opt in for returning 200 which will be the default in 8.0.0 Fixes #70849
1 parent 5d9001c commit 03bd55d

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ public void testClusterHealthNotFoundIndex() throws IOException {
304304
assertThat(response.status(), equalTo(RestStatus.REQUEST_TIMEOUT));
305305
assertThat(response.getStatus(), equalTo(ClusterHealthStatus.RED));
306306
assertNoIndices(response);
307+
assertWarnings("The HTTP status code for a cluster health timeout will be changed from 408 to 200 in a " +
308+
"future version. Set the [es.cluster_health.request_timeout_200] system property to [true] to suppress this message and " +
309+
"opt in to the future behaviour now.");
307310
}
308311

309312
public void testRemoteInfo() throws Exception {

server/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthResponse.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.elasticsearch.cluster.health.ClusterHealthStatus;
1414
import org.elasticsearch.cluster.health.ClusterIndexHealth;
1515
import org.elasticsearch.cluster.health.ClusterStateHealth;
16+
import org.elasticsearch.common.logging.DeprecationCategory;
17+
import org.elasticsearch.common.logging.DeprecationLogger;
1618
import org.elasticsearch.common.xcontent.ParseField;
1719
import org.elasticsearch.common.Strings;
1820
import org.elasticsearch.common.io.stream.StreamInput;
@@ -24,6 +26,7 @@
2426
import org.elasticsearch.common.xcontent.XContentBuilder;
2527
import org.elasticsearch.common.xcontent.XContentParser;
2628
import org.elasticsearch.rest.RestStatus;
29+
import org.elasticsearch.rest.action.search.RestSearchAction;
2730

2831
import java.io.IOException;
2932
import java.util.HashMap;
@@ -55,6 +58,7 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo
5558
private static final String INITIALIZING_SHARDS = "initializing_shards";
5659
private static final String UNASSIGNED_SHARDS = "unassigned_shards";
5760
private static final String INDICES = "indices";
61+
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(RestSearchAction.class);
5862

5963
private static final ConstructingObjectParser<ClusterHealthResponse, Void> PARSER =
6064
new ConstructingObjectParser<>("cluster_health_response", true,
@@ -97,7 +101,11 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo
97101
});
98102

99103
private static final ObjectParser.NamedObjectParser<ClusterIndexHealth, Void> INDEX_PARSER =
100-
(XContentParser parser, Void context, String index) -> ClusterIndexHealth.innerFromXContent(parser, index);
104+
(XContentParser parser, Void context, String index) -> ClusterIndexHealth.innerFromXContent(parser, index);
105+
private static final String ES_CLUSTER_HEALTH_REQUEST_TIMEOUT_200_KEY = "es.cluster_health.request_timeout_200";
106+
static final String CLUSTER_HEALTH_REQUEST_TIMEOUT_DEPRECATION_MSG = "The HTTP status code for a cluster health timeout " +
107+
"will be changed from 408 to 200 in a future version. Set the [" + ES_CLUSTER_HEALTH_REQUEST_TIMEOUT_200_KEY + "] " +
108+
"system property to [true] to suppress this message and opt in to the future behaviour now.";
101109

102110
static {
103111
// ClusterStateHealth fields
@@ -130,8 +138,15 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo
130138
private boolean timedOut = false;
131139
private ClusterStateHealth clusterStateHealth;
132140
private ClusterHealthStatus clusterHealthStatus;
141+
private boolean esClusterHealthRequestTimeout200 = readEsClusterHealthRequestTimeout200FromProperty();
133142

134-
public ClusterHealthResponse() {}
143+
public ClusterHealthResponse() {
144+
}
145+
146+
/** For the testing of opting in for the 200 status code without setting a system property */
147+
ClusterHealthResponse(boolean esClusterHealthRequestTimeout200) {
148+
this.esClusterHealthRequestTimeout200 = esClusterHealthRequestTimeout200;
149+
}
135150

136151
public ClusterHealthResponse(StreamInput in) throws IOException {
137152
super(in);
@@ -299,7 +314,16 @@ public String toString() {
299314

300315
@Override
301316
public RestStatus status() {
302-
return isTimedOut() ? RestStatus.REQUEST_TIMEOUT : RestStatus.OK;
317+
if (isTimedOut() == false) {
318+
return RestStatus.OK;
319+
}
320+
if (esClusterHealthRequestTimeout200) {
321+
return RestStatus.OK;
322+
} else {
323+
deprecationLogger.critical(DeprecationCategory.API,"cluster_health_request_timeout",
324+
CLUSTER_HEALTH_REQUEST_TIMEOUT_DEPRECATION_MSG);
325+
return RestStatus.REQUEST_TIMEOUT;
326+
}
303327
}
304328

305329
@Override
@@ -359,4 +383,17 @@ public int hashCode() {
359383
return Objects.hash(clusterName, numberOfPendingTasks, numberOfInFlightFetch, delayedUnassignedShards, taskMaxWaitingTime,
360384
timedOut, clusterStateHealth, clusterHealthStatus);
361385
}
386+
387+
private static boolean readEsClusterHealthRequestTimeout200FromProperty() {
388+
String property = System.getProperty(ES_CLUSTER_HEALTH_REQUEST_TIMEOUT_200_KEY);
389+
if (property == null) {
390+
return false;
391+
}
392+
if (Boolean.parseBoolean(property)) {
393+
return true;
394+
} else {
395+
throw new IllegalArgumentException(ES_CLUSTER_HEALTH_REQUEST_TIMEOUT_200_KEY + " can only be unset or [true] but was ["
396+
+ property + "]");
397+
}
398+
}
362399
}

server/src/test/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthResponsesTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,21 @@ public void testIsTimeout() {
4848
res.setTimedOut(randomBoolean());
4949
if (res.isTimedOut()) {
5050
assertEquals(RestStatus.REQUEST_TIMEOUT, res.status());
51+
assertWarnings(ClusterHealthResponse.CLUSTER_HEALTH_REQUEST_TIMEOUT_DEPRECATION_MSG);
5152
} else {
5253
assertEquals(RestStatus.OK, res.status());
5354
}
5455
}
5556
}
5657

58+
public void testTimeoutReturns200IfOptedIn() {
59+
ClusterHealthResponse res = new ClusterHealthResponse(true);
60+
for (int i = 0; i < 5; i++) {
61+
res.setTimedOut(randomBoolean());
62+
assertEquals(RestStatus.OK, res.status());
63+
}
64+
}
65+
5766
public void testClusterHealth() throws IOException {
5867
ClusterState clusterState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).build();
5968
int pendingTasks = randomIntBetween(0, 200);

0 commit comments

Comments
 (0)