Snapshot restore and index creates should keep index settings and cluster blocks in sync

Restoring an index from a snapshot or creating a new index can bring the index settings index.blocks.read_only, index.blocks.read, index.blocks.write and index.blocks.metadata out-of-sync with the corresponding cluster blocks.

Closes #13931
This commit is contained in:
Yannick Welsch 2015-10-03 16:30:44 +02:00
parent 83366e7017
commit 069b397794
5 changed files with 112 additions and 5 deletions

View File

@ -300,6 +300,15 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
return this; return this;
} }
public Builder updateBlocks(IndexMetaData indexMetaData) {
removeIndexBlock(indexMetaData.index(), MetaDataIndexStateService.INDEX_CLOSED_BLOCK);
removeIndexBlock(indexMetaData.index(), IndexMetaData.INDEX_READ_ONLY_BLOCK);
removeIndexBlock(indexMetaData.index(), IndexMetaData.INDEX_READ_BLOCK);
removeIndexBlock(indexMetaData.index(), IndexMetaData.INDEX_WRITE_BLOCK);
removeIndexBlock(indexMetaData.index(), IndexMetaData.INDEX_METADATA_BLOCK);
return addBlocks(indexMetaData);
}
public Builder addGlobalBlock(ClusterBlock block) { public Builder addGlobalBlock(ClusterBlock block) {
global.add(block); global.add(block);
return this; return this;

View File

@ -453,9 +453,7 @@ public class MetaDataCreateIndexService extends AbstractComponent {
blocks.addIndexBlock(request.index(), block); blocks.addIndexBlock(request.index(), block);
} }
} }
if (request.state() == State.CLOSE) { blocks.updateBlocks(indexMetaData);
blocks.addIndexBlock(request.index(), MetaDataIndexStateService.INDEX_CLOSED_BLOCK);
}
ClusterState updatedState = ClusterState.builder(currentState).blocks(blocks).metaData(newMetaData).build(); ClusterState updatedState = ClusterState.builder(currentState).blocks(blocks).metaData(newMetaData).build();

View File

@ -101,7 +101,6 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_CREATED; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_CREATED;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_MINIMUM_COMPATIBLE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_MINIMUM_COMPATIBLE;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_UPGRADED; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_VERSION_UPGRADED;
import static org.elasticsearch.cluster.metadata.MetaDataIndexStateService.INDEX_CLOSED_BLOCK;
import static org.elasticsearch.common.util.set.Sets.newHashSet; import static org.elasticsearch.common.util.set.Sets.newHashSet;
/** /**
@ -272,6 +271,7 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis
populateIgnoredShards(index, ignoreShards); populateIgnoredShards(index, ignoreShards);
} }
rtBuilder.addAsNewRestore(updatedIndexMetaData, restoreSource, ignoreShards); rtBuilder.addAsNewRestore(updatedIndexMetaData, restoreSource, ignoreShards);
blocks.addBlocks(updatedIndexMetaData);
mdBuilder.put(updatedIndexMetaData, true); mdBuilder.put(updatedIndexMetaData, true);
} else { } else {
validateExistingIndex(currentIndexMetaData, snapshotIndexMetaData, renamedIndex, partial); validateExistingIndex(currentIndexMetaData, snapshotIndexMetaData, renamedIndex, partial);
@ -295,9 +295,10 @@ public class RestoreService extends AbstractComponent implements ClusterStateLis
indexMdBuilder.settings(Settings.settingsBuilder().put(snapshotIndexMetaData.settings()).put(IndexMetaData.SETTING_INDEX_UUID, currentIndexMetaData.indexUUID())); indexMdBuilder.settings(Settings.settingsBuilder().put(snapshotIndexMetaData.settings()).put(IndexMetaData.SETTING_INDEX_UUID, currentIndexMetaData.indexUUID()));
IndexMetaData updatedIndexMetaData = indexMdBuilder.index(renamedIndex).build(); IndexMetaData updatedIndexMetaData = indexMdBuilder.index(renamedIndex).build();
rtBuilder.addAsRestore(updatedIndexMetaData, restoreSource); rtBuilder.addAsRestore(updatedIndexMetaData, restoreSource);
blocks.removeIndexBlock(renamedIndex, INDEX_CLOSED_BLOCK); blocks.updateBlocks(updatedIndexMetaData);
mdBuilder.put(updatedIndexMetaData, true); mdBuilder.put(updatedIndexMetaData, true);
} }
for (int shard = 0; shard < snapshotIndexMetaData.getNumberOfShards(); shard++) { for (int shard = 0; shard < snapshotIndexMetaData.getNumberOfShards(); shard++) {
if (!ignoreShards.contains(shard)) { if (!ignoreShards.contains(shard)) {
shardsBuilder.put(new ShardId(renamedIndex, shard), new RestoreInProgress.ShardRestoreStatus(clusterService.state().nodes().localNodeId())); shardsBuilder.put(new ShardId(renamedIndex, shard), new RestoreInProgress.ShardRestoreStatus(clusterService.state().nodes().localNodeId()));

View File

@ -33,6 +33,7 @@ import org.junit.Test;
import java.util.HashMap; import java.util.HashMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.notNullValue;
@ -151,6 +152,13 @@ public class CreateIndexIT extends ESIntegTestCase {
} }
} }
@Test
public void testCreateIndexWithMetadataBlocks() {
assertAcked(prepareCreate("test").setSettings(Settings.builder().put(IndexMetaData.SETTING_BLOCKS_METADATA, true)));
assertBlocked(client().admin().indices().prepareGetSettings("test"), IndexMetaData.INDEX_METADATA_BLOCK);
disableIndexBlock("test", IndexMetaData.SETTING_BLOCKS_METADATA);
}
@Test @Test
public void testInvalidShardCountSettingsWithoutPrefix() throws Exception { public void testInvalidShardCountSettingsWithoutPrefix() throws Exception {
try { try {

View File

@ -44,6 +44,7 @@ import org.elasticsearch.cluster.SnapshotsInProgress;
import org.elasticsearch.cluster.SnapshotsInProgress.Entry; import org.elasticsearch.cluster.SnapshotsInProgress.Entry;
import org.elasticsearch.cluster.SnapshotsInProgress.ShardSnapshotStatus; import org.elasticsearch.cluster.SnapshotsInProgress.ShardSnapshotStatus;
import org.elasticsearch.cluster.SnapshotsInProgress.State; import org.elasticsearch.cluster.SnapshotsInProgress.State;
import org.elasticsearch.cluster.block.ClusterBlocks;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.metadata.MetaDataIndexStateService; import org.elasticsearch.cluster.metadata.MetaDataIndexStateService;
@ -1739,6 +1740,96 @@ public class SharedClusterSnapshotRestoreIT extends AbstractSnapshotIntegTestCas
} }
@Test
public void recreateBlocksOnRestoreTest() throws Exception {
Client client = client();
logger.info("--> creating repository");
assertAcked(client.admin().cluster().preparePutRepository("test-repo")
.setType("fs").setSettings(Settings.settingsBuilder()
.put("location", randomRepoPath())
.put("compress", randomBoolean())
.put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES)));
Settings.Builder indexSettings = Settings.builder()
.put(indexSettings())
.put(SETTING_NUMBER_OF_REPLICAS, between(0, 1))
.put(INDEX_REFRESH_INTERVAL, "10s");
logger.info("--> create index");
assertAcked(prepareCreate("test-idx", 2, indexSettings));
try {
List<String> initialBlockSettings = randomSubsetOf(randomInt(3),
IndexMetaData.SETTING_BLOCKS_WRITE, IndexMetaData.SETTING_BLOCKS_METADATA, IndexMetaData.SETTING_READ_ONLY);
Settings.Builder initialSettingsBuilder = Settings.builder();
for (String blockSetting : initialBlockSettings) {
initialSettingsBuilder.put(blockSetting, true);
}
Settings initialSettings = initialSettingsBuilder.build();
logger.info("--> using initial block settings {}", initialSettings.getAsMap());
if (!initialSettings.getAsMap().isEmpty()) {
logger.info("--> apply initial blocks to index");
client().admin().indices().prepareUpdateSettings("test-idx").setSettings(initialSettingsBuilder).get();
}
logger.info("--> snapshot index");
CreateSnapshotResponse createSnapshotResponse = client.admin().cluster().prepareCreateSnapshot("test-repo", "test-snap")
.setWaitForCompletion(true).setIndices("test-idx").get();
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0));
assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()));
logger.info("--> remove blocks and delete index");
disableIndexBlock("test-idx", IndexMetaData.SETTING_BLOCKS_METADATA);
disableIndexBlock("test-idx", IndexMetaData.SETTING_READ_ONLY);
disableIndexBlock("test-idx", IndexMetaData.SETTING_BLOCKS_WRITE);
disableIndexBlock("test-idx", IndexMetaData.SETTING_BLOCKS_READ);
cluster().wipeIndices("test-idx");
logger.info("--> restore index with additional block changes");
List<String> changeBlockSettings = randomSubsetOf(randomInt(4),
IndexMetaData.SETTING_BLOCKS_METADATA, IndexMetaData.SETTING_BLOCKS_WRITE,
IndexMetaData.SETTING_READ_ONLY, IndexMetaData.SETTING_BLOCKS_READ);
Settings.Builder changedSettingsBuilder = Settings.builder();
for (String blockSetting : changeBlockSettings) {
changedSettingsBuilder.put(blockSetting, randomBoolean());
}
Settings changedSettings = changedSettingsBuilder.build();
logger.info("--> applying changed block settings {}", changedSettings.getAsMap());
RestoreSnapshotResponse restoreSnapshotResponse = client.admin().cluster()
.prepareRestoreSnapshot("test-repo", "test-snap")
.setIndexSettings(changedSettings)
.setWaitForCompletion(true).execute().actionGet();
assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0));
ClusterBlocks blocks = client.admin().cluster().prepareState().clear().setBlocks(true).get().getState().blocks();
// compute current index settings (as we cannot query them if they contain SETTING_BLOCKS_METADATA)
Settings mergedSettings = Settings.builder()
.put(initialSettings)
.put(changedSettings)
.build();
logger.info("--> merged block settings {}", mergedSettings.getAsMap());
logger.info("--> checking consistency between settings and blocks");
assertThat(mergedSettings.getAsBoolean(IndexMetaData.SETTING_BLOCKS_METADATA, false),
is(blocks.hasIndexBlock("test-idx", IndexMetaData.INDEX_METADATA_BLOCK)));
assertThat(mergedSettings.getAsBoolean(IndexMetaData.SETTING_BLOCKS_READ, false),
is(blocks.hasIndexBlock("test-idx", IndexMetaData.INDEX_READ_BLOCK)));
assertThat(mergedSettings.getAsBoolean(IndexMetaData.SETTING_BLOCKS_WRITE, false),
is(blocks.hasIndexBlock("test-idx", IndexMetaData.INDEX_WRITE_BLOCK)));
assertThat(mergedSettings.getAsBoolean(IndexMetaData.SETTING_READ_ONLY, false),
is(blocks.hasIndexBlock("test-idx", IndexMetaData.INDEX_READ_ONLY_BLOCK)));
} finally {
logger.info("--> cleaning up blocks");
disableIndexBlock("test-idx", IndexMetaData.SETTING_BLOCKS_METADATA);
disableIndexBlock("test-idx", IndexMetaData.SETTING_READ_ONLY);
disableIndexBlock("test-idx", IndexMetaData.SETTING_BLOCKS_WRITE);
disableIndexBlock("test-idx", IndexMetaData.SETTING_BLOCKS_READ);
}
}
@Test @Test
public void deleteIndexDuringSnapshotTest() throws Exception { public void deleteIndexDuringSnapshotTest() throws Exception {
Client client = client(); Client client = client();