Skip to content

Commit a2c3298

Browse files
martijnvgAdam Locke
authored andcommitted
Reuse previous indices lookup when possible (elastic#79004)
In cases when indices, aliases and data streams aren't modified then the indices lookup can be reused. For example in: * The IndexMetadataUpdater#applyChanges(...) method builds a new metadata instance, but only primary term or insync allocations may be updated. No new indices, aliases or data streams are added, so re-building indices lookup is not necessary. * MasterService#patchVersions Additionally the logic that checks when indices lookup can be reused, this logic also checks the hidden and system flags of indices/datastreams. In clusters with many indices the cost of building indices lookup is non-neglectable and should be avoided in this case. Closes elastic#78980 Partially addresses to elastic#77888
1 parent 683d072 commit a2c3298

File tree

12 files changed

+223
-14
lines changed

12 files changed

+223
-14
lines changed

server/src/main/java/org/elasticsearch/cluster/metadata/IndexAbstraction.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,23 @@ public boolean isSystem() {
179179
public List<String> getAliases() {
180180
return aliases;
181181
}
182+
183+
@Override
184+
public boolean equals(Object o) {
185+
if (this == o) return true;
186+
if (o == null || getClass() != o.getClass()) return false;
187+
ConcreteIndex that = (ConcreteIndex) o;
188+
return isHidden == that.isHidden &&
189+
isSystem == that.isSystem &&
190+
concreteIndexName.equals(that.concreteIndexName) &&
191+
Objects.equals(aliases, that.aliases) &&
192+
Objects.equals(dataStream, that.dataStream);
193+
}
194+
195+
@Override
196+
public int hashCode() {
197+
return Objects.hash(concreteIndexName, isHidden, isSystem, aliases, dataStream);
198+
}
182199
}
183200

184201
/**
@@ -322,6 +339,24 @@ private void validateAliasProperties(List<IndexMetadata> referenceIndexMetadatas
322339
private boolean isNonEmpty(List<IndexMetadata> idxMetas) {
323340
return (Objects.isNull(idxMetas) || idxMetas.isEmpty()) == false;
324341
}
342+
343+
@Override
344+
public boolean equals(Object o) {
345+
if (this == o) return true;
346+
if (o == null || getClass() != o.getClass()) return false;
347+
Alias alias = (Alias) o;
348+
return isHidden == alias.isHidden &&
349+
isSystem == alias.isSystem &&
350+
dataStreamAlias == alias.dataStreamAlias &&
351+
aliasName.equals(alias.aliasName) &&
352+
referenceIndexMetadatas.equals(alias.referenceIndexMetadatas) &&
353+
Objects.equals(writeIndex, alias.writeIndex);
354+
}
355+
356+
@Override
357+
public int hashCode() {
358+
return Objects.hash(aliasName, referenceIndexMetadatas, writeIndex, isHidden, isSystem, dataStreamAlias);
359+
}
325360
}
326361

327362
class DataStream implements IndexAbstraction {
@@ -383,6 +418,20 @@ public List<String> getAliases() {
383418
public org.elasticsearch.cluster.metadata.DataStream getDataStream() {
384419
return dataStream;
385420
}
421+
422+
@Override
423+
public boolean equals(Object o) {
424+
if (this == o) return true;
425+
if (o == null || getClass() != o.getClass()) return false;
426+
DataStream that = (DataStream) o;
427+
return dataStream.equals(that.dataStream) &&
428+
Objects.equals(referencedByDataStreamAliases, that.referencedByDataStreamAliases);
429+
}
430+
431+
@Override
432+
public int hashCode() {
433+
return Objects.hash(dataStream, referencedByDataStreamAliases);
434+
}
386435
}
387436

388437
}

server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,15 +1038,18 @@ public static class Builder {
10381038
private final ImmutableOpenMap.Builder<String, IndexTemplateMetadata> templates;
10391039
private final ImmutableOpenMap.Builder<String, Custom> customs;
10401040

1041+
private SortedMap<String, IndexAbstraction> previousIndicesLookup;
1042+
10411043
public Builder() {
10421044
clusterUUID = UNKNOWN_CLUSTER_UUID;
10431045
indices = ImmutableOpenMap.builder();
10441046
templates = ImmutableOpenMap.builder();
10451047
customs = ImmutableOpenMap.builder();
10461048
indexGraveyard(IndexGraveyard.builder().build()); // create new empty index graveyard to initialize
1049+
previousIndicesLookup = null;
10471050
}
10481051

1049-
public Builder(Metadata metadata) {
1052+
Builder(Metadata metadata) {
10501053
this.clusterUUID = metadata.clusterUUID;
10511054
this.clusterUUIDCommitted = metadata.clusterUUIDCommitted;
10521055
this.coordinationMetadata = metadata.coordinationMetadata;
@@ -1057,13 +1060,17 @@ public Builder(Metadata metadata) {
10571060
this.indices = ImmutableOpenMap.builder(metadata.indices);
10581061
this.templates = ImmutableOpenMap.builder(metadata.templates);
10591062
this.customs = ImmutableOpenMap.builder(metadata.customs);
1063+
previousIndicesLookup = metadata.getIndicesLookup();
10601064
}
10611065

10621066
public Builder put(IndexMetadata.Builder indexMetadataBuilder) {
10631067
// we know its a new one, increment the version and store
10641068
indexMetadataBuilder.version(indexMetadataBuilder.version() + 1);
10651069
IndexMetadata indexMetadata = indexMetadataBuilder.build();
1066-
indices.put(indexMetadata.getIndex().getName(), indexMetadata);
1070+
IndexMetadata previous = indices.put(indexMetadata.getIndex().getName(), indexMetadata);
1071+
if (unsetPreviousIndicesLookup(previous, indexMetadata)) {
1072+
previousIndicesLookup = null;
1073+
}
10671074
return this;
10681075
}
10691076

@@ -1075,10 +1082,37 @@ public Builder put(IndexMetadata indexMetadata, boolean incrementVersion) {
10751082
if (incrementVersion) {
10761083
indexMetadata = IndexMetadata.builder(indexMetadata).version(indexMetadata.getVersion() + 1).build();
10771084
}
1078-
indices.put(indexMetadata.getIndex().getName(), indexMetadata);
1085+
IndexMetadata previous = indices.put(indexMetadata.getIndex().getName(), indexMetadata);
1086+
if (unsetPreviousIndicesLookup(previous, indexMetadata)) {
1087+
previousIndicesLookup = null;
1088+
}
10791089
return this;
10801090
}
10811091

1092+
boolean unsetPreviousIndicesLookup(IndexMetadata previous, IndexMetadata current) {
1093+
if (previous == null) {
1094+
return true;
1095+
}
1096+
1097+
if (previous.getAliases().equals(current.getAliases()) == false) {
1098+
return true;
1099+
}
1100+
1101+
if (previous.isHidden() != current.isHidden()) {
1102+
return true;
1103+
}
1104+
1105+
if (previous.isSystem() != current.isSystem()) {
1106+
return true;
1107+
}
1108+
1109+
if (previous.getState() != current.getState()) {
1110+
return true;
1111+
}
1112+
1113+
return false;
1114+
}
1115+
10821116
public IndexMetadata get(String index) {
10831117
return indices.get(index);
10841118
}
@@ -1097,16 +1131,22 @@ public IndexMetadata getSafe(Index index) {
10971131
}
10981132

10991133
public Builder remove(String index) {
1134+
previousIndicesLookup = null;
1135+
11001136
indices.remove(index);
11011137
return this;
11021138
}
11031139

11041140
public Builder removeAllIndices() {
1141+
previousIndicesLookup = null;
1142+
11051143
indices.clear();
11061144
return this;
11071145
}
11081146

11091147
public Builder indices(ImmutableOpenMap<String, IndexMetadata> indices) {
1148+
previousIndicesLookup = null;
1149+
11101150
this.indices.putAll(indices);
11111151
return this;
11121152
}
@@ -1187,6 +1227,8 @@ public Builder removeIndexTemplate(String name) {
11871227
}
11881228

11891229
public DataStream dataStream(String dataStreamName) {
1230+
previousIndicesLookup = null;
1231+
11901232
DataStreamMetadata dataStreamMetadata = (DataStreamMetadata) customs.get(DataStreamMetadata.TYPE);
11911233
if (dataStreamMetadata != null) {
11921234
return dataStreamMetadata.dataStreams().get(dataStreamName);
@@ -1196,11 +1238,15 @@ public DataStream dataStream(String dataStreamName) {
11961238
}
11971239

11981240
public Builder dataStreams(Map<String, DataStream> dataStreams, Map<String, DataStreamAlias> dataStreamAliases) {
1241+
previousIndicesLookup = null;
1242+
11991243
this.customs.put(DataStreamMetadata.TYPE, new DataStreamMetadata(dataStreams, dataStreamAliases));
12001244
return this;
12011245
}
12021246

12031247
public Builder put(DataStream dataStream) {
1248+
previousIndicesLookup = null;
1249+
12041250
Objects.requireNonNull(dataStream, "it is invalid to add a null data stream");
12051251
Map<String, DataStream> existingDataStreams =
12061252
Optional.ofNullable((DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE))
@@ -1217,6 +1263,8 @@ public Builder put(DataStream dataStream) {
12171263
}
12181264

12191265
public boolean put(String aliasName, String dataStream, Boolean isWriteDataStream, String filter) {
1266+
previousIndicesLookup = null;
1267+
12201268
Map<String, DataStream> existingDataStream =
12211269
Optional.ofNullable((DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE))
12221270
.map(dsmd -> new HashMap<>(dsmd.dataStreams()))
@@ -1255,6 +1303,8 @@ public boolean put(String aliasName, String dataStream, Boolean isWriteDataStrea
12551303
}
12561304

12571305
public Builder removeDataStream(String name) {
1306+
previousIndicesLookup = null;
1307+
12581308
Map<String, DataStream> existingDataStreams =
12591309
Optional.ofNullable((DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE))
12601310
.map(dsmd -> new HashMap<>(dsmd.dataStreams()))
@@ -1291,6 +1341,8 @@ public Builder removeDataStream(String name) {
12911341
}
12921342

12931343
public boolean removeDataStreamAlias(String aliasName, String dataStreamName, boolean mustExist) {
1344+
previousIndicesLookup = null;
1345+
12941346
Map<String, DataStreamAlias> dataStreamAliases =
12951347
Optional.ofNullable((DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE))
12961348
.map(dsmd -> new HashMap<>(dsmd.getDataStreamAliases()))
@@ -1538,10 +1590,15 @@ public Metadata build(boolean builtIndicesLookupEagerly) {
15381590
ImmutableOpenMap<String, IndexMetadata> indices = this.indices.build();
15391591

15401592
SortedMap<String, IndexAbstraction> indicesLookup;
1541-
if (builtIndicesLookupEagerly) {
1542-
indicesLookup = Collections.unmodifiableSortedMap(buildIndicesLookup(dataStreamMetadata, indices));
1593+
if (previousIndicesLookup != null) {
1594+
assert previousIndicesLookup.equals(buildIndicesLookup(dataStreamMetadata, indices));
1595+
indicesLookup = previousIndicesLookup;
15431596
} else {
1544-
indicesLookup = null;
1597+
if (builtIndicesLookupEagerly) {
1598+
indicesLookup = Collections.unmodifiableSortedMap(buildIndicesLookup(dataStreamMetadata, indices));
1599+
} else {
1600+
indicesLookup = null;
1601+
}
15451602
}
15461603

15471604

server/src/main/java/org/elasticsearch/cluster/routing/allocation/IndexMetadataUpdater.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ public Metadata applyChanges(Metadata oldMetadata, RoutingTable newRoutingTable)
121121
}
122122

123123
if (metadataBuilder != null) {
124-
return metadataBuilder.build();
124+
Metadata newMetadata = metadataBuilder.build();
125+
assert oldMetadata.getIndicesLookup() == newMetadata.getIndicesLookup();
126+
return newMetadata;
125127
} else {
126128
return oldMetadata;
127129
}

server/src/main/java/org/elasticsearch/cluster/service/MasterService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ private ClusterState patchVersions(ClusterState previousClusterState, ClusterTas
368368

369369
if (previousClusterState != newClusterState) {
370370
// only the master controls the version numbers
371+
final var previousIndicesLookup = newClusterState.metadata().getIndicesLookup();
371372
Builder builder = incrementVersion(newClusterState);
372373
if (previousClusterState.routingTable() != newClusterState.routingTable()) {
373374
builder.routingTable(RoutingTable.builder(newClusterState.routingTable())
@@ -378,6 +379,7 @@ private ClusterState patchVersions(ClusterState previousClusterState, ClusterTas
378379
}
379380

380381
newClusterState = builder.build();
382+
assert previousIndicesLookup == newClusterState.metadata().getIndicesLookup();
381383
}
382384

383385
return newClusterState;

server/src/test/java/org/elasticsearch/cluster/ClusterChangedEventTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ private static ClusterState createNonInitializedState(final int numNodes, final
354354
private static ClusterState nextState(final ClusterState previousState, List<TestCustomMetadata> customMetadataList) {
355355
final ClusterState.Builder builder = ClusterState.builder(previousState);
356356
builder.stateUUID(UUIDs.randomBase64UUID());
357-
Metadata.Builder metadataBuilder = new Metadata.Builder(previousState.metadata());
357+
Metadata.Builder metadataBuilder = Metadata.builder(previousState.metadata());
358358
for (ObjectObjectCursor<String, Metadata.Custom> customMetadata : previousState.metadata().customs()) {
359359
if (customMetadata.value instanceof TestCustomMetadata) {
360360
metadataBuilder.removeCustom(customMetadata.key);

server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.common.settings.Setting;
2525
import org.elasticsearch.common.settings.Settings;
2626
import org.elasticsearch.common.util.set.Sets;
27+
import org.elasticsearch.index.IndexSettings;
2728
import org.elasticsearch.xcontent.XContentBuilder;
2829
import org.elasticsearch.common.xcontent.XContentHelper;
2930
import org.elasticsearch.xcontent.XContentParser;
@@ -57,8 +58,10 @@
5758
import static org.hamcrest.Matchers.equalTo;
5859
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
5960
import static org.hamcrest.Matchers.is;
61+
import static org.hamcrest.Matchers.not;
6062
import static org.hamcrest.Matchers.notNullValue;
6163
import static org.hamcrest.Matchers.nullValue;
64+
import static org.hamcrest.Matchers.sameInstance;
6265
import static org.hamcrest.Matchers.startsWith;
6366

6467
public class MetadataTests extends ESTestCase {
@@ -1520,6 +1523,102 @@ public void testDataStreamWriteRemoveDataStream() {
15201523
assertThat(metadata.dataStreamAliases().get("logs-postgres").getDataStreams(), containsInAnyOrder("logs-postgres-replicated"));
15211524
}
15221525

1526+
public void testReuseIndicesLookup() {
1527+
String indexName = "my-index";
1528+
String aliasName = "my-alias";
1529+
String dataStreamName = "logs-mysql-prod";
1530+
String dataStreamAliasName = "logs-mysql";
1531+
Metadata previous = Metadata.builder().build();
1532+
1533+
// Things that should change indices lookup
1534+
{
1535+
Metadata.Builder builder = Metadata.builder(previous);
1536+
IndexMetadata idx = DataStreamTestHelper.createFirstBackingIndex(dataStreamName).build();
1537+
builder.put(idx, true);
1538+
DataStream dataStream = new DataStream(dataStreamName, new DataStream.TimestampField("@timestamp"), List.of(idx.getIndex()));
1539+
builder.put(dataStream);
1540+
Metadata metadata = builder.build();
1541+
assertThat(previous.getIndicesLookup(), not(sameInstance(metadata.getIndicesLookup())));
1542+
previous = metadata;
1543+
}
1544+
{
1545+
Metadata.Builder builder = Metadata.builder(previous);
1546+
builder.put(dataStreamAliasName, dataStreamName, false, null);
1547+
Metadata metadata = builder.build();
1548+
assertThat(previous.getIndicesLookup(), not(sameInstance(metadata.getIndicesLookup())));
1549+
previous = metadata;
1550+
}
1551+
{
1552+
Metadata.Builder builder = Metadata.builder(previous);
1553+
builder.put(dataStreamAliasName, dataStreamName, true, null);
1554+
Metadata metadata = builder.build();
1555+
assertThat(previous.getIndicesLookup(), not(sameInstance(metadata.getIndicesLookup())));
1556+
previous = metadata;
1557+
}
1558+
{
1559+
Metadata.Builder builder = Metadata.builder(previous);
1560+
builder.put(IndexMetadata.builder(indexName)
1561+
.settings(settings(Version.CURRENT)).creationDate(randomNonNegativeLong())
1562+
.numberOfShards(1).numberOfReplicas(0));
1563+
Metadata metadata = builder.build();
1564+
assertThat(previous.getIndicesLookup(), not(sameInstance(metadata.getIndicesLookup())));
1565+
previous = metadata;
1566+
}
1567+
{
1568+
Metadata.Builder builder = Metadata.builder(previous);
1569+
IndexMetadata.Builder imBuilder = IndexMetadata.builder(builder.get(indexName));
1570+
imBuilder.putAlias(AliasMetadata.builder(aliasName).build());
1571+
builder.put(imBuilder);
1572+
Metadata metadata = builder.build();
1573+
assertThat(previous.getIndicesLookup(), not(sameInstance(metadata.getIndicesLookup())));
1574+
previous = metadata;
1575+
}
1576+
{
1577+
Metadata.Builder builder = Metadata.builder(previous);
1578+
IndexMetadata.Builder imBuilder = IndexMetadata.builder(builder.get(indexName));
1579+
imBuilder.putAlias(AliasMetadata.builder(aliasName).writeIndex(true).build());
1580+
builder.put(imBuilder);
1581+
Metadata metadata = builder.build();
1582+
assertThat(previous.getIndicesLookup(), not(sameInstance(metadata.getIndicesLookup())));
1583+
previous = metadata;
1584+
}
1585+
{
1586+
Metadata.Builder builder = Metadata.builder(previous);
1587+
IndexMetadata.Builder imBuilder = IndexMetadata.builder(builder.get(indexName));
1588+
Settings.Builder sBuilder = Settings.builder()
1589+
.put(builder.get(indexName).getSettings())
1590+
.put(IndexMetadata.INDEX_HIDDEN_SETTING.getKey(), true);
1591+
imBuilder.settings(sBuilder.build());
1592+
builder.put(imBuilder);
1593+
Metadata metadata = builder.build();
1594+
assertThat(previous.getIndicesLookup(), not(sameInstance(metadata.getIndicesLookup())));
1595+
previous = metadata;
1596+
}
1597+
1598+
// Things that shouldn't change indices lookup
1599+
{
1600+
Metadata.Builder builder = Metadata.builder(previous);
1601+
IndexMetadata.Builder imBuilder = IndexMetadata.builder(builder.get(indexName));
1602+
imBuilder.numberOfReplicas(2);
1603+
builder.put(imBuilder);
1604+
Metadata metadata = builder.build();
1605+
assertThat(previous.getIndicesLookup(), sameInstance(metadata.getIndicesLookup()));
1606+
previous = metadata;
1607+
}
1608+
{
1609+
Metadata.Builder builder = Metadata.builder(previous);
1610+
IndexMetadata.Builder imBuilder = IndexMetadata.builder(builder.get(indexName));
1611+
Settings.Builder sBuilder = Settings.builder()
1612+
.put(builder.get(indexName).getSettings())
1613+
.put(IndexSettings.DEFAULT_FIELD_SETTING.getKey(), "val");
1614+
imBuilder.settings(sBuilder.build());
1615+
builder.put(imBuilder);
1616+
Metadata metadata = builder.build();
1617+
assertThat(previous.getIndicesLookup(), sameInstance(metadata.getIndicesLookup()));
1618+
previous = metadata;
1619+
}
1620+
}
1621+
15231622
public static Metadata randomMetadata() {
15241623
return randomMetadata(1);
15251624
}

0 commit comments

Comments
 (0)