20
20
package org .elasticsearch .recovery ;
21
21
22
22
import com .carrotsearch .hppc .IntHashSet ;
23
+ import com .carrotsearch .hppc .cursors .ObjectCursor ;
23
24
import com .carrotsearch .hppc .procedures .IntProcedure ;
24
25
import org .apache .lucene .index .IndexFileNames ;
25
26
import org .apache .lucene .util .English ;
26
27
import org .elasticsearch .action .ActionFuture ;
27
28
import org .elasticsearch .action .admin .cluster .health .ClusterHealthResponse ;
28
29
import org .elasticsearch .action .admin .cluster .reroute .ClusterRerouteResponse ;
30
+ import org .elasticsearch .action .admin .indices .stats .ShardStats ;
29
31
import org .elasticsearch .action .index .IndexRequestBuilder ;
30
32
import org .elasticsearch .action .index .IndexResponse ;
31
33
import org .elasticsearch .action .search .SearchResponse ;
45
47
import org .elasticsearch .common .xcontent .XContentType ;
46
48
import org .elasticsearch .env .NodeEnvironment ;
47
49
import org .elasticsearch .index .IndexService ;
50
+ import org .elasticsearch .index .IndexSettings ;
51
+ import org .elasticsearch .index .seqno .ReplicationTracker ;
52
+ import org .elasticsearch .index .seqno .RetentionLease ;
48
53
import org .elasticsearch .index .shard .IndexEventListener ;
49
54
import org .elasticsearch .index .shard .IndexShard ;
50
55
import org .elasticsearch .index .shard .IndexShardState ;
77
82
import java .util .Arrays ;
78
83
import java .util .Collection ;
79
84
import java .util .List ;
85
+ import java .util .Map ;
86
+ import java .util .Set ;
80
87
import java .util .concurrent .CountDownLatch ;
81
88
import java .util .concurrent .Semaphore ;
82
89
import java .util .concurrent .TimeUnit ;
90
+ import java .util .stream .Collectors ;
83
91
import java .util .stream .Stream ;
84
92
85
93
import static org .elasticsearch .index .query .QueryBuilders .matchAllQuery ;
88
96
import static org .elasticsearch .test .hamcrest .ElasticsearchAssertions .assertNoFailures ;
89
97
import static org .elasticsearch .test .hamcrest .ElasticsearchAssertions .assertSearchHits ;
90
98
import static org .hamcrest .Matchers .equalTo ;
99
+ import static org .hamcrest .Matchers .everyItem ;
100
+ import static org .hamcrest .Matchers .in ;
91
101
import static org .hamcrest .Matchers .not ;
92
102
import static org .hamcrest .Matchers .startsWith ;
93
103
@@ -103,6 +113,7 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
103
113
@ Override
104
114
protected void beforeIndexDeletion () throws Exception {
105
115
super .beforeIndexDeletion ();
116
+ assertActiveCopiesEstablishedPeerRecoveryRetentionLeases ();
106
117
internalCluster ().assertSeqNos ();
107
118
internalCluster ().assertSameDocIdsOnShards ();
108
119
}
@@ -603,6 +614,49 @@ public void testRelocateWhileContinuouslyIndexingAndWaitingForRefresh() throws E
603
614
assertThat (client ().prepareSearch ("test" ).setSize (0 ).execute ().actionGet ().getHits ().getTotalHits ().value , equalTo (120L ));
604
615
}
605
616
617
+ public void testRelocationEstablishedPeerRecoveryRetentionLeases () throws Exception {
618
+ int halfNodes = randomIntBetween (1 , 3 );
619
+ String indexName = "test" ;
620
+ Settings [] nodeSettings = Stream .concat (
621
+ Stream .generate (() -> Settings .builder ().put ("node.attr.color" , "blue" ).build ()).limit (halfNodes ),
622
+ Stream .generate (() -> Settings .builder ().put ("node.attr.color" , "red" ).build ()).limit (halfNodes )).toArray (Settings []::new );
623
+ List <String > nodes = internalCluster ().startNodes (nodeSettings );
624
+ String [] blueNodes = nodes .subList (0 , halfNodes ).toArray (String []::new );
625
+ String [] redNodes = nodes .subList (0 , halfNodes ).toArray (String []::new );
626
+ ensureStableCluster (halfNodes * 2 );
627
+ assertAcked (
628
+ client ().admin ().indices ().prepareCreate (indexName ).setSettings (Settings .builder ()
629
+ .put (IndexSettings .INDEX_SOFT_DELETES_SETTING .getKey (), randomBoolean ())
630
+ .put (IndexMetaData .SETTING_NUMBER_OF_REPLICAS , randomIntBetween (0 , halfNodes - 1 ))
631
+ .put ("index.routing.allocation.include.color" , "blue" )));
632
+ ensureGreen ("test" );
633
+ assertBusy (() -> assertAllShardsOnNodes (indexName , blueNodes ));
634
+ assertActiveCopiesEstablishedPeerRecoveryRetentionLeases ();
635
+ client ().admin ().indices ().prepareUpdateSettings (indexName )
636
+ .setSettings (Settings .builder ().put ("index.routing.allocation.include.color" , "red" )).get ();
637
+ assertBusy (() -> assertAllShardsOnNodes (indexName , redNodes ));
638
+ ensureGreen ("test" );
639
+ assertActiveCopiesEstablishedPeerRecoveryRetentionLeases ();
640
+ }
641
+
642
+ private void assertActiveCopiesEstablishedPeerRecoveryRetentionLeases () throws Exception {
643
+ assertBusy (() -> {
644
+ for (ObjectCursor <String > it : client ().admin ().cluster ().prepareState ().get ().getState ().metaData ().indices ().keys ()) {
645
+ Map <ShardId , List <ShardStats >> byShardId = Stream .of (client ().admin ().indices ().prepareStats (it .value ).get ().getShards ())
646
+ .collect (Collectors .groupingBy (l -> l .getShardRouting ().shardId ()));
647
+ for (List <ShardStats > shardStats : byShardId .values ()) {
648
+ Set <String > expectedLeaseIds = shardStats .stream ()
649
+ .map (s -> ReplicationTracker .getPeerRecoveryRetentionLeaseId (s .getShardRouting ())).collect (Collectors .toSet ());
650
+ for (ShardStats shardStat : shardStats ) {
651
+ Set <String > actualLeaseIds = shardStat .getRetentionLeaseStats ().retentionLeases ().leases ().stream ()
652
+ .map (RetentionLease ::id ).collect (Collectors .toSet ());
653
+ assertThat (expectedLeaseIds , everyItem (in (actualLeaseIds )));
654
+ }
655
+ }
656
+ }
657
+ });
658
+ }
659
+
606
660
class RecoveryCorruption implements StubbableTransport .SendRequestBehavior {
607
661
608
662
private final CountDownLatch corruptionCount ;
0 commit comments