Skip to content

Commit 39a6dec

Browse files
authored
Prioritise recovery of system index shards (#62640)
Closes #61660. When ordering shard for recovery, ensure system index shards are ordered first so that their recovery will be started first. Note that I rewrote PriorityComparatorTests to use IndexMetadata instead of its local IndexMeta POJO.
1 parent 3a9b657 commit 39a6dec

File tree

2 files changed

+146
-70
lines changed

2 files changed

+146
-70
lines changed

server/src/main/java/org/elasticsearch/gateway/PriorityComparator.java

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@
2828
import java.util.Comparator;
2929

3030
/**
31-
* A comparator that compares ShardRouting based on it's indexes priority (index.priority),
32-
* it's creation date (index.creation_date), or eventually by it's index name in reverse order.
33-
* We try to recover first shards from an index with the highest priority, if that's the same
34-
* we try to compare the timestamp the index is created and pick the newer first (time-based indices,
35-
* here the newer indices matter more). If even that is the same, we compare the index name which is useful
36-
* if the date is baked into the index name. ie logstash-2015.05.03.
31+
* A comparator that compares {@link ShardRouting} instances based on various properties. Instances
32+
* are ordered as follows.
33+
* <ol>
34+
* <li>First, system indices are ordered before non-system indices</li>
35+
* <li>Then indices are ordered by their priority, in descending order (index.priority)</li>
36+
* <li>Then newer indices are ordered before older indices, based on their creation date. This benefits
37+
* time-series indices, where newer indices are considered more urgent (index.creation_date)</li>
38+
* <li>Lastly the index names are compared, which is useful when a date is baked into the index
39+
* name, e.g. <code>logstash-2015.05.03</code></li>
40+
* </ol>
3741
*/
3842
public abstract class PriorityComparator implements Comparator<ShardRouting> {
3943

@@ -43,13 +47,20 @@ public final int compare(ShardRouting o1, ShardRouting o2) {
4347
final String o2Index = o2.getIndexName();
4448
int cmp = 0;
4549
if (o1Index.equals(o2Index) == false) {
46-
final Settings settingsO1 = getIndexSettings(o1.index());
47-
final Settings settingsO2 = getIndexSettings(o2.index());
48-
cmp = Long.compare(priority(settingsO2), priority(settingsO1));
50+
final IndexMetadata metadata01 = getMetadata(o1.index());
51+
final IndexMetadata metadata02 = getMetadata(o2.index());
52+
cmp = Boolean.compare(metadata02.isSystem(), metadata01.isSystem());
53+
4954
if (cmp == 0) {
50-
cmp = Long.compare(timeCreated(settingsO2), timeCreated(settingsO1));
55+
final Settings settingsO1 = metadata01.getSettings();
56+
final Settings settingsO2 = metadata02.getSettings();
57+
cmp = Long.compare(priority(settingsO2), priority(settingsO1));
58+
5159
if (cmp == 0) {
52-
cmp = o2Index.compareTo(o1Index);
60+
cmp = Long.compare(timeCreated(settingsO2), timeCreated(settingsO1));
61+
if (cmp == 0) {
62+
cmp = o2Index.compareTo(o1Index);
63+
}
5364
}
5465
}
5566
}
@@ -64,17 +75,16 @@ private static long timeCreated(Settings settings) {
6475
return settings.getAsLong(IndexMetadata.SETTING_CREATION_DATE, -1L);
6576
}
6677

67-
protected abstract Settings getIndexSettings(Index index);
78+
protected abstract IndexMetadata getMetadata(Index index);
6879

6980
/**
7081
* Returns a PriorityComparator that uses the RoutingAllocation index metadata to access the index setting per index.
7182
*/
7283
public static PriorityComparator getAllocationComparator(final RoutingAllocation allocation) {
7384
return new PriorityComparator() {
7485
@Override
75-
protected Settings getIndexSettings(Index index) {
76-
IndexMetadata indexMetadata = allocation.metadata().getIndexSafe(index);
77-
return indexMetadata.getSettings();
86+
protected IndexMetadata getMetadata(Index index) {
87+
return allocation.metadata().getIndexSafe(index);
7888
}
7989
};
8090
}

server/src/test/java/org/elasticsearch/gateway/PriorityComparatorTests.java

Lines changed: 121 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.elasticsearch.gateway;
2020

21+
import org.elasticsearch.Version;
2122
import org.elasticsearch.cluster.metadata.IndexMetadata;
2223
import org.elasticsearch.cluster.routing.RoutingNodes;
2324
import org.elasticsearch.cluster.routing.ShardRouting;
@@ -35,6 +36,7 @@
3536
import java.util.Locale;
3637
import java.util.Map;
3738

39+
import static org.hamcrest.Matchers.greaterThan;
3840
import static org.mockito.Mockito.mock;
3941

4042
public class PriorityComparatorTests extends ESTestCase {
@@ -52,15 +54,17 @@ public void testPreferNewIndices() {
5254
}
5355
shards.sort(new PriorityComparator() {
5456
@Override
55-
protected Settings getIndexSettings(Index index) {
57+
protected IndexMetadata getMetadata(Index index) {
58+
Settings settings;
5659
if ("oldest".equals(index.getName())) {
57-
return Settings.builder().put(IndexMetadata.SETTING_CREATION_DATE, 10)
58-
.put(IndexMetadata.SETTING_PRIORITY, 1).build();
60+
settings = buildSettings(10, 1);
5961
} else if ("newest".equals(index.getName())) {
60-
return Settings.builder().put(IndexMetadata.SETTING_CREATION_DATE, 100)
61-
.put(IndexMetadata.SETTING_PRIORITY, 1).build();
62+
settings = buildSettings(100, 1);
63+
} else {
64+
settings = Settings.EMPTY;
6265
}
63-
return Settings.EMPTY;
66+
67+
return IndexMetadata.builder(index.getName()).settings(settings).build();
6468
}
6569
});
6670
RoutingNodes.UnassignedShards.UnassignedIterator iterator = shards.iterator();
@@ -84,15 +88,53 @@ public void testPreferPriorityIndices() {
8488
}
8589
shards.sort(new PriorityComparator() {
8690
@Override
87-
protected Settings getIndexSettings(Index index) {
91+
protected IndexMetadata getMetadata(Index index) {
92+
Settings settings;
93+
if ("oldest".equals(index.getName())) {
94+
settings = buildSettings(10, 100);
95+
} else if ("newest".equals(index.getName())) {
96+
settings = buildSettings(100, 1);
97+
} else {
98+
settings = Settings.EMPTY;
99+
}
100+
101+
return IndexMetadata.builder(index.getName()).settings(settings).build();
102+
}
103+
});
104+
RoutingNodes.UnassignedShards.UnassignedIterator iterator = shards.iterator();
105+
ShardRouting next = iterator.next();
106+
assertEquals("oldest", next.getIndexName());
107+
next = iterator.next();
108+
assertEquals("newest", next.getIndexName());
109+
assertFalse(iterator.hasNext());
110+
}
111+
112+
public void testPreferSystemIndices() {
113+
RoutingNodes.UnassignedShards shards = new RoutingNodes.UnassignedShards(mock(RoutingNodes.class));
114+
List<ShardRouting> shardRoutings = Arrays.asList(
115+
TestShardRouting.newShardRouting("oldest", 0, null, null,
116+
randomBoolean(), ShardRoutingState.UNASSIGNED, new UnassignedInfo(randomFrom(UnassignedInfo.Reason.values()), "foobar")),
117+
TestShardRouting.newShardRouting("newest", 0, null, null,
118+
randomBoolean(), ShardRoutingState.UNASSIGNED, new UnassignedInfo(randomFrom(UnassignedInfo.Reason.values()), "foobar")));
119+
Collections.shuffle(shardRoutings, random());
120+
for (ShardRouting routing : shardRoutings) {
121+
shards.add(routing);
122+
}
123+
shards.sort(new PriorityComparator() {
124+
@Override
125+
protected IndexMetadata getMetadata(Index index) {
126+
Settings settings;
127+
boolean isSystem = false;
88128
if ("oldest".equals(index.getName())) {
89-
return Settings.builder().put(IndexMetadata.SETTING_CREATION_DATE, 10)
90-
.put(IndexMetadata.SETTING_PRIORITY, 100).build();
129+
settings = buildSettings(10, 100);
130+
isSystem = true;
91131
} else if ("newest".equals(index.getName())) {
92-
return Settings.builder().put(IndexMetadata.SETTING_CREATION_DATE, 100)
93-
.put(IndexMetadata.SETTING_PRIORITY, 1).build();
132+
settings = buildSettings(100, 1);
133+
} else {
134+
settings = Settings.EMPTY;
94135
}
95-
return Settings.EMPTY;
136+
137+
return IndexMetadata.builder(index.getName()).system(isSystem).settings(settings).build();
96138
}
97139
});
98140
RoutingNodes.UnassignedShards.UnassignedIterator iterator = shards.iterator();
@@ -106,75 +148,99 @@ protected Settings getIndexSettings(Index index) {
106148
public void testPriorityComparatorSort() {
107149
RoutingNodes.UnassignedShards shards = new RoutingNodes.UnassignedShards(mock(RoutingNodes.class));
108150
int numIndices = randomIntBetween(3, 99);
109-
IndexMeta[] indices = new IndexMeta[numIndices];
110-
final Map<String, IndexMeta> map = new HashMap<>();
151+
IndexMetadata[] indices = new IndexMetadata[numIndices];
152+
final Map<String, IndexMetadata> map = new HashMap<>();
111153

112154
for (int i = 0; i < indices.length; i++) {
155+
int priority = 0;
156+
int creationDate = 0;
157+
boolean isSystem = false;
158+
113159
if (frequently()) {
114-
indices[i] = new IndexMeta("idx_2015_04_" + String.format(Locale.ROOT, "%02d", i), randomIntBetween(1, 1000),
115-
randomIntBetween(1, 10000));
116-
} else { // sometimes just use defaults
117-
indices[i] = new IndexMeta("idx_2015_04_" + String.format(Locale.ROOT, "%02d", i));
160+
priority = randomIntBetween(1, 1000);
161+
creationDate = randomIntBetween(1, 10000);
118162
}
119-
map.put(indices[i].name, indices[i]);
163+
if (rarely()) {
164+
isSystem = true;
165+
}
166+
// else sometimes just use the defaults
167+
168+
indices[i] = IndexMetadata.builder(String.format(Locale.ROOT, "idx_%04d", i))
169+
.system(isSystem)
170+
.settings(buildSettings(creationDate, priority))
171+
.build();
172+
173+
map.put(indices[i].getIndex().getName(), indices[i]);
120174
}
121175
int numShards = randomIntBetween(10, 100);
122176
for (int i = 0; i < numShards; i++) {
123-
IndexMeta indexMeta = randomFrom(indices);
124-
shards.add(TestShardRouting.newShardRouting(indexMeta.name, randomIntBetween(1, 5), null, null,
177+
IndexMetadata indexMeta = randomFrom(indices);
178+
shards.add(TestShardRouting.newShardRouting(indexMeta.getIndex().getName(), randomIntBetween(1, 5), null, null,
125179
randomBoolean(), ShardRoutingState.UNASSIGNED, new UnassignedInfo(randomFrom(UnassignedInfo.Reason.values()),
126180
"foobar")));
127181
}
128182
shards.sort(new PriorityComparator() {
129183
@Override
130-
protected Settings getIndexSettings(Index index) {
131-
IndexMeta indexMeta = map.get(index.getName());
132-
return indexMeta.settings;
184+
protected IndexMetadata getMetadata(Index index) {
185+
return map.get(index.getName());
133186
}
134187
});
135188
ShardRouting previous = null;
136189
for (ShardRouting routing : shards) {
137190
if (previous != null) {
138-
IndexMeta prevMeta = map.get(previous.getIndexName());
139-
IndexMeta currentMeta = map.get(routing.getIndexName());
140-
if (prevMeta.priority == currentMeta.priority) {
141-
if (prevMeta.creationDate == currentMeta.creationDate) {
142-
if (prevMeta.name.equals(currentMeta.name) == false) {
143-
assertTrue("indexName mismatch, expected:" + currentMeta.name + " after " + prevMeta.name + " " +
144-
prevMeta.name.compareTo(currentMeta.name), prevMeta.name.compareTo(currentMeta.name) > 0);
191+
IndexMetadata prevMeta = map.get(previous.getIndexName());
192+
IndexMetadata currentMeta = map.get(routing.getIndexName());
193+
194+
if (prevMeta.isSystem() == currentMeta.isSystem()) {
195+
final int prevPriority = prevMeta.getSettings().getAsInt(IndexMetadata.SETTING_PRIORITY, -1);
196+
final int currentPriority = currentMeta.getSettings().getAsInt(IndexMetadata.SETTING_PRIORITY, -1);
197+
198+
if (prevPriority == currentPriority) {
199+
final int prevCreationDate = prevMeta.getSettings().getAsInt(IndexMetadata.SETTING_CREATION_DATE, -1);
200+
final int currentCreationDate = currentMeta.getSettings().getAsInt(IndexMetadata.SETTING_CREATION_DATE, -1);
201+
202+
if (prevCreationDate == currentCreationDate) {
203+
final String prevName = prevMeta.getIndex().getName();
204+
final String currentName = currentMeta.getIndex().getName();
205+
206+
if (prevName.equals(currentName) == false) {
207+
assertThat(
208+
"indexName mismatch, expected:" + currentName + " after " + prevName,
209+
prevName,
210+
greaterThan(currentName)
211+
);
212+
}
213+
} else {
214+
assertThat(
215+
"creationDate mismatch, expected:" + currentCreationDate + " after " + prevCreationDate,
216+
prevCreationDate, greaterThan(currentCreationDate)
217+
);
145218
}
146219
} else {
147-
assertTrue("creationDate mismatch, expected:" + currentMeta.creationDate + " after " + prevMeta.creationDate,
148-
prevMeta.creationDate > currentMeta.creationDate);
220+
assertThat(
221+
"priority mismatch, expected:" + currentPriority + " after " + prevPriority,
222+
prevPriority, greaterThan(currentPriority)
223+
);
149224
}
150225
} else {
151-
assertTrue("priority mismatch, expected:" + currentMeta.priority + " after " + prevMeta.priority,
152-
prevMeta.priority > currentMeta.priority);
226+
assertThat(
227+
"system mismatch, expected:" + currentMeta.isSystem() + " after " + prevMeta.isSystem(),
228+
prevMeta.isSystem(),
229+
greaterThan(currentMeta.isSystem())
230+
);
153231
}
154232
}
155233
previous = routing;
156234
}
157235
}
158236

159-
private static class IndexMeta {
160-
final String name;
161-
final int priority;
162-
final long creationDate;
163-
final Settings settings;
164-
165-
private IndexMeta(String name) { // default
166-
this.name = name;
167-
this.priority = 1;
168-
this.creationDate = -1;
169-
this.settings = Settings.EMPTY;
170-
}
171-
172-
private IndexMeta(String name, int priority, long creationDate) {
173-
this.name = name;
174-
this.priority = priority;
175-
this.creationDate = creationDate;
176-
this.settings = Settings.builder().put(IndexMetadata.SETTING_CREATION_DATE, creationDate)
177-
.put(IndexMetadata.SETTING_PRIORITY, priority).build();
178-
}
237+
private static Settings buildSettings(int creationDate, int priority) {
238+
return Settings.builder()
239+
.put(IndexMetadata.SETTING_CREATION_DATE, creationDate)
240+
.put(IndexMetadata.SETTING_PRIORITY, priority)
241+
.put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT)
242+
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
243+
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
244+
.build();
179245
}
180246
}

0 commit comments

Comments
 (0)