Skip to content

Commit 7a50aff

Browse files
committed
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 224a5dc commit 7a50aff

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

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

0 commit comments

Comments
 (0)