diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/ProjectMetadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/ProjectMetadata.java index 24adeedd7366e..5ea0bada50946 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/ProjectMetadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/ProjectMetadata.java @@ -46,6 +46,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -1761,7 +1762,7 @@ static SortedMap buildIndicesLookup( if (indices.isEmpty()) { return Collections.emptySortedMap(); } - SortedMap indicesLookup = new TreeMap<>(); + Map indicesLookup = new HashMap<>(); Map indexToDataStreamLookup = new HashMap<>(); collectDataStreams(dataStreamMetadata, indicesLookup, indexToDataStreamLookup); @@ -1769,7 +1770,121 @@ static SortedMap buildIndicesLookup( collectIndices(indices, indexToDataStreamLookup, indicesLookup, aliasToIndices); collectAliases(aliasToIndices, indicesLookup); - return Collections.unmodifiableSortedMap(indicesLookup); + // We do a ton of lookups on this map but also need its sorted properties at times. + // Using this hybrid of a sorted and a hash-map trades some heap overhead relative to just using a TreeMap + // for much faster O(1) lookups in large clusters. + return new SortedMap<>() { + + private final SortedMap sortedMap = Collections.unmodifiableSortedMap( + new TreeMap<>(indicesLookup) + ); + + @Override + public Comparator comparator() { + return sortedMap.comparator(); + } + + @Override + public SortedMap subMap(String fromKey, String toKey) { + return sortedMap.subMap(fromKey, toKey); + } + + @Override + public SortedMap headMap(String toKey) { + return sortedMap.headMap(toKey); + } + + @Override + public SortedMap tailMap(String fromKey) { + return sortedMap.tailMap(fromKey); + } + + @Override + public String firstKey() { + return sortedMap.firstKey(); + } + + @Override + public String lastKey() { + return sortedMap.lastKey(); + } + + @Override + public Set keySet() { + return sortedMap.keySet(); + } + + @Override + public Collection values() { + return sortedMap.values(); + } + + @Override + public Set> entrySet() { + return sortedMap.entrySet(); + } + + @Override + public int size() { + return indicesLookup.size(); + } + + @Override + public boolean isEmpty() { + return indicesLookup.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return indicesLookup.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return indicesLookup.containsValue(value); + } + + @Override + public IndexAbstraction get(Object key) { + return indicesLookup.get(key); + } + + @Override + public IndexAbstraction put(String key, IndexAbstraction value) { + throw new UnsupportedOperationException(); + } + + @Override + public IndexAbstraction remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + return indicesLookup.equals(obj); + } + + @Override + public int hashCode() { + return indicesLookup.hashCode(); + } + }; } private static void collectAliases(Map> aliasToIndices, Map indicesLookup) {