You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by tu...@apache.org on 2023/02/13 03:30:55 UTC

[shardingsphere] branch master updated: Refactor check primary instance logic for MySQLNormalReplicationDatabaseDiscoveryProvider (#24123)

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

tuichenchuxin 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 35dd65883c4 Refactor check primary instance logic for MySQLNormalReplicationDatabaseDiscoveryProvider (#24123)
35dd65883c4 is described below

commit 35dd65883c486fd7de9cc8859dcd8bfa2d73dcd3
Author: zhaojinchao <zh...@apache.org>
AuthorDate: Mon Feb 13 11:30:47 2023 +0800

    Refactor check primary instance logic for MySQLNormalReplicationDatabaseDiscoveryProvider (#24123)
    
    * Fix check primary instance logic
    
    * Refactor logic and add PrimaryDataSourceNotFoundException
    
    * Add some unit test.
    
    * Adjustment serialVersionUID
    
    * Fix
    
    * Add document
---
 .../user-manual/error-code/sql-error-code.cn.md    |  1 +
 .../user-manual/error-code/sql-error-code.en.md    |  1 +
 .../PrimaryDataSourceNotFoundException.java        | 33 ++++++++++++++++++++++
 ...NormalReplicationDatabaseDiscoveryProvider.java | 20 +++++++------
 ...alReplicationDatabaseDiscoveryProviderTest.java | 24 ++++++++++++----
 5 files changed, 66 insertions(+), 13 deletions(-)

diff --git a/docs/document/content/user-manual/error-code/sql-error-code.cn.md b/docs/document/content/user-manual/error-code/sql-error-code.cn.md
index dfc190e1c07..0110649ab19 100644
--- a/docs/document/content/user-manual/error-code/sql-error-code.cn.md
+++ b/docs/document/content/user-manual/error-code/sql-error-code.cn.md
@@ -234,6 +234,7 @@ SQL 错误码以标准的 SQL State,Vendor Code 和详细错误信息提供,
 | 44000     | 20382       | \`%s\` is not in MGR replication group member in database \`%s\`. |
 | 44000     | 20383       | Group name in MGR is not same with configured one \`%s\` in database \`%s\`. |
 | 42S01     | 20390       | MySQL Duplicate primary data source in database \`%s\`. |
+| 42S02     | 20391       | Primary data source not found in database \`%s\`. |
 
 ### SQL 方言转换
 
diff --git a/docs/document/content/user-manual/error-code/sql-error-code.en.md b/docs/document/content/user-manual/error-code/sql-error-code.en.md
index 75eef7e1e2b..24968d21070 100644
--- a/docs/document/content/user-manual/error-code/sql-error-code.en.md
+++ b/docs/document/content/user-manual/error-code/sql-error-code.en.md
@@ -233,6 +233,7 @@ SQL error codes provide by standard `SQL State`, `Vendor Code` and `Reason`, whi
 | 44000     | 20382       | \`%s\` is not in MGR replication group member in database \`%s\`. |
 | 44000     | 20383       | Group name in MGR is not same with configured one \`%s\` in database \`%s\`. |
 | 42S01     | 20390       | MySQL Duplicate primary data source in database \`%s\`. |
+| 42S02     | 20391       | Primary data source not found in database \`%s\`. |
 
 ### SQL Dialect Translator
 
diff --git a/features/db-discovery/provider/mysql/src/main/java/org/apache/shardingsphere/dbdiscovery/mysql/exception/replica/PrimaryDataSourceNotFoundException.java b/features/db-discovery/provider/mysql/src/main/java/org/apache/shardingsphere/dbdiscovery/mysql/exception/replica/PrimaryDataSourceNotFoundException.java
new file mode 100644
index 00000000000..69f363d505c
--- /dev/null
+++ b/features/db-discovery/provider/mysql/src/main/java/org/apache/shardingsphere/dbdiscovery/mysql/exception/replica/PrimaryDataSourceNotFoundException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.dbdiscovery.mysql.exception.replica;
+
+import org.apache.shardingsphere.dbdiscovery.exception.DBDiscoveryProviderException;
+import org.apache.shardingsphere.infra.util.exception.external.sql.sqlstate.XOpenSQLState;
+
+/**
+ * Primary data source not found exception.
+ */
+public final class PrimaryDataSourceNotFoundException extends DBDiscoveryProviderException {
+    
+    private static final long serialVersionUID = -4646464806520242027L;
+    
+    public PrimaryDataSourceNotFoundException(final String databaseName) {
+        super(XOpenSQLState.NOT_FOUND, 91, "Primary data source not found in database `%s`.", databaseName);
+    }
+}
diff --git a/features/db-discovery/provider/mysql/src/main/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProvider.java b/features/db-discovery/provider/mysql/src/main/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProvider.java
index def5436aa33..252c606e34d 100644
--- a/features/db-discovery/provider/mysql/src/main/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProvider.java
+++ b/features/db-discovery/provider/mysql/src/main/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProvider.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.dbdiscovery.mysql.type;
 
 import lombok.SneakyThrows;
 import org.apache.shardingsphere.dbdiscovery.mysql.exception.replica.DuplicatePrimaryDataSourceException;
+import org.apache.shardingsphere.dbdiscovery.mysql.exception.replica.PrimaryDataSourceNotFoundException;
 import org.apache.shardingsphere.dbdiscovery.spi.DatabaseDiscoveryProvider;
 import org.apache.shardingsphere.dbdiscovery.spi.ReplicaDataSourceStatus;
 import org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine;
@@ -34,7 +35,6 @@ import java.util.LinkedList;
 import java.util.Optional;
 import java.util.Properties;
 import java.util.TreeSet;
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
@@ -65,17 +65,14 @@ public final class MySQLNormalReplicationDatabaseDiscoveryProvider implements Da
     public void checkEnvironment(final String databaseName, final Collection<DataSource> dataSources) {
         ExecutorService executorService = ExecutorEngine.createExecutorEngineWithCPUAndResources(dataSources.size()).getExecutorServiceManager().getExecutorService();
         Collection<Future<Boolean>> futures = new LinkedList<>();
-        Set<Boolean> primaryInstances = new TreeSet<>();
+        Collection<Boolean> primaryInstances = new TreeSet<>();
         for (DataSource each : dataSources) {
-            futures.add(executorService.submit(() -> queryPrimaryInstance(each, primaryInstances)));
+            futures.add(executorService.submit(() -> isPrimaryInstance(each)));
         }
         for (Future<Boolean> each : futures) {
-            ShardingSpherePreconditions.checkState(each.get(), () -> new DuplicatePrimaryDataSourceException(databaseName));
+            checkPrimaryInstances(databaseName, each.get(), primaryInstances);
         }
-    }
-    
-    private synchronized boolean queryPrimaryInstance(final DataSource dataSource, final Collection<Boolean> primaryInstances) throws SQLException {
-        return isPrimaryInstance(dataSource) && primaryInstances.add(Boolean.TRUE);
+        ShardingSpherePreconditions.checkState(!primaryInstances.isEmpty(), () -> new PrimaryDataSourceNotFoundException(databaseName));
     }
     
     @Override
@@ -110,6 +107,13 @@ public final class MySQLNormalReplicationDatabaseDiscoveryProvider implements Da
         }
     }
     
+    private void checkPrimaryInstances(final String databaseName, final boolean isPrimaryInstance, final Collection<Boolean> primaryInstances) {
+        if (!isPrimaryInstance) {
+            return;
+        }
+        ShardingSpherePreconditions.checkState(primaryInstances.add(Boolean.TRUE), () -> new DuplicatePrimaryDataSourceException(databaseName));
+    }
+    
     @Override
     public ReplicaDataSourceStatus loadReplicaStatus(final DataSource replicaDataSource) throws SQLException {
         try (
diff --git a/features/db-discovery/provider/mysql/src/test/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProviderTest.java b/features/db-discovery/provider/mysql/src/test/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProviderTest.java
index f134b33b7d1..c9ad624d002 100644
--- a/features/db-discovery/provider/mysql/src/test/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProviderTest.java
+++ b/features/db-discovery/provider/mysql/src/test/java/org/apache/shardingsphere/dbdiscovery/mysql/type/MySQLNormalReplicationDatabaseDiscoveryProviderTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.shardingsphere.dbdiscovery.mysql.type;
 
+import org.apache.shardingsphere.dbdiscovery.mysql.exception.replica.DuplicatePrimaryDataSourceException;
+import org.apache.shardingsphere.dbdiscovery.mysql.exception.replica.PrimaryDataSourceNotFoundException;
 import org.apache.shardingsphere.dbdiscovery.spi.DatabaseDiscoveryProvider;
 import org.apache.shardingsphere.dbdiscovery.spi.ReplicaDataSourceStatus;
 import org.apache.shardingsphere.test.fixture.jdbc.MockedDataSource;
@@ -28,6 +30,7 @@ import javax.sql.DataSource;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.Collections;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -39,24 +42,35 @@ import static org.mockito.Mockito.when;
 
 public final class MySQLNormalReplicationDatabaseDiscoveryProviderTest {
     
+    @Test(expected = PrimaryDataSourceNotFoundException.class)
+    public void assertCheckEnvironmentNoPrimaryDataSource() throws SQLException {
+        new MySQLNormalReplicationDatabaseDiscoveryProvider().checkEnvironment("foo_db", Collections.singleton(mockDataSourceForReplicationInstances("ON")));
+    }
+    
     @Test
-    public void assertCheckEnvironment() throws SQLException {
-        new MySQLNormalReplicationDatabaseDiscoveryProvider().checkEnvironment("foo_db", Collections.singleton(mockDataSourceForReplicationInstances()));
+    public void assertCheckEnvironmentHasSinglePrimaryDataSource() throws SQLException {
+        new MySQLNormalReplicationDatabaseDiscoveryProvider().checkEnvironment("foo_db", Collections.singleton(mockDataSourceForReplicationInstances("OFF")));
+    }
+    
+    @Test(expected = DuplicatePrimaryDataSourceException.class)
+    public void assertCheckEnvironmentHasManyPrimaryDataSources() throws SQLException {
+        new MySQLNormalReplicationDatabaseDiscoveryProvider().checkEnvironment("foo_db", Arrays.asList(mockDataSourceForReplicationInstances("OFF"),
+                mockDataSourceForReplicationInstances("OFF")));
     }
     
     @Test
     public void assertIsPrimaryInstance() throws SQLException {
-        assertTrue(new MySQLNormalReplicationDatabaseDiscoveryProvider().isPrimaryInstance(mockDataSourceForReplicationInstances()));
+        assertTrue(new MySQLNormalReplicationDatabaseDiscoveryProvider().isPrimaryInstance(mockDataSourceForReplicationInstances("OFF")));
     }
     
-    private DataSource mockDataSourceForReplicationInstances() throws SQLException {
+    private DataSource mockDataSourceForReplicationInstances(final String readOnly) throws SQLException {
         ResultSet slaveHostsResultSet = mock(ResultSet.class);
         when(slaveHostsResultSet.next()).thenReturn(true, false);
         when(slaveHostsResultSet.getString("Host")).thenReturn("127.0.0.1");
         when(slaveHostsResultSet.getString("Port")).thenReturn("3306");
         ResultSet readonlyResultSet = mock(ResultSet.class);
         when(readonlyResultSet.next()).thenReturn(true, false);
-        when(readonlyResultSet.getString("Value")).thenReturn("OFF");
+        when(readonlyResultSet.getString("Value")).thenReturn(readOnly);
         Connection connection = mock(Connection.class, RETURNS_DEEP_STUBS);
         when(connection.createStatement().executeQuery("SHOW SLAVE HOSTS")).thenReturn(slaveHostsResultSet);
         when(connection.getMetaData().getURL()).thenReturn("jdbc:mysql://127.0.0.1:3306/foo_ds");