Skip to content

Commit 5aa5d7b

Browse files
authored
Ignore Lucene index in peer recovery if translog corrupted (#49114)
If the translog on a replica is corrupt, we should not perform an operation-based recovery or utilize sync_id as we won't be able to open an engine in the next step. This change adds an extra validation that ensures translog is okay when preparing a peer recovery request.
1 parent d1ea991 commit 5aa5d7b

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

server/src/main/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetService.java

+13
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
import org.elasticsearch.index.shard.ShardId;
5151
import org.elasticsearch.index.shard.ShardNotFoundException;
5252
import org.elasticsearch.index.store.Store;
53+
import org.elasticsearch.index.translog.Translog;
54+
import org.elasticsearch.index.translog.TranslogCorruptedException;
5355
import org.elasticsearch.indices.recovery.RecoveriesCollection.RecoveryRef;
5456
import org.elasticsearch.tasks.Task;
5557
import org.elasticsearch.threadpool.ThreadPool;
@@ -327,6 +329,17 @@ public static StartRecoveryRequest getStartRecoveryRequest(Logger logger, Discov
327329
Store.MetadataSnapshot metadataSnapshot;
328330
try {
329331
metadataSnapshot = recoveryTarget.indexShard().snapshotStoreMetadata();
332+
// Make sure that the current translog is consistent with the Lucene index; otherwise, we have to throw away the Lucene index.
333+
try {
334+
final String expectedTranslogUUID = metadataSnapshot.getCommitUserData().get(Translog.TRANSLOG_UUID_KEY);
335+
final long globalCheckpoint = Translog.readGlobalCheckpoint(recoveryTarget.translogLocation(), expectedTranslogUUID);
336+
assert globalCheckpoint + 1 >= startingSeqNo : "invalid startingSeqNo " + startingSeqNo + " >= " + globalCheckpoint;
337+
} catch (IOException | TranslogCorruptedException e) {
338+
logger.warn(new ParameterizedMessage("error while reading global checkpoint from translog, " +
339+
"resetting the starting sequence number from {} to unassigned and recovering as if there are none", startingSeqNo), e);
340+
metadataSnapshot = Store.MetadataSnapshot.EMPTY;
341+
startingSeqNo = UNASSIGNED_SEQ_NO;
342+
}
330343
} catch (final org.apache.lucene.index.IndexNotFoundException e) {
331344
// happens on an empty folder. no need to log
332345
assert startingSeqNo == UNASSIGNED_SEQ_NO : startingSeqNo;

server/src/test/java/org/elasticsearch/indices/recovery/PeerRecoveryTargetServiceTests.java

+30
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.elasticsearch.common.bytes.BytesArray;
3636
import org.elasticsearch.common.settings.Settings;
3737
import org.elasticsearch.common.xcontent.XContentType;
38+
import org.elasticsearch.core.internal.io.IOUtils;
3839
import org.elasticsearch.index.engine.Engine;
3940
import org.elasticsearch.index.engine.NoOpEngine;
4041
import org.elasticsearch.index.mapper.SourceToParse;
@@ -60,6 +61,7 @@
6061
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
6162
import static org.hamcrest.Matchers.empty;
6263
import static org.hamcrest.Matchers.equalTo;
64+
import static org.hamcrest.Matchers.sameInstance;
6365

6466
public class PeerRecoveryTargetServiceTests extends IndexShardTestCase {
6567

@@ -286,4 +288,32 @@ public void testResetStartingSeqNoIfLastCommitCorrupted() throws Exception {
286288
recoveryTarget.decRef();
287289
closeShards(shard);
288290
}
291+
292+
public void testResetStartRequestIfTranslogIsCorrupted() throws Exception {
293+
DiscoveryNode pNode = new DiscoveryNode("foo", buildNewFakeTransportAddress(),
294+
Collections.emptyMap(), Collections.emptySet(), Version.CURRENT);
295+
DiscoveryNode rNode = new DiscoveryNode("foo", buildNewFakeTransportAddress(),
296+
Collections.emptyMap(), Collections.emptySet(), Version.CURRENT);
297+
IndexShard shard = newStartedShard(false);
298+
final SeqNoStats seqNoStats = populateRandomData(shard);
299+
shard.close("test", false);
300+
if (randomBoolean()) {
301+
shard.store().associateIndexWithNewTranslog(UUIDs.randomBase64UUID());
302+
} else if (randomBoolean()) {
303+
Translog.createEmptyTranslog(
304+
shard.shardPath().resolveTranslog(), seqNoStats.getGlobalCheckpoint(), shard.shardId(), shard.getOperationPrimaryTerm());
305+
} else {
306+
IOUtils.rm(shard.shardPath().resolveTranslog());
307+
}
308+
shard = reinitShard(shard, ShardRoutingHelper.initWithSameId(shard.routingEntry(), RecoverySource.PeerRecoverySource.INSTANCE));
309+
shard.markAsRecovering("peer recovery", new RecoveryState(shard.routingEntry(), pNode, rNode));
310+
shard.prepareForIndexRecovery();
311+
RecoveryTarget recoveryTarget = new RecoveryTarget(shard, null, null);
312+
StartRecoveryRequest request = PeerRecoveryTargetService.getStartRecoveryRequest(
313+
logger, rNode, recoveryTarget, randomNonNegativeLong());
314+
assertThat(request.startingSeqNo(), equalTo(UNASSIGNED_SEQ_NO));
315+
assertThat(request.metadataSnapshot(), sameInstance(Store.MetadataSnapshot.EMPTY));
316+
recoveryTarget.decRef();
317+
closeShards(shard);
318+
}
289319
}

0 commit comments

Comments
 (0)