Skip to content

Commit 2e89040

Browse files
authored
Fail earlier Put Follow requests for closed leader indices (#47638)
Backport of #47582 Today when following a new leader index, we fetch the remote cluster state, check the remote cluster license, check the user privileges, retrieve the index shard stats before initiating a CCR restore session. But if the leader index to follow is closed, we're executing a bunch of operations that would inevitability fail at some point (on retrieving the index shard stats, because this type of request forbid closed indices when resolving indices). We could fail a Put Follow request at the first step by checking the leader index state directly from the remote cluster state. This also helps the Resume Follow API to fail a bit earlier.
1 parent a345640 commit 2e89040

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/CcrLicenseChecker.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.elasticsearch.index.engine.CommitStats;
3333
import org.elasticsearch.index.engine.Engine;
3434
import org.elasticsearch.index.shard.ShardId;
35+
import org.elasticsearch.indices.IndexClosedException;
3536
import org.elasticsearch.license.RemoteClusterLicenseChecker;
3637
import org.elasticsearch.license.XPackLicenseState;
3738
import org.elasticsearch.rest.RestStatus;
@@ -130,7 +131,10 @@ public void checkRemoteClusterLicenseAndFetchLeaderIndexMetadataAndHistoryUUIDs(
130131
onFailure.accept(new IndexNotFoundException(leaderIndex));
131132
return;
132133
}
133-
134+
if (leaderIndexMetaData.getState() == IndexMetaData.State.CLOSE) {
135+
onFailure.accept(new IndexClosedException(leaderIndexMetaData.getIndex()));
136+
return;
137+
}
134138
final Client remoteClient = client.getRemoteClusterClient(clusterAlias);
135139
hasPrivilegesToFollowIndices(remoteClient, new String[] {leaderIndex}, e -> {
136140
if (e == null) {

x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/IndexFollowingIT.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import org.elasticsearch.index.seqno.ReplicationTracker;
7373
import org.elasticsearch.index.seqno.RetentionLeaseActions;
7474
import org.elasticsearch.index.shard.ShardId;
75+
import org.elasticsearch.indices.IndexClosedException;
7576
import org.elasticsearch.persistent.PersistentTasksCustomMetaData;
7677
import org.elasticsearch.plugins.Plugin;
7778
import org.elasticsearch.rest.RestStatus;
@@ -110,6 +111,7 @@
110111
import java.util.function.BooleanSupplier;
111112
import java.util.function.Consumer;
112113
import java.util.stream.Collectors;
114+
import java.util.stream.IntStream;
113115
import java.util.stream.Stream;
114116

115117
import static java.util.Collections.singletonMap;
@@ -744,6 +746,47 @@ public void testDeleteLeaderIndex() throws Exception {
744746
ensureNoCcrTasks();
745747
}
746748

749+
public void testFollowClosedIndex() {
750+
final String leaderIndex = "test-index";
751+
assertAcked(leaderClient().admin().indices().prepareCreate(leaderIndex)
752+
.setSettings(Settings.builder()
753+
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true)
754+
.build()));
755+
assertAcked(leaderClient().admin().indices().prepareClose(leaderIndex));
756+
757+
final String followerIndex = "follow-test-index";
758+
expectThrows(IndexClosedException.class,
759+
() -> followerClient().execute(PutFollowAction.INSTANCE, putFollow(leaderIndex, followerIndex)).actionGet());
760+
assertFalse(followerClient().admin().indices().prepareExists(followerIndex).get().isExists());
761+
}
762+
763+
public void testResumeFollowOnClosedIndex() throws Exception {
764+
final String leaderIndex = "test-index";
765+
assertAcked(leaderClient().admin().indices().prepareCreate(leaderIndex)
766+
.setSettings(Settings.builder()
767+
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true)
768+
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
769+
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
770+
.build()));
771+
ensureLeaderGreen(leaderIndex);
772+
773+
final int nbDocs = randomIntBetween(10, 100);
774+
IntStream.of(nbDocs).forEach(i -> leaderClient().prepareIndex().setIndex(leaderIndex).setSource("field", i).get());
775+
776+
final String followerIndex = "follow-test-index";
777+
PutFollowAction.Response response =
778+
followerClient().execute(PutFollowAction.INSTANCE, putFollow(leaderIndex, followerIndex)).actionGet();
779+
assertTrue(response.isFollowIndexCreated());
780+
assertTrue(response.isFollowIndexShardsAcked());
781+
assertTrue(response.isIndexFollowingStarted());
782+
783+
pauseFollow(followerIndex);
784+
assertAcked(leaderClient().admin().indices().prepareClose(leaderIndex));
785+
786+
expectThrows(IndexClosedException.class, () ->
787+
followerClient().execute(ResumeFollowAction.INSTANCE, resumeFollow(followerIndex)).actionGet());
788+
}
789+
747790
public void testDeleteFollowerIndex() throws Exception {
748791
assertAcked(leaderClient().admin().indices().prepareCreate("index1")
749792
.setSettings(Settings.builder()

0 commit comments

Comments
 (0)