|
20 | 20 | package org.elasticsearch.index.shard; |
21 | 21 |
|
22 | 22 | import org.apache.lucene.index.SegmentInfos; |
| 23 | +import org.elasticsearch.Version; |
23 | 24 | import org.elasticsearch.action.ActionListener; |
24 | 25 | import org.elasticsearch.action.admin.indices.flush.FlushRequest; |
| 26 | +import org.elasticsearch.cluster.node.DiscoveryNode; |
25 | 27 | import org.elasticsearch.cluster.routing.RecoverySource; |
26 | 28 | import org.elasticsearch.cluster.routing.ShardRoutingHelper; |
| 29 | +import org.elasticsearch.cluster.routing.ShardRoutingState; |
27 | 30 | import org.elasticsearch.common.collect.Tuple; |
28 | 31 | import org.elasticsearch.common.settings.Settings; |
29 | 32 | import org.elasticsearch.common.unit.TimeValue; |
30 | 33 | import org.elasticsearch.index.IndexSettings; |
31 | 34 | import org.elasticsearch.index.engine.Engine; |
| 35 | +import org.elasticsearch.index.engine.InternalEngine; |
32 | 36 | import org.elasticsearch.index.engine.InternalEngineFactory; |
33 | 37 | import org.elasticsearch.index.seqno.RetentionLease; |
34 | 38 | import org.elasticsearch.index.seqno.RetentionLeaseStats; |
35 | 39 | import org.elasticsearch.index.seqno.RetentionLeases; |
36 | 40 | import org.elasticsearch.index.seqno.SequenceNumbers; |
| 41 | +import org.elasticsearch.indices.recovery.RecoveryState; |
37 | 42 | import org.elasticsearch.threadpool.ThreadPool; |
38 | 43 |
|
39 | 44 | import java.io.IOException; |
| 45 | +import java.util.ArrayList; |
40 | 46 | import java.util.Collections; |
41 | 47 | import java.util.HashMap; |
| 48 | +import java.util.List; |
42 | 49 | import java.util.Map; |
43 | 50 | import java.util.concurrent.ExecutorService; |
44 | 51 | import java.util.concurrent.ScheduledExecutorService; |
45 | 52 | import java.util.concurrent.TimeUnit; |
| 53 | +import java.util.concurrent.atomic.AtomicBoolean; |
46 | 54 | import java.util.concurrent.atomic.AtomicLong; |
47 | 55 |
|
| 56 | +import static org.elasticsearch.cluster.routing.TestShardRouting.newShardRouting; |
48 | 57 | import static org.hamcrest.Matchers.contains; |
| 58 | +import static org.hamcrest.Matchers.containsInAnyOrder; |
49 | 59 | import static org.hamcrest.Matchers.empty; |
50 | 60 | import static org.hamcrest.Matchers.equalTo; |
51 | 61 | import static org.hamcrest.Matchers.hasItem; |
@@ -294,6 +304,76 @@ public void testRetentionLeaseStats() throws IOException { |
294 | 304 | } |
295 | 305 | } |
296 | 306 |
|
| 307 | + public void testRecoverFromStoreReserveRetentionLeases() throws Exception { |
| 308 | + final AtomicBoolean throwDuringRecoverFromTranslog = new AtomicBoolean(); |
| 309 | + final IndexShard shard = newStartedShard(false, Settings.builder().put("index.soft_deletes.enabled", true).build(), |
| 310 | + config -> new InternalEngine(config) { |
| 311 | + @Override |
| 312 | + public InternalEngine recoverFromTranslog(TranslogRecoveryRunner translogRecoveryRunner, |
| 313 | + long recoverUpToSeqNo) throws IOException { |
| 314 | + if (throwDuringRecoverFromTranslog.get()) { |
| 315 | + throw new RuntimeException("crashed before recover from translog is completed"); |
| 316 | + } |
| 317 | + return super.recoverFromTranslog(translogRecoveryRunner, recoverUpToSeqNo); |
| 318 | + } |
| 319 | + }); |
| 320 | + final List<RetentionLease> leases = new ArrayList<>(); |
| 321 | + long version = randomLongBetween(0, 100); |
| 322 | + long primaryTerm = randomLongBetween(1, 100); |
| 323 | + final int iterations = randomIntBetween(1, 10); |
| 324 | + for (int i = 0; i < iterations; i++) { |
| 325 | + if (randomBoolean()) { |
| 326 | + indexDoc(shard, "_doc", Integer.toString(i)); |
| 327 | + } else { |
| 328 | + leases.add(new RetentionLease(Integer.toString(i), randomNonNegativeLong(), |
| 329 | + randomLongBetween(Integer.MAX_VALUE, Long.MAX_VALUE), "test")); |
| 330 | + } |
| 331 | + if (randomBoolean()) { |
| 332 | + if (randomBoolean()) { |
| 333 | + version += randomLongBetween(1, 100); |
| 334 | + primaryTerm += randomLongBetween(0, 100); |
| 335 | + shard.updateRetentionLeasesOnReplica(new RetentionLeases(primaryTerm, version, leases)); |
| 336 | + shard.flush(new FlushRequest().force(true).waitIfOngoing(true)); |
| 337 | + } |
| 338 | + } |
| 339 | + if (randomBoolean()) { |
| 340 | + shard.updateGlobalCheckpointOnReplica(randomLongBetween(shard.getGlobalCheckpoint(), shard.getLocalCheckpoint()), "test"); |
| 341 | + flushShard(shard); |
| 342 | + } |
| 343 | + } |
| 344 | + version += randomLongBetween(1, 100); |
| 345 | + primaryTerm += randomLongBetween(0, 100); |
| 346 | + shard.updateRetentionLeasesOnReplica(new RetentionLeases(primaryTerm, version, leases)); |
| 347 | + shard.flush(new FlushRequest().force(true).waitIfOngoing(true)); |
| 348 | + closeShard(shard, false); |
| 349 | + |
| 350 | + final IndexShard failedShard = reinitShard(shard, newShardRouting(shard.routingEntry().shardId(), |
| 351 | + shard.routingEntry().currentNodeId(), true, ShardRoutingState.INITIALIZING, |
| 352 | + RecoverySource.ExistingStoreRecoverySource.INSTANCE)); |
| 353 | + final DiscoveryNode localNode = new DiscoveryNode("foo", buildNewFakeTransportAddress(), |
| 354 | + Collections.emptyMap(), Collections.emptySet(), Version.CURRENT); |
| 355 | + failedShard.markAsRecovering("store", new RecoveryState(failedShard.routingEntry(), localNode, null)); |
| 356 | + throwDuringRecoverFromTranslog.set(true); |
| 357 | + expectThrows(IndexShardRecoveryException.class, failedShard::recoverFromStore); |
| 358 | + closeShards(failedShard); |
| 359 | + |
| 360 | + final IndexShard newShard = reinitShard(shard, newShardRouting(shard.routingEntry().shardId(), |
| 361 | + shard.routingEntry().currentNodeId(), true, ShardRoutingState.INITIALIZING, |
| 362 | + RecoverySource.ExistingStoreRecoverySource.INSTANCE)); |
| 363 | + newShard.markAsRecovering("store", new RecoveryState(failedShard.routingEntry(), localNode, null)); |
| 364 | + throwDuringRecoverFromTranslog.set(false); |
| 365 | + assertTrue(newShard.recoverFromStore()); |
| 366 | + final RetentionLeases retentionLeases = newShard.getRetentionLeases(); |
| 367 | + assertThat(retentionLeases.version(), equalTo(version)); |
| 368 | + assertThat(retentionLeases.primaryTerm(), equalTo(primaryTerm)); |
| 369 | + if (leases.isEmpty()) { |
| 370 | + assertThat(retentionLeases.leases(), empty()); |
| 371 | + } else { |
| 372 | + assertThat(retentionLeases.leases(), containsInAnyOrder(leases.toArray(new RetentionLease[0]))); |
| 373 | + } |
| 374 | + closeShards(newShard); |
| 375 | + } |
| 376 | + |
297 | 377 | private void assertRetentionLeases( |
298 | 378 | final IndexShard indexShard, |
299 | 379 | final int size, |
|
0 commit comments