Fail earlier Put Follow requests for closed leader indices (#47637)

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.
This commit is contained in:
Tanguy Leroux 2019-10-07 13:59:04 +02:00 committed by GitHub
parent bc85b22c1f
commit b5ac0204d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 1 deletions

View File

@ -32,6 +32,7 @@ import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.engine.CommitStats; import org.elasticsearch.index.engine.CommitStats;
import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.indices.IndexClosedException;
import org.elasticsearch.license.RemoteClusterLicenseChecker; import org.elasticsearch.license.RemoteClusterLicenseChecker;
import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
@ -130,7 +131,10 @@ public class CcrLicenseChecker {
onFailure.accept(new IndexNotFoundException(leaderIndex)); onFailure.accept(new IndexNotFoundException(leaderIndex));
return; return;
} }
if (leaderIndexMetaData.getState() == IndexMetaData.State.CLOSE) {
onFailure.accept(new IndexClosedException(leaderIndexMetaData.getIndex()));
return;
}
final Client remoteClient = client.getRemoteClusterClient(clusterAlias); final Client remoteClient = client.getRemoteClusterClient(clusterAlias);
hasPrivilegesToFollowIndices(remoteClient, new String[] {leaderIndex}, e -> { hasPrivilegesToFollowIndices(remoteClient, new String[] {leaderIndex}, e -> {
if (e == null) { if (e == null) {

View File

@ -72,6 +72,7 @@ import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.seqno.ReplicationTracker; import org.elasticsearch.index.seqno.ReplicationTracker;
import org.elasticsearch.index.seqno.RetentionLeaseActions; import org.elasticsearch.index.seqno.RetentionLeaseActions;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.indices.IndexClosedException;
import org.elasticsearch.persistent.PersistentTasksCustomMetaData; import org.elasticsearch.persistent.PersistentTasksCustomMetaData;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.RestStatus;
@ -110,6 +111,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
@ -744,6 +746,47 @@ public class IndexFollowingIT extends CcrIntegTestCase {
ensureNoCcrTasks(); ensureNoCcrTasks();
} }
public void testFollowClosedIndex() {
final String leaderIndex = "test-index";
assertAcked(leaderClient().admin().indices().prepareCreate(leaderIndex)
.setSettings(Settings.builder()
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true)
.build()));
assertAcked(leaderClient().admin().indices().prepareClose(leaderIndex));
final String followerIndex = "follow-test-index";
expectThrows(IndexClosedException.class,
() -> followerClient().execute(PutFollowAction.INSTANCE, putFollow(leaderIndex, followerIndex)).actionGet());
assertFalse(followerClient().admin().indices().prepareExists(followerIndex).get().isExists());
}
public void testResumeFollowOnClosedIndex() throws Exception {
final String leaderIndex = "test-index";
assertAcked(leaderClient().admin().indices().prepareCreate(leaderIndex)
.setSettings(Settings.builder()
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
.build()));
ensureLeaderGreen(leaderIndex);
final int nbDocs = randomIntBetween(10, 100);
IntStream.of(nbDocs).forEach(i -> leaderClient().prepareIndex().setIndex(leaderIndex).setSource("field", i).get());
final String followerIndex = "follow-test-index";
PutFollowAction.Response response =
followerClient().execute(PutFollowAction.INSTANCE, putFollow(leaderIndex, followerIndex)).actionGet();
assertTrue(response.isFollowIndexCreated());
assertTrue(response.isFollowIndexShardsAcked());
assertTrue(response.isIndexFollowingStarted());
pauseFollow(followerIndex);
assertAcked(leaderClient().admin().indices().prepareClose(leaderIndex));
expectThrows(IndexClosedException.class, () ->
followerClient().execute(ResumeFollowAction.INSTANCE, resumeFollow(followerIndex)).actionGet());
}
public void testDeleteFollowerIndex() throws Exception { public void testDeleteFollowerIndex() throws Exception {
assertAcked(leaderClient().admin().indices().prepareCreate("index1") assertAcked(leaderClient().admin().indices().prepareCreate("index1")
.setSettings(Settings.builder() .setSettings(Settings.builder()