99
1010package org .elasticsearch .cluster .routing .allocation .allocator ;
1111
12+ import org .elasticsearch .action .support .replication .ClusterStateCreationUtils ;
13+ import org .elasticsearch .cluster .ClusterState ;
1214import org .elasticsearch .cluster .metadata .ProjectId ;
1315import org .elasticsearch .cluster .node .DiscoveryNodeUtils ;
1416import org .elasticsearch .cluster .node .DiscoveryNodes ;
2224import org .elasticsearch .common .settings .ClusterSettings ;
2325import org .elasticsearch .common .settings .Settings ;
2426import org .elasticsearch .common .time .AdvancingTimeProvider ;
27+ import org .elasticsearch .core .Nullable ;
2528import org .elasticsearch .index .Index ;
2629import org .elasticsearch .index .shard .ShardId ;
2730import org .elasticsearch .test .ESTestCase ;
3235
3336public class UndesiredAllocationsTrackerTests extends ESTestCase {
3437
38+ public void testShardsArePrunedWhenRemovedFromRoutingTable () {
39+ final int primaryShards = randomIntBetween (2 , 5 );
40+ final int numberOfIndices = randomIntBetween (2 , 5 );
41+ final int numberOfNodes = randomIntBetween (2 , 5 );
42+
43+ final var clusterSettings = ClusterSettings .createBuiltInClusterSettings (
44+ Settings .builder ()
45+ .put (UndesiredAllocationsTracker .MAX_UNDESIRED_ALLOCATIONS_TO_TRACK .getKey (), numberOfIndices * primaryShards )
46+ .build ()
47+ );
48+ final var advancingTimeProvider = new AdvancingTimeProvider ();
49+ final var undesiredAllocationsTracker = new UndesiredAllocationsTracker (clusterSettings , advancingTimeProvider );
50+
51+ final var indexNames = IntStream .range (0 , numberOfIndices ).mapToObj (i -> "index-" + i ).toArray (String []::new );
52+ final var state = ClusterStateCreationUtils .state (numberOfNodes , indexNames , primaryShards );
53+ final var routingNodes = RoutingNodes .immutable (state .globalRoutingTable (), state .nodes ());
54+
55+ // Mark all primary shards as undesired
56+ routingNodes .forEach (routingNode -> {
57+ routingNode .forEach (shardRouting -> {
58+ if (shardRouting .primary ()) {
59+ undesiredAllocationsTracker .trackUndesiredAllocation (shardRouting );
60+ }
61+ });
62+ });
63+ assertEquals (numberOfIndices * primaryShards , undesiredAllocationsTracker .getUndesiredAllocations ().size ());
64+
65+ // Simulate an index being deleted
66+ ClusterState stateWithIndexRemoved = removeRandomIndex (state );
67+
68+ // Run cleanup with new RoutingNodes
69+ undesiredAllocationsTracker .cleanup (
70+ RoutingNodes .immutable (stateWithIndexRemoved .globalRoutingTable (), stateWithIndexRemoved .nodes ())
71+ );
72+ assertEquals ((numberOfIndices - 1 ) * primaryShards , undesiredAllocationsTracker .getUndesiredAllocations ().size ());
73+ undesiredAllocationsTracker .getUndesiredAllocations ()
74+ .iterator ()
75+ .forEachRemaining (olc -> assertNotNull (stateWithIndexRemoved .routingTable (ProjectId .DEFAULT ).index (olc .key .index ())));
76+ }
77+
3578 public void testNewestRecordsArePurgedWhenLimitIsDecreased () {
3679 final var initialMaximum = randomIntBetween (10 , 20 );
3780 final var clusterSettings = ClusterSettings .createBuiltInClusterSettings (
@@ -42,17 +85,17 @@ public void testNewestRecordsArePurgedWhenLimitIsDecreased() {
4285 final var indexName = randomIdentifier ();
4386 final var index = new Index (indexName , indexName );
4487 final var indexRoutingTableBuilder = IndexRoutingTable .builder (index );
45- final var discoveryNodesBuilder = DiscoveryNodes . builder ( );
88+ final var discoveryNodes = randomDiscoveryNodes ( randomIntBetween ( initialMaximum / 2 , initialMaximum ) );
4689
4790 // The shards with the lowest IDs will have the earliest timestamps
4891 for (int i = 0 ; i < initialMaximum ; i ++) {
49- final var routing = createAssignedRouting (index , i , discoveryNodesBuilder );
92+ final var routing = createAssignedRouting (index , i , discoveryNodes );
5093 indexRoutingTableBuilder .addShard (routing );
5194 undesiredAllocationsTracker .trackUndesiredAllocation (routing );
5295 }
5396 final var routingNodes = RoutingNodes .immutable (
5497 GlobalRoutingTable .builder ().put (ProjectId .DEFAULT , RoutingTable .builder ().add (indexRoutingTableBuilder ).build ()).build (),
55- discoveryNodesBuilder . build ()
98+ discoveryNodes
5699 );
57100
58101 // Reduce the maximum
@@ -73,9 +116,45 @@ public void testNewestRecordsArePurgedWhenLimitIsDecreased() {
73116 assertEquals (IntStream .range (0 , reducedMaximum ).boxed ().collect (Collectors .toSet ()), remainingShardIds );
74117 }
75118
76- private ShardRouting createAssignedRouting (Index index , int shardId , DiscoveryNodes .Builder discoveryNodesBuilder ) {
77- final var nodeId = randomAlphaOfLength (10 );
78- discoveryNodesBuilder .add (DiscoveryNodeUtils .create (nodeId ));
119+ public void testCannotTrackMoreShardsThanTheLimit () {
120+ final int maxToTrack = randomIntBetween (1 , 10 );
121+ final var clusterSettings = ClusterSettings .createBuiltInClusterSettings (
122+ Settings .builder ().put (UndesiredAllocationsTracker .MAX_UNDESIRED_ALLOCATIONS_TO_TRACK .getKey (), maxToTrack ).build ()
123+ );
124+ final var advancingTimeProvider = new AdvancingTimeProvider ();
125+ final var undesiredAllocationsTracker = new UndesiredAllocationsTracker (clusterSettings , advancingTimeProvider );
126+ final var index = new Index (randomIdentifier (), randomIdentifier ());
127+
128+ final int shardsToAdd = randomIntBetween (maxToTrack + 1 , maxToTrack * 2 );
129+ for (int i = 0 ; i < shardsToAdd ; i ++) {
130+ final var routing = createAssignedRouting (index , i );
131+ undesiredAllocationsTracker .trackUndesiredAllocation (routing );
132+ }
133+
134+ // Only the first {maxToTrack} shards should be tracked
135+ assertEquals (maxToTrack , undesiredAllocationsTracker .getUndesiredAllocations ().size ());
136+ final var trackedShardIds = StreamSupport .stream (undesiredAllocationsTracker .getUndesiredAllocations ().spliterator (), false )
137+ .map (olc -> olc .key .shardId ().id ())
138+ .collect (Collectors .toSet ());
139+ assertEquals (IntStream .range (0 , maxToTrack ).boxed ().collect (Collectors .toSet ()), trackedShardIds );
140+ }
141+
142+ private ClusterState removeRandomIndex (ClusterState state ) {
143+ RoutingTable originalRoutingTable = state .routingTable (ProjectId .DEFAULT );
144+ RoutingTable updatedRoutingTable = RoutingTable .builder (originalRoutingTable )
145+ .remove (randomFrom (originalRoutingTable .indicesRouting ().keySet ()))
146+ .build ();
147+ return ClusterState .builder (state )
148+ .routingTable (GlobalRoutingTable .builder ().put (ProjectId .DEFAULT , updatedRoutingTable ).build ())
149+ .build ();
150+ }
151+
152+ private ShardRouting createAssignedRouting (Index index , int shardId ) {
153+ return createAssignedRouting (index , shardId , null );
154+ }
155+
156+ private ShardRouting createAssignedRouting (Index index , int shardId , @ Nullable DiscoveryNodes discoveryNodes ) {
157+ final var nodeId = discoveryNodes == null ? randomAlphaOfLength (10 ) : randomFrom (discoveryNodes .getNodes ().keySet ());
79158 return ShardRouting .newUnassigned (
80159 new ShardId (index , shardId ),
81160 true ,
@@ -84,4 +163,12 @@ private ShardRouting createAssignedRouting(Index index, int shardId, DiscoveryNo
84163 randomFrom (ShardRouting .Role .DEFAULT , ShardRouting .Role .INDEX_ONLY )
85164 ).initialize (nodeId , null , randomNonNegativeLong ());
86165 }
166+
167+ private DiscoveryNodes randomDiscoveryNodes (int numberOfNodes ) {
168+ final var nodes = DiscoveryNodes .builder ();
169+ for (int i = 0 ; i < numberOfNodes ; i ++) {
170+ nodes .add (DiscoveryNodeUtils .create ("node-" + i ));
171+ }
172+ return nodes .build ();
173+ }
87174}
0 commit comments