Skip to content

Commit cedf204

Browse files
authored
Backport the Close Index API refactoring to 6.x (#37359)
This commit backports to 6.x of the Close Index API refactoring. It cherry-picks the following commits from master: 3ca885e [Close Index API] Add TransportShardCloseAction for pre-closing verifications (#36249) 8e5dd20 [Close Index API] Refactor MetaDataIndexStateService (#36354) 7372529 [Tests] Reduce randomization in CloseWhileRelocatingShardsIT (#36694) 103c4d4 [Close Index API] Mark unavailable shard copy as stale during verification (#36755) 1959388 [Close Index API] Propagate tasks ids between Freeze, Close and Verify(#36630) e149b08 [Close Index API] Add unique UUID to ClusterBlock (#36775) dc371ef [Tests] Fix ReopenWhileClosingIT with correct min num shards The following two commits were needed to adapt the change to 6.x: ef6ae69 [Close Index API] Adapt MetaDataIndexStateServiceTests after merge 21b7653 [Tests] Adapt CloseIndexIT tests for 6.x Related to #33888
1 parent f2c9051 commit cedf204

File tree

23 files changed

+2282
-274
lines changed

23 files changed

+2282
-274
lines changed

distribution/archives/integ-test-zip/src/test/java/org/elasticsearch/test/rest/WaitForRefreshAndCloseIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
import org.apache.http.util.EntityUtils;
2323
import org.elasticsearch.action.ActionFuture;
2424
import org.elasticsearch.action.support.PlainActionFuture;
25+
import org.elasticsearch.client.Request;
2526
import org.elasticsearch.client.Response;
2627
import org.elasticsearch.client.ResponseException;
2728
import org.elasticsearch.client.ResponseListener;
28-
import org.elasticsearch.client.Request;
2929
import org.junit.After;
3030
import org.junit.Before;
3131

server/src/main/java/org/elasticsearch/action/admin/indices/close/CloseIndexClusterStateUpdateRequest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@
2525
*/
2626
public class CloseIndexClusterStateUpdateRequest extends IndicesClusterStateUpdateRequest<CloseIndexClusterStateUpdateRequest> {
2727

28-
CloseIndexClusterStateUpdateRequest() {
28+
private final long taskId;
2929

30+
public CloseIndexClusterStateUpdateRequest(final long taskId) {
31+
this.taskId = taskId;
32+
}
33+
34+
public long taskId() {
35+
return taskId;
3036
}
3137
}

server/src/main/java/org/elasticsearch/action/admin/indices/close/TransportCloseIndexAction.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.elasticsearch.action.support.master.AcknowledgedResponse;
2727
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
2828
import org.elasticsearch.cluster.ClusterState;
29-
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
3029
import org.elasticsearch.cluster.block.ClusterBlockException;
3130
import org.elasticsearch.cluster.block.ClusterBlockLevel;
3231
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
@@ -100,24 +99,32 @@ protected ClusterBlockException checkBlock(CloseIndexRequest request, ClusterSta
10099
@Override
101100
protected void masterOperation(final CloseIndexRequest request, final ClusterState state,
102101
final ActionListener<AcknowledgedResponse> listener) {
102+
throw new UnsupportedOperationException("The task parameter is required");
103+
}
104+
105+
@Override
106+
protected void masterOperation(final Task task, final CloseIndexRequest request, final ClusterState state,
107+
final ActionListener<AcknowledgedResponse> listener) throws Exception {
103108
final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request);
104109
if (concreteIndices == null || concreteIndices.length == 0) {
105110
listener.onResponse(new AcknowledgedResponse(true));
106111
return;
107112
}
108-
CloseIndexClusterStateUpdateRequest updateRequest = new CloseIndexClusterStateUpdateRequest()
109-
.ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout())
110-
.indices(concreteIndices);
111113

112-
indexStateService.closeIndices(updateRequest, new ActionListener<ClusterStateUpdateResponse>() {
114+
final CloseIndexClusterStateUpdateRequest closeRequest = new CloseIndexClusterStateUpdateRequest(task.getId())
115+
.ackTimeout(request.timeout())
116+
.masterNodeTimeout(request.masterNodeTimeout())
117+
.indices(concreteIndices);
118+
119+
indexStateService.closeIndices(closeRequest, new ActionListener<AcknowledgedResponse>() {
113120

114121
@Override
115-
public void onResponse(ClusterStateUpdateResponse response) {
116-
listener.onResponse(new AcknowledgedResponse(response.isAcknowledged()));
122+
public void onResponse(final AcknowledgedResponse response) {
123+
listener.onResponse(response);
117124
}
118125

119126
@Override
120-
public void onFailure(Exception t) {
127+
public void onFailure(final Exception t) {
121128
logger.debug(() -> new ParameterizedMessage("failed to close indices [{}]", (Object) concreteIndices), t);
122129
listener.onFailure(t);
123130
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.action.admin.indices.close;
20+
21+
import org.elasticsearch.action.ActionListener;
22+
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
23+
import org.elasticsearch.action.support.ActionFilters;
24+
import org.elasticsearch.action.support.replication.ReplicationOperation;
25+
import org.elasticsearch.action.support.replication.ReplicationRequest;
26+
import org.elasticsearch.action.support.replication.ReplicationResponse;
27+
import org.elasticsearch.action.support.replication.TransportReplicationAction;
28+
import org.elasticsearch.cluster.action.shard.ShardStateAction;
29+
import org.elasticsearch.cluster.block.ClusterBlock;
30+
import org.elasticsearch.cluster.block.ClusterBlocks;
31+
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
32+
import org.elasticsearch.cluster.service.ClusterService;
33+
import org.elasticsearch.common.inject.Inject;
34+
import org.elasticsearch.common.io.stream.StreamInput;
35+
import org.elasticsearch.common.io.stream.StreamOutput;
36+
import org.elasticsearch.common.lease.Releasable;
37+
import org.elasticsearch.common.settings.Settings;
38+
import org.elasticsearch.index.shard.IndexShard;
39+
import org.elasticsearch.index.shard.ShardId;
40+
import org.elasticsearch.indices.IndicesService;
41+
import org.elasticsearch.tasks.TaskId;
42+
import org.elasticsearch.threadpool.ThreadPool;
43+
import org.elasticsearch.transport.TransportService;
44+
45+
import java.io.IOException;
46+
import java.util.Objects;
47+
import java.util.function.Consumer;
48+
49+
public class TransportVerifyShardBeforeCloseAction extends TransportReplicationAction<
50+
TransportVerifyShardBeforeCloseAction.ShardRequest, TransportVerifyShardBeforeCloseAction.ShardRequest, ReplicationResponse> {
51+
52+
public static final String NAME = CloseIndexAction.NAME + "[s]";
53+
54+
@Inject
55+
public TransportVerifyShardBeforeCloseAction(final Settings settings, final TransportService transportService,
56+
final ClusterService clusterService, final IndicesService indicesService,
57+
final ThreadPool threadPool, final ShardStateAction stateAction,
58+
final ActionFilters actionFilters, final IndexNameExpressionResolver resolver) {
59+
super(settings, NAME, transportService, clusterService, indicesService, threadPool, stateAction, actionFilters, resolver,
60+
ShardRequest::new, ShardRequest::new, ThreadPool.Names.MANAGEMENT);
61+
}
62+
63+
@Override
64+
protected ReplicationResponse newResponseInstance() {
65+
return new ReplicationResponse();
66+
}
67+
68+
@Override
69+
protected void acquirePrimaryOperationPermit(final IndexShard primary,
70+
final ShardRequest request,
71+
final ActionListener<Releasable> onAcquired) {
72+
primary.acquireAllPrimaryOperationsPermits(onAcquired, request.timeout());
73+
}
74+
75+
@Override
76+
protected void acquireReplicaOperationPermit(final IndexShard replica,
77+
final ShardRequest request,
78+
final ActionListener<Releasable> onAcquired,
79+
final long primaryTerm,
80+
final long globalCheckpoint,
81+
final long maxSeqNoOfUpdateOrDeletes) {
82+
replica.acquireAllReplicaOperationsPermits(primaryTerm, globalCheckpoint, maxSeqNoOfUpdateOrDeletes, onAcquired, request.timeout());
83+
}
84+
85+
@Override
86+
protected PrimaryResult<ShardRequest, ReplicationResponse> shardOperationOnPrimary(final ShardRequest shardRequest,
87+
final IndexShard primary) throws Exception {
88+
executeShardOperation(shardRequest, primary);
89+
return new PrimaryResult<>(shardRequest, new ReplicationResponse());
90+
}
91+
92+
@Override
93+
protected ReplicaResult shardOperationOnReplica(final ShardRequest shardRequest, final IndexShard replica) throws Exception {
94+
executeShardOperation(shardRequest, replica);
95+
return new ReplicaResult();
96+
}
97+
98+
private void executeShardOperation(final ShardRequest request, final IndexShard indexShard) {
99+
final ShardId shardId = indexShard.shardId();
100+
if (indexShard.getActiveOperationsCount() != 0) {
101+
throw new IllegalStateException("On-going operations in progress while checking index shard " + shardId + " before closing");
102+
}
103+
104+
final ClusterBlocks clusterBlocks = clusterService.state().blocks();
105+
if (clusterBlocks.hasIndexBlock(shardId.getIndexName(), request.clusterBlock()) == false) {
106+
throw new IllegalStateException("Index shard " + shardId + " must be blocked by " + request.clusterBlock() + " before closing");
107+
}
108+
109+
final long maxSeqNo = indexShard.seqNoStats().getMaxSeqNo();
110+
if (indexShard.getGlobalCheckpoint() != maxSeqNo) {
111+
throw new IllegalStateException("Global checkpoint [" + indexShard.getGlobalCheckpoint()
112+
+ "] mismatches maximum sequence number [" + maxSeqNo + "] on index shard " + shardId);
113+
}
114+
indexShard.flush(new FlushRequest());
115+
logger.debug("{} shard is ready for closing", shardId);
116+
}
117+
118+
@Override
119+
protected ReplicationOperation.Replicas<ShardRequest> newReplicasProxy(final long primaryTerm) {
120+
return new VerifyShardBeforeCloseActionReplicasProxy(primaryTerm);
121+
}
122+
123+
/**
124+
* A {@link ReplicasProxy} that marks as stale the shards that are unavailable during the verification
125+
* and the flush of the shard. This is done to ensure that such shards won't be later promoted as primary
126+
* or reopened in an unverified state with potential non flushed translog operations.
127+
*/
128+
class VerifyShardBeforeCloseActionReplicasProxy extends ReplicasProxy {
129+
130+
VerifyShardBeforeCloseActionReplicasProxy(final long primaryTerm) {
131+
super(primaryTerm);
132+
}
133+
134+
@Override
135+
public void markShardCopyAsStaleIfNeeded(final ShardId shardId, final String allocationId, final Runnable onSuccess,
136+
final Consumer<Exception> onPrimaryDemoted, final Consumer<Exception> onIgnoredFailure) {
137+
shardStateAction.remoteShardFailed(shardId, allocationId, primaryTerm, true, "mark copy as stale", null,
138+
createShardActionListener(onSuccess, onPrimaryDemoted, onIgnoredFailure));
139+
}
140+
}
141+
142+
public static class ShardRequest extends ReplicationRequest<ShardRequest> {
143+
144+
private ClusterBlock clusterBlock;
145+
146+
ShardRequest(){
147+
}
148+
149+
public ShardRequest(final ShardId shardId, final ClusterBlock clusterBlock, final TaskId parentTaskId) {
150+
super(shardId);
151+
this.clusterBlock = Objects.requireNonNull(clusterBlock);
152+
setParentTask(parentTaskId);
153+
}
154+
155+
@Override
156+
public String toString() {
157+
return "verify shard " + shardId + " before close with block " + clusterBlock;
158+
}
159+
160+
@Override
161+
public void readFrom(final StreamInput in) throws IOException {
162+
super.readFrom(in);
163+
clusterBlock = ClusterBlock.readClusterBlock(in);
164+
}
165+
166+
@Override
167+
public void writeTo(final StreamOutput out) throws IOException {
168+
super.writeTo(out);
169+
clusterBlock.writeTo(out);
170+
}
171+
172+
public ClusterBlock clusterBlock() {
173+
return clusterBlock;
174+
}
175+
}
176+
}

server/src/main/java/org/elasticsearch/cluster/block/ClusterBlock.java

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.cluster.block;
2121

2222
import org.elasticsearch.Version;
23+
import org.elasticsearch.common.Nullable;
2324
import org.elasticsearch.common.io.stream.StreamInput;
2425
import org.elasticsearch.common.io.stream.StreamOutput;
2526
import org.elasticsearch.common.io.stream.Streamable;
@@ -31,29 +32,31 @@
3132
import java.util.ArrayList;
3233
import java.util.EnumSet;
3334
import java.util.Locale;
35+
import java.util.Objects;
3436

3537
public class ClusterBlock implements Streamable, ToXContentFragment {
3638

3739
private int id;
38-
40+
private @Nullable String uuid;
3941
private String description;
40-
4142
private EnumSet<ClusterBlockLevel> levels;
42-
4343
private boolean retryable;
44-
4544
private boolean disableStatePersistence = false;
46-
4745
private boolean allowReleaseResources;
48-
4946
private RestStatus status;
5047

51-
ClusterBlock() {
48+
private ClusterBlock() {
49+
}
50+
51+
public ClusterBlock(int id, String description, boolean retryable, boolean disableStatePersistence,
52+
boolean allowReleaseResources, RestStatus status, EnumSet<ClusterBlockLevel> levels) {
53+
this(id, null, description, retryable, disableStatePersistence, allowReleaseResources, status, levels);
5254
}
5355

54-
public ClusterBlock(int id, String description, boolean retryable, boolean disableStatePersistence, boolean allowReleaseResources,
55-
RestStatus status, EnumSet<ClusterBlockLevel> levels) {
56+
public ClusterBlock(int id, String uuid, String description, boolean retryable, boolean disableStatePersistence,
57+
boolean allowReleaseResources, RestStatus status, EnumSet<ClusterBlockLevel> levels) {
5658
this.id = id;
59+
this.uuid = uuid;
5760
this.description = description;
5861
this.retryable = retryable;
5962
this.disableStatePersistence = disableStatePersistence;
@@ -66,6 +69,10 @@ public int id() {
6669
return this.id;
6770
}
6871

72+
public String uuid() {
73+
return uuid;
74+
}
75+
6976
public String description() {
7077
return this.description;
7178
}
@@ -105,6 +112,9 @@ public boolean disableStatePersistence() {
105112
@Override
106113
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
107114
builder.startObject(Integer.toString(id));
115+
if (uuid != null) {
116+
builder.field("uuid", uuid);
117+
}
108118
builder.field("description", description);
109119
builder.field("retryable", retryable);
110120
if (disableStatePersistence) {
@@ -128,6 +138,11 @@ public static ClusterBlock readClusterBlock(StreamInput in) throws IOException {
128138
@Override
129139
public void readFrom(StreamInput in) throws IOException {
130140
id = in.readVInt();
141+
if (in.getVersion().onOrAfter(Version.V_6_7_0)) {
142+
uuid = in.readOptionalString();
143+
} else {
144+
uuid = null;
145+
}
131146
description = in.readString();
132147
final int len = in.readVInt();
133148
ArrayList<ClusterBlockLevel> levels = new ArrayList<>(len);
@@ -148,6 +163,9 @@ public void readFrom(StreamInput in) throws IOException {
148163
@Override
149164
public void writeTo(StreamOutput out) throws IOException {
150165
out.writeVInt(id);
166+
if (out.getVersion().onOrAfter(Version.V_6_7_0)) {
167+
out.writeOptionalString(uuid);
168+
}
151169
out.writeString(description);
152170
out.writeVInt(levels.size());
153171
for (ClusterBlockLevel level : levels) {
@@ -164,7 +182,11 @@ public void writeTo(StreamOutput out) throws IOException {
164182
@Override
165183
public String toString() {
166184
StringBuilder sb = new StringBuilder();
167-
sb.append(id).append(",").append(description).append(", blocks ");
185+
sb.append(id).append(",");
186+
if (uuid != null) {
187+
sb.append(uuid).append(',');
188+
}
189+
sb.append(description).append(", blocks ");
168190
String delimiter = "";
169191
for (ClusterBlockLevel level : levels) {
170192
sb.append(delimiter).append(level.name());
@@ -175,19 +197,19 @@ public String toString() {
175197

176198
@Override
177199
public boolean equals(Object o) {
178-
if (this == o) return true;
179-
if (o == null || getClass() != o.getClass()) return false;
180-
181-
ClusterBlock that = (ClusterBlock) o;
182-
183-
if (id != that.id) return false;
184-
185-
return true;
200+
if (this == o) {
201+
return true;
202+
}
203+
if (o == null || getClass() != o.getClass()) {
204+
return false;
205+
}
206+
final ClusterBlock that = (ClusterBlock) o;
207+
return id == that.id && Objects.equals(uuid, that.uuid);
186208
}
187209

188210
@Override
189211
public int hashCode() {
190-
return id;
212+
return Objects.hash(id, uuid);
191213
}
192214

193215
public boolean isAllowReleaseResources() {

0 commit comments

Comments
 (0)