Skip to content

Commit 7293f47

Browse files
Speed up Name Collision Check in Metadata.Builder (#83340)
Once either indices, datastreams or their aliases become very numerous, these checks of adding everything to a fresh set and then retaining collisions become very expensive. Slightly adjusted the logic to just collect collisions instead to save endless set adding. Also refactored the logic a little to make it easier to profile the time spent on these validations and extraced some cold-paths for maybe a minor speedup.
1 parent 7c6a2a6 commit 7293f47

File tree

1 file changed

+115
-71
lines changed
  • server/src/main/java/org/elasticsearch/cluster/metadata

1 file changed

+115
-71
lines changed

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

Lines changed: 115 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,22 +1627,19 @@ public Metadata build(boolean builtIndicesLookupEagerly) {
16271627
// 1) The datastructures will be rebuilt only when needed. Now during serializing we rebuild these datastructures
16281628
// while these datastructures aren't even used.
16291629
// 2) The aliasAndIndexLookup can be updated instead of rebuilding it all the time.
1630-
1631-
final Set<String> allIndices = new HashSet<>(indices.size());
16321630
final List<String> visibleIndices = new ArrayList<>();
16331631
final List<String> allOpenIndices = new ArrayList<>();
16341632
final List<String> visibleOpenIndices = new ArrayList<>();
16351633
final List<String> allClosedIndices = new ArrayList<>();
16361634
final List<String> visibleClosedIndices = new ArrayList<>();
1637-
final Set<String> allAliases = new HashSet<>();
1635+
final Set<String> indicesAliases = new HashSet<>();
16381636
final ImmutableOpenMap<String, IndexMetadata> indicesMap = indices.build();
1637+
final Set<String> allIndices = indicesMap.keySet();
16391638

16401639
int oldestIndexVersionId = Version.CURRENT.id;
16411640

16421641
for (IndexMetadata indexMetadata : indicesMap.values()) {
16431642
final String name = indexMetadata.getIndex().getName();
1644-
boolean added = allIndices.add(name);
1645-
assert added : "double index named [" + name + "]";
16461643
final boolean visible = indexMetadata.isHidden() == false;
16471644
if (visible) {
16481645
visibleIndices.add(name);
@@ -1658,74 +1655,12 @@ public Metadata build(boolean builtIndicesLookupEagerly) {
16581655
visibleClosedIndices.add(name);
16591656
}
16601657
}
1661-
indexMetadata.getAliases().keysIt().forEachRemaining(allAliases::add);
1658+
indexMetadata.getAliases().keysIt().forEachRemaining(indicesAliases::add);
16621659
oldestIndexVersionId = Math.min(oldestIndexVersionId, indexMetadata.getCreationVersion().id);
16631660
}
16641661

1665-
final ArrayList<String> duplicates = new ArrayList<>();
1666-
final Set<String> allDataStreams = new HashSet<>();
1667-
DataStreamMetadata dataStreamMetadata = (DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE);
1668-
if (dataStreamMetadata != null) {
1669-
for (DataStream dataStream : dataStreamMetadata.dataStreams().values()) {
1670-
allDataStreams.add(dataStream.getName());
1671-
}
1672-
// Adding data stream aliases:
1673-
for (String dataStreamAlias : dataStreamMetadata.getDataStreamAliases().keySet()) {
1674-
if (allAliases.add(dataStreamAlias) == false) {
1675-
duplicates.add("data stream alias and indices alias have the same name (" + dataStreamAlias + ")");
1676-
}
1677-
}
1678-
}
1679-
1680-
final Set<String> aliasDuplicatesWithIndices = new HashSet<>(allAliases);
1681-
aliasDuplicatesWithIndices.retainAll(allIndices);
1682-
if (aliasDuplicatesWithIndices.isEmpty() == false) {
1683-
// iterate again and constructs a helpful message
1684-
for (IndexMetadata cursor : indicesMap.values()) {
1685-
for (String alias : aliasDuplicatesWithIndices) {
1686-
if (cursor.getAliases().containsKey(alias)) {
1687-
duplicates.add(alias + " (alias of " + cursor.getIndex() + ") conflicts with index");
1688-
}
1689-
}
1690-
}
1691-
}
1692-
1693-
final Set<String> aliasDuplicatesWithDataStreams = new HashSet<>(allAliases);
1694-
aliasDuplicatesWithDataStreams.retainAll(allDataStreams);
1695-
if (aliasDuplicatesWithDataStreams.isEmpty() == false) {
1696-
// iterate again and constructs a helpful message
1697-
for (String alias : aliasDuplicatesWithDataStreams) {
1698-
// reported var avoids adding a message twice if an index alias has the same name as a data stream.
1699-
boolean reported = false;
1700-
for (IndexMetadata cursor : indicesMap.values()) {
1701-
if (cursor.getAliases().containsKey(alias)) {
1702-
duplicates.add(alias + " (alias of " + cursor.getIndex() + ") conflicts with data stream");
1703-
reported = true;
1704-
}
1705-
}
1706-
// This is for adding an error message for when a data steam alias has the same name as a data stream.
1707-
if (reported == false && dataStreamMetadata != null && dataStreamMetadata.dataStreams().containsKey(alias)) {
1708-
duplicates.add("data stream alias and data stream have the same name (" + alias + ")");
1709-
}
1710-
}
1711-
}
1712-
1713-
final Set<String> dataStreamDuplicatesWithIndices = new HashSet<>(allDataStreams);
1714-
dataStreamDuplicatesWithIndices.retainAll(allIndices);
1715-
if (dataStreamDuplicatesWithIndices.isEmpty() == false) {
1716-
for (String dataStream : dataStreamDuplicatesWithIndices) {
1717-
duplicates.add("data stream [" + dataStream + "] conflicts with index");
1718-
}
1719-
}
1720-
1721-
if (duplicates.size() > 0) {
1722-
throw new IllegalStateException(
1723-
"index, alias, and data stream names need to be unique, but the following duplicates "
1724-
+ "were found ["
1725-
+ Strings.collectionToCommaDelimitedString(duplicates)
1726-
+ "]"
1727-
);
1728-
}
1662+
final DataStreamMetadata dataStreamMetadata = (DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE);
1663+
ensureNoNameCollisions(indicesAliases, indicesMap, allIndices, dataStreamMetadata);
17291664

17301665
SortedMap<String, IndexAbstraction> indicesLookup;
17311666
if (previousIndicesLookup != null) {
@@ -1787,8 +1722,117 @@ public Metadata build(boolean builtIndicesLookupEagerly) {
17871722
);
17881723
}
17891724

1790-
static SortedMap<String, IndexAbstraction> buildIndicesLookup(
1725+
private static void ensureNoNameCollisions(
1726+
Set<String> indexAliases,
1727+
ImmutableOpenMap<String, IndexMetadata> indicesMap,
1728+
Set<String> allIndices,
1729+
@Nullable DataStreamMetadata dataStreamMetadata
1730+
) {
1731+
final ArrayList<String> duplicates = new ArrayList<>();
1732+
final Set<String> aliasDuplicatesWithIndices = new HashSet<>();
1733+
for (String alias : indexAliases) {
1734+
if (allIndices.contains(alias)) {
1735+
aliasDuplicatesWithIndices.add(alias);
1736+
}
1737+
}
1738+
1739+
final Set<String> aliasDuplicatesWithDataStreams = new HashSet<>();
1740+
final Set<String> allDataStreams;
1741+
if (dataStreamMetadata != null) {
1742+
allDataStreams = dataStreamMetadata.dataStreams().keySet();
1743+
// Adding data stream aliases:
1744+
for (String dataStreamAlias : dataStreamMetadata.getDataStreamAliases().keySet()) {
1745+
if (indexAliases.contains(dataStreamAlias)) {
1746+
duplicates.add("data stream alias and indices alias have the same name (" + dataStreamAlias + ")");
1747+
}
1748+
if (allIndices.contains(dataStreamAlias)) {
1749+
aliasDuplicatesWithIndices.add(dataStreamAlias);
1750+
}
1751+
if (allDataStreams.contains(dataStreamAlias)) {
1752+
aliasDuplicatesWithDataStreams.add(dataStreamAlias);
1753+
}
1754+
}
1755+
} else {
1756+
allDataStreams = Set.of();
1757+
}
1758+
1759+
if (aliasDuplicatesWithIndices.isEmpty() == false) {
1760+
collectAliasDuplicates(indicesMap, aliasDuplicatesWithIndices, duplicates);
1761+
}
1762+
1763+
for (String alias : indexAliases) {
1764+
if (allDataStreams.contains(alias)) {
1765+
aliasDuplicatesWithDataStreams.add(alias);
1766+
}
1767+
}
1768+
if (aliasDuplicatesWithDataStreams.isEmpty() == false) {
1769+
collectAliasDuplicates(indicesMap, dataStreamMetadata, aliasDuplicatesWithDataStreams, duplicates);
1770+
}
1771+
1772+
final Set<String> dataStreamDuplicatesWithIndices = new HashSet<>();
1773+
for (String ds : allDataStreams) {
1774+
if (allIndices.contains(ds)) {
1775+
dataStreamDuplicatesWithIndices.add(ds);
1776+
}
1777+
}
1778+
for (String dataStream : dataStreamDuplicatesWithIndices) {
1779+
duplicates.add("data stream [" + dataStream + "] conflicts with index");
1780+
}
1781+
1782+
if (duplicates.isEmpty() == false) {
1783+
throw new IllegalStateException(
1784+
"index, alias, and data stream names need to be unique, but the following duplicates "
1785+
+ "were found ["
1786+
+ Strings.collectionToCommaDelimitedString(duplicates)
1787+
+ "]"
1788+
);
1789+
}
1790+
}
1791+
1792+
/**
1793+
* Iterates the detected duplicates between datastreams and aliases and collects them into the duplicates list as helpful messages.
1794+
*/
1795+
private static void collectAliasDuplicates(
1796+
ImmutableOpenMap<String, IndexMetadata> indicesMap,
17911797
DataStreamMetadata dataStreamMetadata,
1798+
Set<String> aliasDuplicatesWithDataStreams,
1799+
ArrayList<String> duplicates
1800+
) {
1801+
for (String alias : aliasDuplicatesWithDataStreams) {
1802+
// reported var avoids adding a message twice if an index alias has the same name as a data stream.
1803+
boolean reported = false;
1804+
for (IndexMetadata cursor : indicesMap.values()) {
1805+
if (cursor.getAliases().containsKey(alias)) {
1806+
duplicates.add(alias + " (alias of " + cursor.getIndex() + ") conflicts with data stream");
1807+
reported = true;
1808+
}
1809+
}
1810+
// This is for adding an error message for when a data steam alias has the same name as a data stream.
1811+
if (reported == false && dataStreamMetadata != null && dataStreamMetadata.dataStreams().containsKey(alias)) {
1812+
duplicates.add("data stream alias and data stream have the same name (" + alias + ")");
1813+
}
1814+
}
1815+
}
1816+
1817+
/**
1818+
* Collect all duplicate names across indices and aliases that were detected into a list of helpful duplicate failure messages.
1819+
*/
1820+
private static void collectAliasDuplicates(
1821+
ImmutableOpenMap<String, IndexMetadata> indicesMap,
1822+
Set<String> aliasDuplicatesWithIndices,
1823+
ArrayList<String> duplicates
1824+
) {
1825+
for (IndexMetadata cursor : indicesMap.values()) {
1826+
for (String alias : aliasDuplicatesWithIndices) {
1827+
if (cursor.getAliases().containsKey(alias)) {
1828+
duplicates.add(alias + " (alias of " + cursor.getIndex() + ") conflicts with index");
1829+
}
1830+
}
1831+
}
1832+
}
1833+
1834+
static SortedMap<String, IndexAbstraction> buildIndicesLookup(
1835+
@Nullable DataStreamMetadata dataStreamMetadata,
17921836
ImmutableOpenMap<String, IndexMetadata> indices
17931837
) {
17941838
SortedMap<String, IndexAbstraction> indicesLookup = new TreeMap<>();

0 commit comments

Comments
 (0)