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);