This is an automated email from the ASF dual-hosted git repository.

yx9o 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 c453536116f Warn when single tables exist in multiple storage units 
(#39600)
c453536116f is described below

commit c453536116f57e5739eaa90fdbacb2747308a7cc
Author: Haoran Meng <[email protected]>
AuthorDate: Wed Aug 26 08:06:17 2026 +0800

    Warn when single tables exist in multiple storage units (#39600)
---
 .../single/datanode/SingleTableDataNodeLoader.java | 19 ++++++++-
 .../datanode/SingleTableDataNodeLoaderTest.java    | 48 ++++++++++++++++++++++
 2 files changed, 65 insertions(+), 2 deletions(-)

diff --git 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
index f84d0fb1561..ae251323eb7 100644
--- 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
+++ 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
@@ -20,6 +20,7 @@ package org.apache.shardingsphere.single.datanode;
 import com.cedarsoftware.util.CaseInsensitiveSet;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import 
org.apache.shardingsphere.database.connector.core.metadata.data.loader.type.SchemaMetaDataLoader;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.database.DatabaseTypeEngine;
@@ -46,6 +47,7 @@ import java.util.stream.Collectors;
  * Single table data node loader.
  */
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Slf4j
 public final class SingleTableDataNodeLoader {
     
     /**
@@ -68,7 +70,9 @@ public final class SingleTableDataNodeLoader {
         Collection<String> splitTables = 
SingleTableLoadUtils.splitTableLines(configuredTables);
         if (splitTables.contains(SingleTableConstants.ALL_TABLES) || 
splitTables.contains(SingleTableConstants.ALL_SCHEMA_TABLES)) {
             Map<String, DatabaseType> storageTypes = 
dataSourceMap.entrySet().stream().collect(Collectors.toMap(Entry::getKey, each 
-> DatabaseTypeEngine.getStorageType(each.getValue())));
-            return load(databaseName, dataSourceMap, Collections.emptySet(), 
excludedTables, storageTypes);
+            Map<String, Collection<DataNode>> result = load(databaseName, 
dataSourceMap, Collections.emptySet(), excludedTables, storageTypes);
+            warnIfSingleTableLoadedFromMultipleDataSources(databaseName, 
result);
+            return result;
         }
         Collection<DataNode> configuredDataNodes = 
getConfiguredDataNodes(splitTables);
         Collection<String> configuredDataSources = 
getConfiguredDataSources(configuredDataNodes);
@@ -78,7 +82,9 @@ public final class SingleTableDataNodeLoader {
         Map<String, DatabaseType> validStorageTypes = 
validDataSources.entrySet().stream().collect(Collectors.toMap(Entry::getKey, 
each -> DatabaseTypeEngine.getStorageType(each.getValue())));
         Map<String, Collection<DataNode>> actualDataNodes = load(databaseName, 
validDataSources, includedTables, excludedTables, validStorageTypes);
         Map<String, Map<String, Collection<String>>> configuredTableMap = 
getConfiguredTableMap(databaseName, protocolType, splitTables, 
validStorageTypes);
-        return loadSpecifiedDataNodes(actualDataNodes, 
featureRequiredSingleTables, configuredTableMap);
+        Map<String, Collection<DataNode>> result = 
loadSpecifiedDataNodes(actualDataNodes, featureRequiredSingleTables, 
configuredTableMap);
+        warnIfSingleTableLoadedFromMultipleDataSources(databaseName, result);
+        return result;
     }
     
     /**
@@ -121,6 +127,15 @@ public final class SingleTableDataNodeLoader {
         return result;
     }
     
+    private static void warnIfSingleTableLoadedFromMultipleDataSources(final 
String databaseName, final Map<String, Collection<DataNode>> dataNodes) {
+        for (Entry<String, Collection<DataNode>> entry : dataNodes.entrySet()) 
{
+            Collection<String> dataSourceNames = 
entry.getValue().stream().map(DataNode::getDataSourceName).collect(Collectors.toCollection(LinkedHashSet::new));
+            if (1 < dataSourceNames.size()) {
+                log.warn("Single table '{}' is loaded from multiple storage 
units {} in database '{}'.", entry.getKey(), dataSourceNames, databaseName);
+            }
+        }
+    }
+    
     private static Collection<DataNode> getConfiguredDataNodes(final 
Collection<String> splitTables) {
         return 
splitTables.stream().map(DataNode::new).collect(Collectors.toList());
     }
diff --git 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
index 1b0966aea63..cad0aad5812 100644
--- 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
+++ 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.shardingsphere.single.datanode;
 
+import ch.qos.logback.classic.Level;
+import 
org.apache.shardingsphere.database.connector.core.metadata.data.loader.type.SchemaMetaDataLoader;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.database.DatabaseTypeEngine;
 import org.apache.shardingsphere.infra.datanode.DataNode;
@@ -25,12 +27,16 @@ import 
org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
 import 
org.apache.shardingsphere.infra.rule.attribute.table.TableMapperRuleAttribute;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.single.exception.SingleTablesLoadingException;
+import 
org.apache.shardingsphere.test.infra.framework.extension.log.LogCaptureAssertion;
+import 
org.apache.shardingsphere.test.infra.framework.extension.log.LogCaptureExtension;
 import org.apache.shardingsphere.test.infra.fixture.jdbc.MockedDataSource;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.MockedConstruction;
 import org.mockito.MockedStatic;
 
 import javax.sql.DataSource;
@@ -54,11 +60,13 @@ import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+@ExtendWith(LogCaptureExtension.class)
 class SingleTableDataNodeLoaderTest {
     
     private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
@@ -169,6 +177,46 @@ class SingleTableDataNodeLoaderTest {
         assertTrue(SingleTableDataNodeLoader.load("foo_db", databaseType, 
dataSourceMap, Collections.emptyList(), configuredTables).isEmpty());
     }
     
+    @Test
+    void assertLoadWithSameTableInDifferentDataSources(final 
LogCaptureAssertion logCaptureAssertion) throws SQLException {
+        Map<String, DataSource> localDataSourceMap = new LinkedHashMap<>(2, 
1F);
+        localDataSourceMap.put("foo_ds", mockDataSource("foo_ds", 
Collections.singletonList("same_tbl")));
+        localDataSourceMap.put("bar_ds", mockDataSource("bar_ds", 
Collections.singletonList("same_tbl")));
+        Map<String, Collection<DataNode>> actual = 
SingleTableDataNodeLoader.load(
+                "foo_db", databaseType, localDataSourceMap, 
Collections.emptyList(), Arrays.asList("foo_ds.same_tbl", "bar_ds.same_tbl"));
+        assertThat(actual.get("same_tbl").size(), is(2));
+        logCaptureAssertion.assertLogCount(1);
+        logCaptureAssertion.assertLogContent(0, Level.WARN,
+                "Single table 'same_tbl' is loaded from multiple storage units 
[foo_ds, bar_ds] in database 'foo_db'.", true);
+    }
+    
+    @Test
+    void assertLoadWithSameTableInDifferentSchemasOfSameDataSource(final 
LogCaptureAssertion logCaptureAssertion) throws SQLException {
+        Map<String, DataSource> localDataSourceMap = 
Collections.singletonMap("foo_ds", dataSourceMap.get("foo_ds"));
+        Map<String, Collection<String>> schemaTableNames = new 
LinkedHashMap<>(2, 1F);
+        schemaTableNames.put("foo_schema", Collections.singleton("same_tbl"));
+        schemaTableNames.put("bar_schema", Collections.singleton("same_tbl"));
+        try (
+                MockedConstruction<SchemaMetaDataLoader> ignored = 
mockConstruction(SchemaMetaDataLoader.class,
+                        (mock, context) -> 
when(mock.loadSchemaTableNames("foo_db", localDataSourceMap.get("foo_ds"), 
Collections.emptySet(), Collections.emptySet())).thenReturn(schemaTableNames))) 
{
+            Map<String, Collection<DataNode>> actual = 
SingleTableDataNodeLoader.load(
+                    "foo_db", databaseType, localDataSourceMap, 
Collections.emptyList(), Collections.singleton("*.*.*"));
+            assertThat(actual.get("same_tbl").size(), is(2));
+            logCaptureAssertion.assertLogCount(0);
+        }
+    }
+    
+    @Test
+    void assertLoadWithDataSourceMapDoesNotWarn(final LogCaptureAssertion 
logCaptureAssertion) throws SQLException {
+        Map<String, DataSource> localDataSourceMap = new LinkedHashMap<>(2, 
1F);
+        localDataSourceMap.put("foo_ds", mockDataSource("foo_ds", 
Collections.singletonList("same_tbl")));
+        localDataSourceMap.put("bar_ds", mockDataSource("bar_ds", 
Collections.singletonList("same_tbl")));
+        Map<String, Collection<DataNode>> actual = 
SingleTableDataNodeLoader.load(
+                "foo_db", localDataSourceMap, Collections.emptySet(), 
Collections.emptySet(), createStorageTypes(localDataSourceMap));
+        assertThat(actual.get("same_tbl").size(), is(2));
+        logCaptureAssertion.assertLogCount(0);
+    }
+    
     @Test
     void assertLoadWithEmptyConfiguredTablesAndFeatureRequiredSingleTables() {
         assertTrue(SingleTableDataNodeLoader.load(

Reply via email to