Skip to content

Commit 30e0edb

Browse files
authored
Detect and optimize noop of update index settings (#61348)
This optimization is more relevant in the context of CCR. When a node in the follower cluster leaves, we reallocate the shard-follow tasks on that node to other nodes. The new tasks will overwhelm the follower cluster with many put-mapping, update-settings requests, although most of them are noop. This change detects and optimizes the noop update-settings requests.
1 parent bb48a6d commit 30e0edb

File tree

3 files changed

+78
-8
lines changed

3 files changed

+78
-8
lines changed

server/src/internalClusterTest/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121

2222
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
2323
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
24+
import org.elasticsearch.cluster.ClusterState;
2425
import org.elasticsearch.cluster.metadata.IndexMetadata;
26+
import org.elasticsearch.cluster.service.ClusterService;
2527
import org.elasticsearch.common.Priority;
2628
import org.elasticsearch.common.settings.Setting;
2729
import org.elasticsearch.common.settings.Settings;
30+
import org.elasticsearch.common.unit.TimeValue;
2831
import org.elasticsearch.index.IndexModule;
2932
import org.elasticsearch.index.IndexService;
3033
import org.elasticsearch.index.VersionType;
@@ -687,4 +690,52 @@ private void runTestDefaultNumberOfReplicasTest(final boolean closeIndex) {
687690
assertThat(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(response.getIndexToSettings().get("test")), equalTo(1));
688691
}
689692

693+
public void testNoopUpdate() {
694+
internalCluster().ensureAtLeastNumDataNodes(2);
695+
final ClusterService clusterService = internalCluster().getMasterNodeInstance(ClusterService.class);
696+
assertAcked(client().admin().indices().prepareCreate("test")
697+
.setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)));
698+
699+
ClusterState currentState = clusterService.state();
700+
assertAcked(client().admin().indices().prepareUpdateSettings("test")
701+
.setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)));
702+
assertNotSame(currentState, clusterService.state());
703+
client().admin().cluster().prepareHealth()
704+
.setWaitForGreenStatus()
705+
.setWaitForNoInitializingShards(true)
706+
.setWaitForEvents(Priority.LANGUID)
707+
.setTimeout(TimeValue.MAX_VALUE)
708+
.get();
709+
currentState = clusterService.state();
710+
711+
assertAcked(client().admin().indices().prepareUpdateSettings("test")
712+
.setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)));
713+
assertSame(clusterService.state(), currentState);
714+
715+
assertAcked(client().admin().indices().prepareUpdateSettings("test")
716+
.setSettings(Settings.builder().putNull(IndexMetadata.SETTING_NUMBER_OF_REPLICAS)));
717+
assertSame(clusterService.state(), currentState);
718+
719+
assertAcked(client().admin().indices().prepareUpdateSettings("test")
720+
.setSettings(Settings.builder()
721+
.putNull(SETTING_BLOCKS_READ)
722+
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)));
723+
assertSame(currentState, clusterService.state());
724+
725+
assertAcked(client().admin().indices().prepareUpdateSettings("test")
726+
.setSettings(Settings.builder()
727+
.put(SETTING_BLOCKS_READ, true)
728+
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)));
729+
assertNotSame(currentState, clusterService.state());
730+
currentState = clusterService.state();
731+
732+
assertAcked(client().admin().indices().prepareUpdateSettings("test")
733+
.setSettings(Settings.builder().put(SETTING_BLOCKS_READ, true)));
734+
assertSame(currentState, clusterService.state());
735+
736+
assertAcked(client().admin().indices().prepareUpdateSettings("test")
737+
.setSettings(Settings.builder().putNull(SETTING_BLOCKS_READ)));
738+
assertNotSame(currentState, clusterService.state());
739+
}
740+
690741
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.rest.RestStatus;
3232

3333
import java.io.IOException;
34+
import java.util.Collections;
3435
import java.util.EnumMap;
3536
import java.util.HashMap;
3637
import java.util.HashSet;
@@ -405,6 +406,10 @@ public Builder removeIndexBlocks(String index) {
405406
return this;
406407
}
407408

409+
public boolean hasIndexBlock(String index, ClusterBlock block) {
410+
return indices.getOrDefault(index, Collections.emptySet()).contains(block);
411+
}
412+
408413
public Builder removeIndexBlock(String index, ClusterBlock block) {
409414
if (!indices.containsKey(index)) {
410415
return this;

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataUpdateSettingsService.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,6 @@ public ClusterState execute(ClusterState currentState) {
177177
}
178178
}
179179

180-
ClusterBlocks.Builder blocks = ClusterBlocks.builder().blocks(currentState.blocks());
181-
for (IndexMetadata.APIBlock block : IndexMetadata.APIBlock.values()) {
182-
maybeUpdateClusterBlock(actualIndices, blocks, block.block, block.setting, openSettings);
183-
}
184-
185180
if (!openIndices.isEmpty()) {
186181
for (Index index : openIndices) {
187182
IndexMetadata indexMetadata = metadataBuilder.getSafe(index);
@@ -246,15 +241,26 @@ public ClusterState execute(ClusterState currentState) {
246241
MetadataCreateIndexService.validateTranslogRetentionSettings(metadataBuilder.get(index).getSettings());
247242
}
248243
}
244+
boolean changed = false;
249245
// increment settings versions
250246
for (final String index : actualIndices) {
251247
if (same(currentState.metadata().index(index).getSettings(), metadataBuilder.get(index).getSettings()) == false) {
248+
changed = true;
252249
final IndexMetadata.Builder builder = IndexMetadata.builder(metadataBuilder.get(index));
253250
builder.settingsVersion(1 + builder.settingsVersion());
254251
metadataBuilder.put(builder);
255252
}
256253
}
257254

255+
final ClusterBlocks.Builder blocks = ClusterBlocks.builder().blocks(currentState.blocks());
256+
for (IndexMetadata.APIBlock block : IndexMetadata.APIBlock.values()) {
257+
changed |= maybeUpdateClusterBlock(actualIndices, blocks, block.block, block.setting, openSettings);
258+
}
259+
260+
if (changed == false) {
261+
return currentState;
262+
}
263+
258264
ClusterState updatedState = ClusterState.builder(currentState).metadata(metadataBuilder)
259265
.routingTable(routingTableBuilder.build()).blocks(blocks).build();
260266

@@ -294,18 +300,26 @@ private int getTotalNewShards(Index index, ClusterState currentState, int update
294300
/**
295301
* Updates the cluster block only iff the setting exists in the given settings
296302
*/
297-
private static void maybeUpdateClusterBlock(String[] actualIndices, ClusterBlocks.Builder blocks, ClusterBlock block,
303+
private static boolean maybeUpdateClusterBlock(String[] actualIndices, ClusterBlocks.Builder blocks, ClusterBlock block,
298304
Setting<Boolean> setting, Settings openSettings) {
305+
boolean changed = false;
299306
if (setting.exists(openSettings)) {
300307
final boolean updateBlock = setting.get(openSettings);
301308
for (String index : actualIndices) {
302309
if (updateBlock) {
303-
blocks.addIndexBlock(index, block);
310+
if (blocks.hasIndexBlock(index, block) == false) {
311+
blocks.addIndexBlock(index, block);
312+
changed = true;
313+
}
304314
} else {
305-
blocks.removeIndexBlock(index, block);
315+
if (blocks.hasIndexBlock(index, block)) {
316+
blocks.removeIndexBlock(index, block);
317+
changed = true;
318+
}
306319
}
307320
}
308321
}
322+
return changed;
309323
}
310324

311325

0 commit comments

Comments
 (0)