Skip to content

Commit d78f40f

Browse files
author
Ali Beyad
authored
Index creation waits for active shard copies before returning (#18985)
Before returning, index creation now waits for the configured number of shard copies to be started. In the past, a client would create an index and then potentially have to check the cluster health to wait to execute write operations. With the cluster health semantics changing so that index creation does not cause the cluster health to go RED, this change enables waiting for the desired number of active shards to be active before returning from index creation. Relates #9126
1 parent 7759c23 commit d78f40f

File tree

47 files changed

+1282
-121
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1282
-121
lines changed

core/src/main/java/org/elasticsearch/action/ActionListener.java

+29
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package org.elasticsearch.action;
2121

22+
import java.util.function.Consumer;
23+
2224
/**
2325
* A listener for action responses or failures.
2426
*/
@@ -33,4 +35,31 @@ public interface ActionListener<Response> {
3335
* A failure caused by an exception at some phase of the task.
3436
*/
3537
void onFailure(Exception e);
38+
39+
/**
40+
* Creates a listener that listens for a response (or failure) and executes the
41+
* corresponding consumer when the response (or failure) is received.
42+
*
43+
* @param onResponse the consumer of the response, when the listener receives one
44+
* @param onFailure the consumer of the failure, when the listener receives one
45+
* @param <Response> the type of the response
46+
* @return a listener that listens for responses and invokes the consumer when received
47+
*/
48+
static <Response> ActionListener<Response> wrap(Consumer<Response> onResponse, Consumer<Exception> onFailure) {
49+
return new ActionListener<Response>() {
50+
@Override
51+
public void onResponse(Response response) {
52+
try {
53+
onResponse.accept(response);
54+
} catch (Exception e) {
55+
onFailure(e);
56+
}
57+
}
58+
59+
@Override
60+
public void onFailure(Exception e) {
61+
onFailure.accept(e);
62+
}
63+
};
64+
}
3665
}

core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java

+12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.action.admin.indices.create;
2121

2222
import org.elasticsearch.action.admin.indices.alias.Alias;
23+
import org.elasticsearch.action.support.ActiveShardCount;
2324
import org.elasticsearch.cluster.ack.ClusterStateUpdateRequest;
2425
import org.elasticsearch.cluster.block.ClusterBlock;
2526
import org.elasticsearch.cluster.metadata.IndexMetaData;
@@ -55,6 +56,8 @@ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequ
5556

5657
private final Set<ClusterBlock> blocks = new HashSet<>();
5758

59+
private ActiveShardCount waitForActiveShards = ActiveShardCount.DEFAULT;
60+
5861

5962
public CreateIndexClusterStateUpdateRequest(TransportMessage originalMessage, String cause, String index, boolean updateAllTypes) {
6063
this.originalMessage = originalMessage;
@@ -98,6 +101,11 @@ public CreateIndexClusterStateUpdateRequest shrinkFrom(Index shrinkFrom) {
98101
return this;
99102
}
100103

104+
public CreateIndexClusterStateUpdateRequest waitForActiveShards(ActiveShardCount waitForActiveShards) {
105+
this.waitForActiveShards = waitForActiveShards;
106+
return this;
107+
}
108+
101109
public TransportMessage originalMessage() {
102110
return originalMessage;
103111
}
@@ -142,4 +150,8 @@ public Index shrinkFrom() {
142150
public boolean updateAllTypes() {
143151
return updateAllTypes;
144152
}
153+
154+
public ActiveShardCount waitForActiveShards() {
155+
return waitForActiveShards;
156+
}
145157
}

core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequest.java

+29
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.action.IndicesRequest;
2626
import org.elasticsearch.action.admin.indices.alias.Alias;
2727
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
28+
import org.elasticsearch.action.support.ActiveShardCount;
2829
import org.elasticsearch.action.support.IndicesOptions;
2930
import org.elasticsearch.action.support.master.AcknowledgedRequest;
3031
import org.elasticsearch.cluster.metadata.IndexMetaData;
@@ -77,6 +78,8 @@ public class CreateIndexRequest extends AcknowledgedRequest<CreateIndexRequest>
7778

7879
private boolean updateAllTypes = false;
7980

81+
private ActiveShardCount waitForActiveShards = ActiveShardCount.DEFAULT;
82+
8083
public CreateIndexRequest() {
8184
}
8285

@@ -440,6 +443,30 @@ public CreateIndexRequest updateAllTypes(boolean updateAllTypes) {
440443
return this;
441444
}
442445

446+
public ActiveShardCount waitForActiveShards() {
447+
return waitForActiveShards;
448+
}
449+
450+
/**
451+
* Sets the number of shard copies that should be active for index creation to return.
452+
* Defaults to {@link ActiveShardCount#DEFAULT}, which will wait for one shard copy
453+
* (the primary) to become active. Set this value to {@link ActiveShardCount#ALL} to
454+
* wait for all shards (primary and all replicas) to be active before returning.
455+
* Otherwise, use {@link ActiveShardCount#from(int)} to set this value to any
456+
* non-negative integer, up to the number of copies per shard (number of replicas + 1),
457+
* to wait for the desired amount of shard copies to become active before returning.
458+
* Index creation will only wait up until the timeout value for the number of shard copies
459+
* to be active before returning. Check {@link CreateIndexResponse#isShardsAcked()} to
460+
* determine if the requisite shard copies were all started before returning or timing out.
461+
*
462+
* @param waitForActiveShards number of active shard copies to wait on
463+
*/
464+
public CreateIndexRequest waitForActiveShards(ActiveShardCount waitForActiveShards) {
465+
this.waitForActiveShards = waitForActiveShards;
466+
return this;
467+
}
468+
469+
443470
@Override
444471
public void readFrom(StreamInput in) throws IOException {
445472
super.readFrom(in);
@@ -462,6 +489,7 @@ public void readFrom(StreamInput in) throws IOException {
462489
aliases.add(Alias.read(in));
463490
}
464491
updateAllTypes = in.readBoolean();
492+
waitForActiveShards = ActiveShardCount.readFrom(in);
465493
}
466494

467495
@Override
@@ -486,5 +514,6 @@ public void writeTo(StreamOutput out) throws IOException {
486514
alias.writeTo(out);
487515
}
488516
out.writeBoolean(updateAllTypes);
517+
waitForActiveShards.writeTo(out);
489518
}
490519
}

core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexRequestBuilder.java

+20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.action.admin.indices.create;
2121

2222
import org.elasticsearch.action.admin.indices.alias.Alias;
23+
import org.elasticsearch.action.support.ActiveShardCount;
2324
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
2425
import org.elasticsearch.client.ElasticsearchClient;
2526
import org.elasticsearch.cluster.metadata.IndexMetaData;
@@ -249,4 +250,23 @@ public CreateIndexRequestBuilder setUpdateAllTypes(boolean updateAllTypes) {
249250
request.updateAllTypes(updateAllTypes);
250251
return this;
251252
}
253+
254+
/**
255+
* Sets the number of shard copies that should be active for index creation to return.
256+
* Defaults to {@link ActiveShardCount#DEFAULT}, which will wait for one shard copy
257+
* (the primary) to become active. Set this value to {@link ActiveShardCount#ALL} to
258+
* wait for all shards (primary and all replicas) to be active before returning.
259+
* Otherwise, use {@link ActiveShardCount#from(int)} to set this value to any
260+
* non-negative integer, up to the number of copies per shard (number of replicas + 1),
261+
* to wait for the desired amount of shard copies to become active before returning.
262+
* Index creation will only wait up until the timeout value for the number of shard copies
263+
* to be active before returning. Check {@link CreateIndexResponse#isShardsAcked()} to
264+
* determine if the requisite shard copies were all started before returning or timing out.
265+
*
266+
* @param waitForActiveShards number of active shard copies to wait on
267+
*/
268+
public CreateIndexRequestBuilder setWaitForActiveShards(ActiveShardCount waitForActiveShards) {
269+
request.waitForActiveShards(waitForActiveShards);
270+
return this;
271+
}
252272
}

core/src/main/java/org/elasticsearch/action/admin/indices/create/CreateIndexResponse.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.action.support.master.AcknowledgedResponse;
2323
import org.elasticsearch.common.io.stream.StreamInput;
2424
import org.elasticsearch.common.io.stream.StreamOutput;
25+
import org.elasticsearch.common.xcontent.XContentBuilder;
2526

2627
import java.io.IOException;
2728

@@ -30,22 +31,41 @@
3031
*/
3132
public class CreateIndexResponse extends AcknowledgedResponse {
3233

34+
private boolean shardsAcked;
35+
3336
protected CreateIndexResponse() {
3437
}
3538

36-
protected CreateIndexResponse(boolean acknowledged) {
39+
protected CreateIndexResponse(boolean acknowledged, boolean shardsAcked) {
3740
super(acknowledged);
41+
assert acknowledged || shardsAcked == false; // if its not acknowledged, then shards acked should be false too
42+
this.shardsAcked = shardsAcked;
3843
}
3944

4045
@Override
4146
public void readFrom(StreamInput in) throws IOException {
4247
super.readFrom(in);
4348
readAcknowledged(in);
49+
shardsAcked = in.readBoolean();
4450
}
4551

4652
@Override
4753
public void writeTo(StreamOutput out) throws IOException {
4854
super.writeTo(out);
4955
writeAcknowledged(out);
56+
out.writeBoolean(shardsAcked);
57+
}
58+
59+
/**
60+
* Returns true if the requisite number of shards were started before
61+
* returning from the index creation operation. If {@link #isAcknowledged()}
62+
* is false, then this also returns false.
63+
*/
64+
public boolean isShardsAcked() {
65+
return shardsAcked;
66+
}
67+
68+
public void addCustomFields(XContentBuilder builder) throws IOException {
69+
builder.field("shards_acknowledged", isShardsAcked());
5070
}
5171
}

core/src/main/java/org/elasticsearch/action/admin/indices/create/TransportCreateIndexAction.java

+6-20
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,13 @@
2323
import org.elasticsearch.action.support.ActionFilters;
2424
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
2525
import org.elasticsearch.cluster.ClusterState;
26-
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
2726
import org.elasticsearch.cluster.block.ClusterBlockException;
2827
import org.elasticsearch.cluster.block.ClusterBlockLevel;
2928
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
3029
import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
3130
import org.elasticsearch.cluster.service.ClusterService;
3231
import org.elasticsearch.common.inject.Inject;
3332
import org.elasticsearch.common.settings.Settings;
34-
import org.elasticsearch.indices.IndexAlreadyExistsException;
3533
import org.elasticsearch.threadpool.ThreadPool;
3634
import org.elasticsearch.transport.TransportService;
3735

@@ -77,24 +75,12 @@ protected void masterOperation(final CreateIndexRequest request, final ClusterSt
7775
final CreateIndexClusterStateUpdateRequest updateRequest = new CreateIndexClusterStateUpdateRequest(request, cause, indexName, request.updateAllTypes())
7876
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
7977
.settings(request.settings()).mappings(request.mappings())
80-
.aliases(request.aliases()).customs(request.customs());
78+
.aliases(request.aliases()).customs(request.customs())
79+
.waitForActiveShards(request.waitForActiveShards());
8180

82-
createIndexService.createIndex(updateRequest, new ActionListener<ClusterStateUpdateResponse>() {
83-
84-
@Override
85-
public void onResponse(ClusterStateUpdateResponse response) {
86-
listener.onResponse(new CreateIndexResponse(response.isAcknowledged()));
87-
}
88-
89-
@Override
90-
public void onFailure(Exception t) {
91-
if (t instanceof IndexAlreadyExistsException) {
92-
logger.trace("[{}] failed to create", t, request.index());
93-
} else {
94-
logger.debug("[{}] failed to create", t, request.index());
95-
}
96-
listener.onFailure(t);
97-
}
98-
});
81+
createIndexService.createIndex(updateRequest, ActionListener.wrap(response ->
82+
listener.onResponse(new CreateIndexResponse(response.isAcknowledged(), response.isShardsAcked())),
83+
listener::onFailure));
9984
}
85+
10086
}

core/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequest.java

+19
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.action.ActionRequestValidationException;
2323
import org.elasticsearch.action.IndicesRequest;
2424
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
25+
import org.elasticsearch.action.support.ActiveShardCount;
2526
import org.elasticsearch.action.support.IndicesOptions;
2627
import org.elasticsearch.action.support.master.AcknowledgedRequest;
2728
import org.elasticsearch.common.ParseField;
@@ -206,4 +207,22 @@ public void source(BytesReference source) {
206207
}
207208
}
208209

210+
/**
211+
* Sets the number of shard copies that should be active for creation of the
212+
* new rollover index to return. Defaults to {@link ActiveShardCount#DEFAULT}, which will
213+
* wait for one shard copy (the primary) to become active. Set this value to
214+
* {@link ActiveShardCount#ALL} to wait for all shards (primary and all replicas) to be active
215+
* before returning. Otherwise, use {@link ActiveShardCount#from(int)} to set this value to any
216+
* non-negative integer, up to the number of copies per shard (number of replicas + 1),
217+
* to wait for the desired amount of shard copies to become active before returning.
218+
* Index creation will only wait up until the timeout value for the number of shard copies
219+
* to be active before returning. Check {@link RolloverResponse#isShardsAcked()} to
220+
* determine if the requisite shard copies were all started before returning or timing out.
221+
*
222+
* @param waitForActiveShards number of active shard copies to wait on
223+
*/
224+
public void setWaitForActiveShards(ActiveShardCount waitForActiveShards) {
225+
this.createIndexRequest.waitForActiveShards(waitForActiveShards);
226+
}
227+
209228
}

core/src/main/java/org/elasticsearch/action/admin/indices/rollover/RolloverRequestBuilder.java

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.elasticsearch.action.admin.indices.rollover;
2020

2121
import org.elasticsearch.action.admin.indices.alias.Alias;
22+
import org.elasticsearch.action.support.ActiveShardCount;
2223
import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
2324
import org.elasticsearch.client.ElasticsearchClient;
2425
import org.elasticsearch.common.settings.Settings;
@@ -70,4 +71,23 @@ public RolloverRequestBuilder mapping(String type, String source) {
7071
this.request.getCreateIndexRequest().mapping(type, source);
7172
return this;
7273
}
74+
75+
/**
76+
* Sets the number of shard copies that should be active for creation of the
77+
* new rollover index to return. Defaults to {@link ActiveShardCount#DEFAULT}, which will
78+
* wait for one shard copy (the primary) to become active. Set this value to
79+
* {@link ActiveShardCount#ALL} to wait for all shards (primary and all replicas) to be active
80+
* before returning. Otherwise, use {@link ActiveShardCount#from(int)} to set this value to any
81+
* non-negative integer, up to the number of copies per shard (number of replicas + 1),
82+
* to wait for the desired amount of shard copies to become active before returning.
83+
* Index creation will only wait up until the timeout value for the number of shard copies
84+
* to be active before returning. Check {@link RolloverResponse#isShardsAcked()} to
85+
* determine if the requisite shard copies were all started before returning or timing out.
86+
*
87+
* @param waitForActiveShards number of active shard copies to wait on
88+
*/
89+
public RolloverRequestBuilder waitForActiveShards(ActiveShardCount waitForActiveShards) {
90+
this.request.setWaitForActiveShards(waitForActiveShards);
91+
return this;
92+
}
7393
}

0 commit comments

Comments
 (0)