Skip to content

Commit 107acee

Browse files
committed
[CCR] Report error if auto follower tries auto follow a leader index with soft deletes disabled (#36886)
Currently if a leader index with soft deletes disabled is auto followed then this index is silently ignored. This commit changes this behavior to mark these indices as auto followed and report an error, which is visible in auto follow stats. Marking the index as auto follow is important, because otherwise the auto follower will continuously try to auto follow and fail. Relates to #33007
1 parent c44bdaa commit 107acee

File tree

3 files changed

+151
-18
lines changed

3 files changed

+151
-18
lines changed

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.cluster.service.ClusterService;
2626
import org.elasticsearch.common.collect.CopyOnWriteHashMap;
2727
import org.elasticsearch.common.collect.Tuple;
28+
import org.elasticsearch.common.settings.Settings;
2829
import org.elasticsearch.common.util.concurrent.AtomicArray;
2930
import org.elasticsearch.common.util.concurrent.CountDown;
3031
import org.elasticsearch.index.Index;
@@ -43,6 +44,7 @@
4344
import java.util.HashSet;
4445
import java.util.LinkedHashMap;
4546
import java.util.List;
47+
import java.util.Locale;
4648
import java.util.Map;
4749
import java.util.Objects;
4850
import java.util.Set;
@@ -343,7 +345,7 @@ private void autoFollowIndices(final AutoFollowMetadata autoFollowMetadata,
343345

344346
Consumer<AutoFollowResult> resultHandler = result -> finalise(slot, result);
345347
checkAutoFollowPattern(autoFollowPatternName, remoteCluster, autoFollowPattern, leaderIndicesToFollow, headers,
346-
patternsForTheSameRemoteCluster, resultHandler);
348+
patternsForTheSameRemoteCluster, remoteClusterState.metaData(), resultHandler);
347349
}
348350
i++;
349351
}
@@ -356,6 +358,7 @@ private void checkAutoFollowPattern(String autoFollowPattenName,
356358
List<Index> leaderIndicesToFollow,
357359
Map<String, String> headers,
358360
List<Tuple<String, AutoFollowPattern>> patternsForTheSameRemoteCluster,
361+
MetaData remoteMetadata,
359362
Consumer<AutoFollowResult> resultHandler) {
360363

361364
final CountDown leaderIndicesCountDown = new CountDown(leaderIndicesToFollow.size());
@@ -375,6 +378,23 @@ private void checkAutoFollowPattern(String autoFollowPattenName,
375378
resultHandler.accept(new AutoFollowResult(autoFollowPattenName, results.asList()));
376379
}
377380
} else {
381+
final Settings leaderIndexSettings = remoteMetadata.getIndexSafe(indexToFollow).getSettings();
382+
if (IndexSettings.INDEX_SOFT_DELETES_SETTING.get(leaderIndexSettings) == false) {
383+
String message = String.format(Locale.ROOT, "index [%s] cannot be followed, because soft deletes are not enabled",
384+
indexToFollow.getName());
385+
LOGGER.warn(message);
386+
updateAutoFollowMetadata(recordLeaderIndexAsFollowFunction(autoFollowPattenName, indexToFollow), error -> {
387+
ElasticsearchException failure = new ElasticsearchException(message);
388+
if (error != null) {
389+
failure.addSuppressed(error);
390+
}
391+
results.set(slot, new Tuple<>(indexToFollow, failure));
392+
if (leaderIndicesCountDown.countDown()) {
393+
resultHandler.accept(new AutoFollowResult(autoFollowPattenName, results.asList()));
394+
}
395+
});
396+
continue;
397+
}
378398
followLeaderIndex(autoFollowPattenName, remoteCluster, indexToFollow, autoFollowPattern, headers, error -> {
379399
results.set(slot, new Tuple<>(indexToFollow, error));
380400
if (leaderIndicesCountDown.countDown()) {
@@ -453,9 +473,7 @@ static List<Index> getLeaderIndicesToFollow(AutoFollowPattern autoFollowPattern,
453473
// has a leader index uuid custom metadata entry that matches with uuid of leaderIndexMetaData variable
454474
// If so then handle it differently: not follow it, but just add an entry to
455475
// AutoFollowMetadata#followedLeaderIndexUUIDs
456-
if (leaderIndexMetaData.getSettings().getAsBoolean(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), false)) {
457-
leaderIndicesToFollow.add(leaderIndexMetaData.getIndex());
458-
}
476+
leaderIndicesToFollow.add(leaderIndexMetaData.getIndex());
459477
}
460478
}
461479
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,43 @@ public void testConflictingPatterns() throws Exception {
296296
assertFalse(followerClient().admin().indices().exists(request).actionGet().isExists());
297297
}
298298

299+
public void testAutoFollowSoftDeletesDisabled() throws Exception {
300+
putAutoFollowPatterns("my-pattern1", new String[] {"logs-*"});
301+
302+
// Soft deletes are disabled:
303+
Settings leaderIndexSettings = Settings.builder()
304+
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), false)
305+
.put(IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1)
306+
.put(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0)
307+
.build();
308+
createLeaderIndex("logs-20200101", leaderIndexSettings);
309+
assertBusy(() -> {
310+
AutoFollowStats autoFollowStats = getAutoFollowStats();
311+
assertThat(autoFollowStats.getNumberOfSuccessfulFollowIndices(), equalTo(0L));
312+
assertThat(autoFollowStats.getNumberOfFailedFollowIndices(), equalTo(1L));
313+
assertThat(autoFollowStats.getRecentAutoFollowErrors().size(), equalTo(1));
314+
ElasticsearchException failure = autoFollowStats.getRecentAutoFollowErrors().firstEntry().getValue();
315+
assertThat(failure.getMessage(), equalTo("index [logs-20200101] cannot be followed, " +
316+
"because soft deletes are not enabled"));
317+
IndicesExistsRequest request = new IndicesExistsRequest("copy-logs-20200101");
318+
assertFalse(followerClient().admin().indices().exists(request).actionGet().isExists());
319+
});
320+
321+
// Soft deletes are enabled:
322+
leaderIndexSettings = Settings.builder()
323+
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true)
324+
.put(IndexMetaData.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1)
325+
.put(IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0)
326+
.build();
327+
createLeaderIndex("logs-20200102", leaderIndexSettings);
328+
assertBusy(() -> {
329+
AutoFollowStats autoFollowStats = getAutoFollowStats();
330+
assertThat(autoFollowStats.getNumberOfSuccessfulFollowIndices(), equalTo(1L));
331+
IndicesExistsRequest request = new IndicesExistsRequest("copy-logs-20200102");
332+
assertTrue(followerClient().admin().indices().exists(request).actionGet().isExists());
333+
});
334+
}
335+
299336
private void putAutoFollowPatterns(String name, String[] patterns) {
300337
PutAutoFollowPatternAction.Request request = new PutAutoFollowPatternAction.Request();
301338
request.setName(name);

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

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void testAutoFollower() {
6262
Client client = mock(Client.class);
6363
when(client.getRemoteClusterClient(anyString())).thenReturn(client);
6464

65-
ClusterState remoteState = createRemoteClusterState("logs-20190101");
65+
ClusterState remoteState = createRemoteClusterState("logs-20190101", true);
6666

6767
AutoFollowPattern autoFollowPattern = new AutoFollowPattern("remote", Collections.singletonList("logs-*"),
6868
null, null, null, null, null, null, null, null, null, null, null);
@@ -183,7 +183,7 @@ void updateAutoFollowMetadata(Function<ClusterState, ClusterState> updateFunctio
183183
public void testAutoFollowerUpdateClusterStateFailure() {
184184
Client client = mock(Client.class);
185185
when(client.getRemoteClusterClient(anyString())).thenReturn(client);
186-
ClusterState remoteState = createRemoteClusterState("logs-20190101");
186+
ClusterState remoteState = createRemoteClusterState("logs-20190101", true);
187187

188188
AutoFollowPattern autoFollowPattern = new AutoFollowPattern("remote", Collections.singletonList("logs-*"),
189189
null, null, null, null, null, null, null, null, null, null, null);
@@ -240,7 +240,7 @@ void updateAutoFollowMetadata(Function<ClusterState, ClusterState> updateFunctio
240240
public void testAutoFollowerCreateAndFollowApiCallFailure() {
241241
Client client = mock(Client.class);
242242
when(client.getRemoteClusterClient(anyString())).thenReturn(client);
243-
ClusterState remoteState = createRemoteClusterState("logs-20190101");
243+
ClusterState remoteState = createRemoteClusterState("logs-20190101", true);
244244

245245
AutoFollowPattern autoFollowPattern = new AutoFollowPattern("remote", Collections.singletonList("logs-*"),
246246
null, null, null, null, null, null, null, null, null, null, null);
@@ -315,8 +315,7 @@ public void testGetLeaderIndicesToFollow() {
315315
String indexName = "metrics-" + i;
316316
Settings.Builder builder = Settings.builder()
317317
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
318-
.put(IndexMetaData.SETTING_INDEX_UUID, indexName)
319-
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), i % 2 == 0);
318+
.put(IndexMetaData.SETTING_INDEX_UUID, indexName);
320319
imdBuilder.put(IndexMetaData.builder("metrics-" + i)
321320
.settings(builder)
322321
.numberOfShards(1)
@@ -347,17 +346,21 @@ public void testGetLeaderIndicesToFollow() {
347346
List<Index> result = AutoFollower.getLeaderIndicesToFollow(autoFollowPattern, remoteState, clusterState,
348347
Collections.emptyList());
349348
result.sort(Comparator.comparing(Index::getName));
350-
assertThat(result.size(), equalTo(3));
349+
assertThat(result.size(), equalTo(5));
351350
assertThat(result.get(0).getName(), equalTo("metrics-0"));
352-
assertThat(result.get(1).getName(), equalTo("metrics-2"));
353-
assertThat(result.get(2).getName(), equalTo("metrics-4"));
351+
assertThat(result.get(1).getName(), equalTo("metrics-1"));
352+
assertThat(result.get(2).getName(), equalTo("metrics-2"));
353+
assertThat(result.get(3).getName(), equalTo("metrics-3"));
354+
assertThat(result.get(4).getName(), equalTo("metrics-4"));
354355

355356
List<String> followedIndexUUIDs = Collections.singletonList(remoteState.metaData().index("metrics-2").getIndexUUID());
356357
result = AutoFollower.getLeaderIndicesToFollow(autoFollowPattern, remoteState, clusterState, followedIndexUUIDs);
357358
result.sort(Comparator.comparing(Index::getName));
358-
assertThat(result.size(), equalTo(2));
359+
assertThat(result.size(), equalTo(4));
359360
assertThat(result.get(0).getName(), equalTo("metrics-0"));
360-
assertThat(result.get(1).getName(), equalTo("metrics-4"));
361+
assertThat(result.get(1).getName(), equalTo("metrics-1"));
362+
assertThat(result.get(2).getName(), equalTo("metrics-3"));
363+
assertThat(result.get(3).getName(), equalTo("metrics-4"));
361364
}
362365

363366
public void testGetLeaderIndicesToFollow_shardsNotStarted() {
@@ -370,7 +373,7 @@ public void testGetLeaderIndicesToFollow_shardsNotStarted() {
370373
.build();
371374

372375
// 1 shard started and another not started:
373-
ClusterState remoteState = createRemoteClusterState("index1");
376+
ClusterState remoteState = createRemoteClusterState("index1", true);
374377
MetaData.Builder mBuilder= MetaData.builder(remoteState.metaData());
375378
mBuilder.put(IndexMetaData.builder("index2")
376379
.settings(settings(Version.CURRENT).put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true))
@@ -692,7 +695,8 @@ public void testWaitForMetadataVersion() {
692695
.metaData(MetaData.builder().putCustom(AutoFollowMetadata.TYPE, autoFollowMetadata))
693696
.build();
694697
String indexName = "logs-" + i;
695-
leaderStates.add(i == 0 ? createRemoteClusterState(indexName) : createRemoteClusterState(leaderStates.get(i - 1), indexName));
698+
leaderStates.add(i == 0 ? createRemoteClusterState(indexName, true) :
699+
createRemoteClusterState(leaderStates.get(i - 1), indexName));
696700
}
697701

698702
List<AutoFollowCoordinator.AutoFollowResult> allResults = new ArrayList<>();
@@ -787,9 +791,83 @@ void updateAutoFollowMetadata(Function<ClusterState, ClusterState> updateFunctio
787791
assertThat(counter.get(), equalTo(states.length));
788792
}
789793

790-
private static ClusterState createRemoteClusterState(String indexName) {
794+
public void testAutoFollowerSoftDeletesDisabled() {
795+
Client client = mock(Client.class);
796+
when(client.getRemoteClusterClient(anyString())).thenReturn(client);
797+
798+
ClusterState remoteState = randomBoolean() ? createRemoteClusterState("logs-20190101", false) :
799+
createRemoteClusterState("logs-20190101", null);
800+
801+
AutoFollowPattern autoFollowPattern = new AutoFollowPattern("remote", Collections.singletonList("logs-*"),
802+
null, null, null, null, null, null, null, null, null, null, null);
803+
Map<String, AutoFollowPattern> patterns = new HashMap<>();
804+
patterns.put("remote", autoFollowPattern);
805+
Map<String, List<String>> followedLeaderIndexUUIDS = new HashMap<>();
806+
followedLeaderIndexUUIDS.put("remote", new ArrayList<>());
807+
Map<String, Map<String, String>> autoFollowHeaders = new HashMap<>();
808+
autoFollowHeaders.put("remote", Collections.singletonMap("key", "val"));
809+
AutoFollowMetadata autoFollowMetadata = new AutoFollowMetadata(patterns, followedLeaderIndexUUIDS, autoFollowHeaders);
810+
811+
ClusterState currentState = ClusterState.builder(new ClusterName("name"))
812+
.metaData(MetaData.builder().putCustom(AutoFollowMetadata.TYPE, autoFollowMetadata))
813+
.build();
814+
815+
List<AutoFollowCoordinator.AutoFollowResult> results = new ArrayList<>();
816+
Consumer<List<AutoFollowCoordinator.AutoFollowResult>> handler = results::addAll;
817+
AutoFollower autoFollower = new AutoFollower("remote", handler, localClusterStateSupplier(currentState), () -> 1L) {
818+
@Override
819+
void getRemoteClusterState(String remoteCluster,
820+
long metadataVersion,
821+
BiConsumer<ClusterStateResponse, Exception> handler) {
822+
assertThat(remoteCluster, equalTo("remote"));
823+
handler.accept(new ClusterStateResponse(new ClusterName("name"), remoteState, 1L, false), null);
824+
}
825+
826+
@Override
827+
void createAndFollow(Map<String, String> headers,
828+
PutFollowAction.Request followRequest,
829+
Runnable successHandler,
830+
Consumer<Exception> failureHandler) {
831+
fail("soft deletes are disabled; index should not be followed");
832+
}
833+
834+
@Override
835+
void updateAutoFollowMetadata(Function<ClusterState, ClusterState> updateFunction,
836+
Consumer<Exception> handler) {
837+
ClusterState resultCs = updateFunction.apply(currentState);
838+
AutoFollowMetadata result = resultCs.metaData().custom(AutoFollowMetadata.TYPE);
839+
assertThat(result.getFollowedLeaderIndexUUIDs().size(), equalTo(1));
840+
assertThat(result.getFollowedLeaderIndexUUIDs().get("remote").size(), equalTo(1));
841+
handler.accept(null);
842+
}
843+
844+
@Override
845+
void cleanFollowedRemoteIndices(ClusterState remoteClusterState, List<String> patterns) {
846+
// Ignore, to avoid invoking updateAutoFollowMetadata(...) twice
847+
}
848+
};
849+
autoFollower.start();
850+
851+
assertThat(results.size(), equalTo(1));
852+
assertThat(results.get(0).clusterStateFetchException, nullValue());
853+
List<Map.Entry<Index, Exception>> entries = new ArrayList<>(results.get(0).autoFollowExecutionResults.entrySet());
854+
assertThat(entries.size(), equalTo(1));
855+
assertThat(entries.get(0).getKey().getName(), equalTo("logs-20190101"));
856+
assertThat(entries.get(0).getValue(), notNullValue());
857+
assertThat(entries.get(0).getValue().getMessage(), equalTo("index [logs-20190101] cannot be followed, " +
858+
"because soft deletes are not enabled"));
859+
}
860+
861+
private static ClusterState createRemoteClusterState(String indexName, Boolean enableSoftDeletes) {
862+
Settings.Builder indexSettings;
863+
if (enableSoftDeletes != null) {
864+
indexSettings = settings(Version.CURRENT).put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), enableSoftDeletes);
865+
} else {
866+
indexSettings = settings(Version.V_6_6_0);
867+
}
868+
791869
IndexMetaData indexMetaData = IndexMetaData.builder(indexName)
792-
.settings(settings(Version.CURRENT).put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true))
870+
.settings(indexSettings)
793871
.numberOfShards(1)
794872
.numberOfReplicas(0)
795873
.build();

0 commit comments

Comments
 (0)