|
@@ -23,6 +23,7 @@ import org.apache.lucene.index.CorruptIndexException;
|
|
|
import org.apache.lucene.index.DirectoryReader;
|
|
|
import org.apache.lucene.index.IndexCommit;
|
|
|
import org.apache.lucene.index.IndexableField;
|
|
|
+import org.apache.lucene.index.IndexWriter;
|
|
|
import org.apache.lucene.index.Term;
|
|
|
import org.apache.lucene.search.IndexSearcher;
|
|
|
import org.apache.lucene.search.TermQuery;
|
|
@@ -118,6 +119,7 @@ import org.elasticsearch.snapshots.Snapshot;
|
|
|
import org.elasticsearch.snapshots.SnapshotId;
|
|
|
import org.elasticsearch.snapshots.SnapshotInfo;
|
|
|
import org.elasticsearch.snapshots.SnapshotShardFailure;
|
|
|
+import org.elasticsearch.test.CorruptionUtils;
|
|
|
import org.elasticsearch.test.DummyShardLock;
|
|
|
import org.elasticsearch.test.FieldMaskingReader;
|
|
|
import org.elasticsearch.test.VersionUtils;
|
|
@@ -126,7 +128,11 @@ import org.elasticsearch.ElasticsearchException;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.nio.charset.Charset;
|
|
|
+import java.nio.file.FileVisitResult;
|
|
|
+import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
+import java.nio.file.SimpleFileVisitor;
|
|
|
+import java.nio.file.attribute.BasicFileAttributes;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
@@ -1239,7 +1245,7 @@ public class IndexShardTests extends IndexShardTestCase {
|
|
|
};
|
|
|
|
|
|
try (Store store = createStore(shardId, new IndexSettings(metaData, Settings.EMPTY), directory)) {
|
|
|
- IndexShard shard = newShard(shardRouting, shardPath, metaData, store,
|
|
|
+ IndexShard shard = newShard(shardRouting, shardPath, metaData, i -> store,
|
|
|
null, new InternalEngineFactory(), () -> {
|
|
|
}, EMPTY_EVENT_LISTENER);
|
|
|
AtomicBoolean failureCallbackTriggered = new AtomicBoolean(false);
|
|
@@ -2590,6 +2596,143 @@ public class IndexShardTests extends IndexShardTestCase {
|
|
|
closeShards(newShard);
|
|
|
}
|
|
|
|
|
|
+ public void testIndexCheckOnStartup() throws Exception {
|
|
|
+ final IndexShard indexShard = newStartedShard(true);
|
|
|
+
|
|
|
+ final long numDocs = between(10, 100);
|
|
|
+ for (long i = 0; i < numDocs; i++) {
|
|
|
+ indexDoc(indexShard, "_doc", Long.toString(i), "{}");
|
|
|
+ }
|
|
|
+ indexShard.flush(new FlushRequest());
|
|
|
+ closeShards(indexShard);
|
|
|
+
|
|
|
+ final ShardPath shardPath = indexShard.shardPath();
|
|
|
+
|
|
|
+ final Path indexPath = corruptIndexFile(shardPath);
|
|
|
+
|
|
|
+ final AtomicInteger corruptedMarkerCount = new AtomicInteger();
|
|
|
+ final SimpleFileVisitor<Path> corruptedVisitor = new SimpleFileVisitor<Path>() {
|
|
|
+ @Override
|
|
|
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
|
|
+ if (Files.isRegularFile(file) && file.getFileName().toString().startsWith(Store.CORRUPTED)) {
|
|
|
+ corruptedMarkerCount.incrementAndGet();
|
|
|
+ }
|
|
|
+ return FileVisitResult.CONTINUE;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ Files.walkFileTree(indexPath, corruptedVisitor);
|
|
|
+
|
|
|
+ assertThat("corruption marker should not be there", corruptedMarkerCount.get(), equalTo(0));
|
|
|
+
|
|
|
+ final ShardRouting shardRouting = ShardRoutingHelper.initWithSameId(indexShard.routingEntry(),
|
|
|
+ RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE
|
|
|
+ );
|
|
|
+ // start shard and perform index check on startup. It enforce shard to fail due to corrupted index files
|
|
|
+ final IndexMetaData indexMetaData = IndexMetaData.builder(indexShard.indexSettings().getIndexMetaData())
|
|
|
+ .settings(Settings.builder()
|
|
|
+ .put(indexShard.indexSettings.getSettings())
|
|
|
+ .put(IndexSettings.INDEX_CHECK_ON_STARTUP.getKey(), randomFrom("true", "checksum")))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ IndexShard corruptedShard = newShard(shardRouting, shardPath, indexMetaData,
|
|
|
+ null, null, indexShard.engineFactory,
|
|
|
+ indexShard.getGlobalCheckpointSyncer(), EMPTY_EVENT_LISTENER);
|
|
|
+
|
|
|
+ final IndexShardRecoveryException indexShardRecoveryException =
|
|
|
+ expectThrows(IndexShardRecoveryException.class, () -> newStartedShard(p -> corruptedShard, true));
|
|
|
+ assertThat(indexShardRecoveryException.getMessage(), equalTo("failed recovery"));
|
|
|
+
|
|
|
+ // check that corrupt marker is there
|
|
|
+ Files.walkFileTree(indexPath, corruptedVisitor);
|
|
|
+ assertThat("store has to be marked as corrupted", corruptedMarkerCount.get(), equalTo(1));
|
|
|
+
|
|
|
+ try {
|
|
|
+ closeShards(corruptedShard);
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ assertThat(e.getMessage(), equalTo("CheckIndex failed"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void testShardDoesNotStartIfCorruptedMarkerIsPresent() throws Exception {
|
|
|
+ final IndexShard indexShard = newStartedShard(true);
|
|
|
+
|
|
|
+ final long numDocs = between(10, 100);
|
|
|
+ for (long i = 0; i < numDocs; i++) {
|
|
|
+ indexDoc(indexShard, "_doc", Long.toString(i), "{}");
|
|
|
+ }
|
|
|
+ indexShard.flush(new FlushRequest());
|
|
|
+ closeShards(indexShard);
|
|
|
+
|
|
|
+ final ShardPath shardPath = indexShard.shardPath();
|
|
|
+
|
|
|
+ final ShardRouting shardRouting = ShardRoutingHelper.initWithSameId(indexShard.routingEntry(),
|
|
|
+ RecoverySource.StoreRecoverySource.EXISTING_STORE_INSTANCE
|
|
|
+ );
|
|
|
+ final IndexMetaData indexMetaData = indexShard.indexSettings().getIndexMetaData();
|
|
|
+
|
|
|
+ final Path indexPath = shardPath.getDataPath().resolve(ShardPath.INDEX_FOLDER_NAME);
|
|
|
+
|
|
|
+ // create corrupted marker
|
|
|
+ final String corruptionMessage = "fake ioexception";
|
|
|
+ try(Store store = createStore(indexShard.indexSettings(), shardPath)) {
|
|
|
+ store.markStoreCorrupted(new IOException(corruptionMessage));
|
|
|
+ }
|
|
|
+
|
|
|
+ // try to start shard on corrupted files
|
|
|
+ final IndexShard corruptedShard = newShard(shardRouting, shardPath, indexMetaData,
|
|
|
+ null, null, indexShard.engineFactory,
|
|
|
+ indexShard.getGlobalCheckpointSyncer(), EMPTY_EVENT_LISTENER);
|
|
|
+
|
|
|
+ final IndexShardRecoveryException exception1 = expectThrows(IndexShardRecoveryException.class,
|
|
|
+ () -> newStartedShard(p -> corruptedShard, true));
|
|
|
+ assertThat(exception1.getCause().getMessage(), equalTo(corruptionMessage + " (resource=preexisting_corruption)"));
|
|
|
+ closeShards(corruptedShard);
|
|
|
+
|
|
|
+ final AtomicInteger corruptedMarkerCount = new AtomicInteger();
|
|
|
+ final SimpleFileVisitor<Path> corruptedVisitor = new SimpleFileVisitor<Path>() {
|
|
|
+ @Override
|
|
|
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
|
|
+ if (Files.isRegularFile(file) && file.getFileName().toString().startsWith(Store.CORRUPTED)) {
|
|
|
+ corruptedMarkerCount.incrementAndGet();
|
|
|
+ }
|
|
|
+ return FileVisitResult.CONTINUE;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ Files.walkFileTree(indexPath, corruptedVisitor);
|
|
|
+ assertThat("store has to be marked as corrupted", corruptedMarkerCount.get(), equalTo(1));
|
|
|
+
|
|
|
+ // try to start another time shard on corrupted files
|
|
|
+ final IndexShard corruptedShard2 = newShard(shardRouting, shardPath, indexMetaData,
|
|
|
+ null, null, indexShard.engineFactory,
|
|
|
+ indexShard.getGlobalCheckpointSyncer(), EMPTY_EVENT_LISTENER);
|
|
|
+
|
|
|
+ final IndexShardRecoveryException exception2 = expectThrows(IndexShardRecoveryException.class,
|
|
|
+ () -> newStartedShard(p -> corruptedShard2, true));
|
|
|
+ assertThat(exception2.getCause().getMessage(), equalTo(corruptionMessage + " (resource=preexisting_corruption)"));
|
|
|
+ closeShards(corruptedShard2);
|
|
|
+
|
|
|
+ // check that corrupt marker is there
|
|
|
+ corruptedMarkerCount.set(0);
|
|
|
+ Files.walkFileTree(indexPath, corruptedVisitor);
|
|
|
+ assertThat("store still has a single corrupt marker", corruptedMarkerCount.get(), equalTo(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Path corruptIndexFile(ShardPath shardPath) throws IOException {
|
|
|
+ final Path indexPath = shardPath.getDataPath().resolve(ShardPath.INDEX_FOLDER_NAME);
|
|
|
+ final Path[] filesToCorrupt =
|
|
|
+ Files.walk(indexPath)
|
|
|
+ .filter(p -> {
|
|
|
+ final String name = p.getFileName().toString();
|
|
|
+ return Files.isRegularFile(p)
|
|
|
+ && name.startsWith("extra") == false // Skip files added by Lucene's ExtrasFS
|
|
|
+ && IndexWriter.WRITE_LOCK_NAME.equals(name) == false
|
|
|
+ && name.startsWith("segments_") == false && name.endsWith(".si") == false;
|
|
|
+ })
|
|
|
+ .toArray(Path[]::new);
|
|
|
+ CorruptionUtils.corruptFile(random(), filesToCorrupt);
|
|
|
+ return indexPath;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Simulates a scenario that happens when we are async fetching snapshot metadata from GatewayService
|
|
|
* and checking index concurrently. This should always be possible without any exception.
|
|
@@ -2613,7 +2756,7 @@ public class IndexShardTests extends IndexShardTestCase {
|
|
|
final IndexMetaData indexMetaData = IndexMetaData.builder(indexShard.indexSettings().getIndexMetaData())
|
|
|
.settings(Settings.builder()
|
|
|
.put(indexShard.indexSettings.getSettings())
|
|
|
- .put(IndexSettings.INDEX_CHECK_ON_STARTUP.getKey(), randomFrom("false", "true", "checksum", "fix")))
|
|
|
+ .put(IndexSettings.INDEX_CHECK_ON_STARTUP.getKey(), randomFrom("false", "true", "checksum")))
|
|
|
.build();
|
|
|
final IndexShard newShard = newShard(shardRouting, indexShard.shardPath(), indexMetaData,
|
|
|
null, null, indexShard.engineFactory, indexShard.getGlobalCheckpointSyncer(), EMPTY_EVENT_LISTENER);
|
|
@@ -2655,6 +2798,16 @@ public class IndexShardTests extends IndexShardTestCase {
|
|
|
closeShards(newShard);
|
|
|
}
|
|
|
|
|
|
+ public void testCheckOnStartupDeprecatedValue() throws Exception {
|
|
|
+ final Settings settings = Settings.builder().put(IndexSettings.INDEX_CHECK_ON_STARTUP.getKey(), "fix").build();
|
|
|
+
|
|
|
+ final IndexShard newShard = newShard(true, settings);
|
|
|
+ closeShards(newShard);
|
|
|
+
|
|
|
+ assertWarnings("Setting [index.shard.check_on_startup] is set to deprecated value [fix], "
|
|
|
+ + "which has no effect and will not be accepted in future");
|
|
|
+ }
|
|
|
+
|
|
|
class Result {
|
|
|
private final int localCheckpoint;
|
|
|
private final int maxSeqNo;
|