You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by me...@apache.org on 2026/03/18 13:45:32 UTC
(shardingsphere) branch master updated: Use IdentifierIndex to search metadata for ShardingSphereMetaData (#38498)
This is an automated email from the ASF dual-hosted git repository.
menghaoran pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new bf4cdbd57a6 Use IdentifierIndex to search metadata for ShardingSphereMetaData (#38498)
bf4cdbd57a6 is described below
commit bf4cdbd57a64ddccd28687902ebd110888e75aab
Author: Haoran Meng <me...@gmail.com>
AuthorDate: Wed Mar 18 21:45:25 2026 +0800
Use IdentifierIndex to search metadata for ShardingSphereMetaData (#38498)
---
.../infra/metadata/ShardingSphereMetaData.java | 28 ++----
.../infra/metadata/identifier/IdentifierIndex.java | 112 +++++++++++++++++++--
.../infra/metadata/ShardingSphereMetaDataTest.java | 11 ++
.../metadata/identifier/IdentifierIndexTest.java | 70 +++++++++++++
4 files changed, 194 insertions(+), 27 deletions(-)
diff --git a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaData.java b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaData.java
index a88207ce2f5..c3c6b54f138 100644
--- a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaData.java
+++ b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaData.java
@@ -42,11 +42,10 @@ import org.apache.shardingsphere.sql.parser.statement.core.value.identifier.Iden
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
-import java.util.Map;
import java.util.Optional;
import java.util.Properties;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
@@ -55,9 +54,6 @@ import java.util.stream.Collectors;
@Getter
public final class ShardingSphereMetaData implements AutoCloseable {
- @Getter(AccessLevel.NONE)
- private final Map<String, ShardingSphereDatabase> databases;
-
@Getter(AccessLevel.NONE)
private final DatabaseIdentifierContext identifierContext;
@@ -108,7 +104,6 @@ public final class ShardingSphereMetaData implements AutoCloseable {
private ShardingSphereMetaData(final Collection<ShardingSphereDatabase> databases, final ResourceMetaData globalResourceMetaData,
final RuleMetaData globalRuleMetaData, final ConfigurationProperties props, final DatabaseType protocolType,
final DatabaseIdentifierContext identifierContext) {
- this.databases = new ConcurrentHashMap<>(databases.stream().collect(Collectors.toMap(ShardingSphereDatabase::getName, each -> each)));
this.globalResourceMetaData = globalResourceMetaData;
this.globalRuleMetaData = globalRuleMetaData;
this.props = props;
@@ -116,7 +111,7 @@ public final class ShardingSphereMetaData implements AutoCloseable {
this.protocolType = protocolType;
this.identifierContext = identifierContext;
databaseIndex = new IdentifierIndex<>(identifierContext, IdentifierScope.DATABASE);
- rebuildDatabaseIndex();
+ databaseIndex.rebuild(new LinkedHashMap<>(databases.stream().collect(Collectors.toMap(ShardingSphereDatabase::getName, each -> each))));
}
/**
@@ -125,7 +120,7 @@ public final class ShardingSphereMetaData implements AutoCloseable {
* @return all databases
*/
public Collection<ShardingSphereDatabase> getAllDatabases() {
- return databases.values();
+ return databaseIndex.getAll();
}
/**
@@ -187,9 +182,8 @@ public final class ShardingSphereMetaData implements AutoCloseable {
*/
public void addDatabase(final String databaseName, final DatabaseType protocolType, final ConfigurationProperties props) {
ShardingSphereDatabase database = ShardingSphereDatabaseFactory.create(databaseName, protocolType, props);
- databases.put(database.getName(), database);
- rebuildDatabaseIndex();
- globalRuleMetaData.getRules().forEach(each -> ((GlobalRule) each).refresh(databases.values(), GlobalRuleChangedType.DATABASE_CHANGED));
+ databaseIndex.put(database.getName(), database);
+ globalRuleMetaData.getRules().forEach(each -> ((GlobalRule) each).refresh(getAllDatabases(), GlobalRuleChangedType.DATABASE_CHANGED));
}
/**
@@ -198,8 +192,7 @@ public final class ShardingSphereMetaData implements AutoCloseable {
* @param database database
*/
public void putDatabase(final ShardingSphereDatabase database) {
- databases.put(database.getName(), database);
- rebuildDatabaseIndex();
+ databaseIndex.put(database.getName(), database);
}
/**
@@ -212,14 +205,13 @@ public final class ShardingSphereMetaData implements AutoCloseable {
if (null == database) {
return;
}
- databases.remove(database.getName());
- rebuildDatabaseIndex();
+ databaseIndex.remove(database.getName());
cleanResources(database);
}
@SneakyThrows(Exception.class)
private void cleanResources(final ShardingSphereDatabase database) {
- globalRuleMetaData.getRules().forEach(each -> ((GlobalRule) each).refresh(databases.values(), GlobalRuleChangedType.DATABASE_CHANGED));
+ globalRuleMetaData.getRules().forEach(each -> ((GlobalRule) each).refresh(getAllDatabases(), GlobalRuleChangedType.DATABASE_CHANGED));
for (ShardingSphereRule each : database.getRuleMetaData().getRules()) {
if (each instanceof AutoCloseable) {
((AutoCloseable) each).close();
@@ -253,8 +245,4 @@ public final class ShardingSphereMetaData implements AutoCloseable {
private static DatabaseType resolveProtocolType(final Collection<ShardingSphereDatabase> databases, final ConfigurationProperties props) {
return databases.isEmpty() ? DatabaseTypeEngine.getProtocolType(Collections.emptyMap(), getProps(props)) : databases.iterator().next().getProtocolType();
}
-
- private void rebuildDatabaseIndex() {
- databaseIndex.rebuild(databases);
- }
}
diff --git a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndex.java b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndex.java
index e9b338519a9..7d37fc5c382 100644
--- a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndex.java
+++ b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndex.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.infra.metadata.identifier;
+import lombok.RequiredArgsConstructor;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCaseRule;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.LookupMode;
@@ -37,6 +38,7 @@ import java.util.Optional;
*
* @param <T> metadata object type
*/
+@RequiredArgsConstructor
public final class IdentifierIndex<T> {
private final DatabaseIdentifierContext databaseIdentifierContext;
@@ -45,18 +47,12 @@ public final class IdentifierIndex<T> {
private volatile Snapshot<T> snapshot = Snapshot.empty();
- public IdentifierIndex(final DatabaseIdentifierContext databaseIdentifierContext, final IdentifierScope identifierScope) {
- this.databaseIdentifierContext = Objects.requireNonNull(databaseIdentifierContext, "databaseIdentifierContext cannot be null.");
- this.identifierScope = Objects.requireNonNull(identifierScope, "identifierScope cannot be null.");
- }
-
/**
* Rebuild identifier index by actual names.
*
* @param values actual name and metadata object map
*/
public synchronized void rebuild(final Map<String, T> values) {
- Objects.requireNonNull(values, "values cannot be null.");
Map<String, T> newExactValues = new LinkedHashMap<>(values.size(), 1F);
Map<String, Collection<String>> newNormalizedIdentifiers = new LinkedHashMap<>(values.size(), 1F);
IdentifierCaseRule rule = databaseIdentifierContext.getRule(identifierScope);
@@ -64,7 +60,81 @@ public final class IdentifierIndex<T> {
newExactValues.put(entry.getKey(), entry.getValue());
addNormalizedIdentifier(newNormalizedIdentifiers, rule, entry.getKey());
}
- snapshot = new Snapshot<>(Collections.unmodifiableMap(newExactValues), Collections.unmodifiableMap(newNormalizedIdentifiers));
+ snapshot = createSnapshot(newExactValues, newNormalizedIdentifiers);
+ }
+
+ /**
+ * Get all metadata objects.
+ *
+ * @return all metadata objects
+ */
+ public Collection<T> getAll() {
+ return snapshot.getExactValues().values();
+ }
+
+ /**
+ * Get all actual names.
+ *
+ * @return all actual names
+ */
+ public Collection<String> getAllNames() {
+ return snapshot.getExactValues().keySet();
+ }
+
+ /**
+ * Put metadata object by actual name.
+ *
+ * @param name actual name
+ * @param value metadata object
+ */
+ public synchronized void put(final String name, final T value) {
+ Snapshot<T> currentSnapshot = snapshot;
+ Map<String, T> newExactValues = new LinkedHashMap<>(currentSnapshot.getExactValues());
+ Map<String, Collection<String>> newNormalizedIdentifiers = copyNormalizedIdentifiers(currentSnapshot.getNormalizedIdentifiers());
+ IdentifierCaseRule rule = databaseIdentifierContext.getRule(identifierScope);
+ if (newExactValues.containsKey(name)) {
+ removeNormalizedIdentifier(newNormalizedIdentifiers, rule, name);
+ }
+ newExactValues.put(name, value);
+ addNormalizedIdentifier(newNormalizedIdentifiers, rule, name);
+ snapshot = createSnapshot(newExactValues, newNormalizedIdentifiers);
+ }
+
+ /**
+ * Remove metadata object by actual name.
+ *
+ * @param name actual name
+ * @return removed metadata object
+ */
+ public synchronized T remove(final String name) {
+ Snapshot<T> currentSnapshot = snapshot;
+ if (!currentSnapshot.getExactValues().containsKey(name)) {
+ return null;
+ }
+ Map<String, T> newExactValues = new LinkedHashMap<>(currentSnapshot.getExactValues());
+ Map<String, Collection<String>> newNormalizedIdentifiers = copyNormalizedIdentifiers(currentSnapshot.getNormalizedIdentifiers());
+ T result = newExactValues.remove(name);
+ removeNormalizedIdentifier(newNormalizedIdentifiers, databaseIdentifierContext.getRule(identifierScope), name);
+ snapshot = createSnapshot(newExactValues, newNormalizedIdentifiers);
+ return result;
+ }
+
+ /**
+ * Judge whether identifier index is empty or not.
+ *
+ * @return identifier index is empty or not
+ */
+ public boolean isEmpty() {
+ return snapshot.getExactValues().isEmpty();
+ }
+
+ /**
+ * Get size of identifier index.
+ *
+ * @return size of identifier index
+ */
+ public int size() {
+ return snapshot.getExactValues().size();
}
/**
@@ -118,6 +188,34 @@ public final class IdentifierIndex<T> {
values.computeIfAbsent(normalizedName, key -> new LinkedList<>()).add(name);
}
+ private void removeNormalizedIdentifier(final Map<String, Collection<String>> values, final IdentifierCaseRule rule, final String name) {
+ String normalizedName = rule.normalize(name);
+ Collection<String> candidateIdentifiers = values.get(normalizedName);
+ if (null == candidateIdentifiers) {
+ return;
+ }
+ candidateIdentifiers.remove(name);
+ if (candidateIdentifiers.isEmpty()) {
+ values.remove(normalizedName);
+ }
+ }
+
+ private Map<String, Collection<String>> copyNormalizedIdentifiers(final Map<String, Collection<String>> values) {
+ Map<String, Collection<String>> result = new LinkedHashMap<>(values.size(), 1F);
+ for (Entry<String, Collection<String>> entry : values.entrySet()) {
+ result.put(entry.getKey(), new LinkedList<>(entry.getValue()));
+ }
+ return result;
+ }
+
+ private Snapshot<T> createSnapshot(final Map<String, T> exactValues, final Map<String, Collection<String>> normalizedIdentifiers) {
+ Map<String, Collection<String>> immutableNormalizedIdentifiers = new LinkedHashMap<>(normalizedIdentifiers.size(), 1F);
+ for (Entry<String, Collection<String>> entry : normalizedIdentifiers.entrySet()) {
+ immutableNormalizedIdentifiers.put(entry.getKey(), Collections.unmodifiableCollection(new LinkedList<>(entry.getValue())));
+ }
+ return new Snapshot<>(Collections.unmodifiableMap(new LinkedHashMap<>(exactValues)), Collections.unmodifiableMap(immutableNormalizedIdentifiers));
+ }
+
private static final class Snapshot<T> {
private static final Snapshot<?> EMPTY = new Snapshot<>(Collections.emptyMap(), Collections.emptyMap());
diff --git a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaDataTest.java b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaDataTest.java
index 098affe41dc..22e6b9a3cfb 100644
--- a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaDataTest.java
+++ b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/ShardingSphereMetaDataTest.java
@@ -105,6 +105,17 @@ class ShardingSphereMetaDataTest {
assertTrue(metaData.containsDatabase("foo_db"));
}
+ @SuppressWarnings("resource")
+ @Test
+ void assertGetAllDatabases() {
+ ShardingSphereRule globalRule = mock(ShardingSphereRule.class);
+ ShardingSphereDatabase database = mockDatabase(mock(ResourceMetaData.class, RETURNS_DEEP_STUBS), new MockedDataSource(), globalRule);
+ ShardingSphereMetaData metaData = new ShardingSphereMetaData(Collections.singleton(database),
+ mock(ResourceMetaData.class), new RuleMetaData(Collections.singleton(globalRule)), new ConfigurationProperties(new Properties()));
+ assertThat(metaData.getAllDatabases().size(), is(1));
+ assertTrue(metaData.getAllDatabases().contains(database));
+ }
+
@SuppressWarnings("resource")
@Test
void assertNotContainsDatabase() {
diff --git a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndexTest.java b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndexTest.java
index a4c2178f791..57b9841ce8b 100644
--- a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndexTest.java
+++ b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/identifier/IdentifierIndexTest.java
@@ -32,7 +32,11 @@ import java.util.Map;
import java.util.Optional;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
class IdentifierIndexTest {
@@ -46,6 +50,26 @@ class IdentifierIndexTest {
assertThat(actualValue, is(Optional.of("value_2")));
}
+ @Test
+ void assertGetAll() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ Map<String, String> values = new LinkedHashMap<>(2, 1F);
+ values.put("Foo", "value_1");
+ values.put("Bar", "value_2");
+ index.rebuild(values);
+ assertThat(index.getAll(), contains("value_1", "value_2"));
+ }
+
+ @Test
+ void assertGetAllNames() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ Map<String, String> values = new LinkedHashMap<>(2, 1F);
+ values.put("Foo", "value_1");
+ values.put("Bar", "value_2");
+ index.rebuild(values);
+ assertThat(index.getAllNames(), contains("Foo", "Bar"));
+ }
+
@Test
void assertFindWithExactLookup() {
IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
@@ -54,6 +78,21 @@ class IdentifierIndexTest {
assertThat(actualValue, is(Optional.of("value_1")));
}
+ @Test
+ void assertPut() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ index.put("Foo", "value_1");
+ assertThat(index.find(new IdentifierValue("\"Foo\"")), is(Optional.of("value_1")));
+ }
+
+ @Test
+ void assertPutOverridesExistingValue() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ index.put("Foo", "value_1");
+ index.put("Foo", "value_2");
+ assertThat(index.find(new IdentifierValue("\"Foo\"")), is(Optional.of("value_2")));
+ }
+
@Test
void assertFindWithNormalizedLookup() {
IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createPostgreSQLRule())), IdentifierScope.TABLE);
@@ -62,6 +101,21 @@ class IdentifierIndexTest {
assertThat(actualValue, is(Optional.of("value_1")));
}
+ @Test
+ void assertRemove() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ index.put("Foo", "value_1");
+ String actualValue = index.remove("Foo");
+ assertThat(actualValue, is("value_1"));
+ assertThat(index.find(new IdentifierValue("\"Foo\"")), is(Optional.empty()));
+ }
+
+ @Test
+ void assertRemoveReturnsNullWhenMissing() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ assertNull(index.remove("Foo"));
+ }
+
@Test
void assertFindReturnsEmpty() {
IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createPostgreSQLRule())), IdentifierScope.TABLE);
@@ -70,6 +124,22 @@ class IdentifierIndexTest {
assertThat(actualValue, is(Optional.empty()));
}
+ @Test
+ void assertIsEmpty() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ assertTrue(index.isEmpty());
+ index.put("Foo", "value_1");
+ assertFalse(index.isEmpty());
+ }
+
+ @Test
+ void assertSize() {
+ IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createExactRule())), IdentifierScope.TABLE);
+ assertThat(index.size(), is(0));
+ index.put("Foo", "value_1");
+ assertThat(index.size(), is(1));
+ }
+
@Test
void assertFindThrowsAmbiguousIdentifierException() {
IdentifierIndex<String> index = new IdentifierIndex<>(new DatabaseIdentifierContext(new IdentifierCaseRuleSet(createMySQLInsensitiveRule())), IdentifierScope.TABLE);