You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/10/04 03:48:26 UTC

(shardingsphere) branch master updated: Move cluster lock strategy SPI to cluster mode core (#40043)

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

terrymanu 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 8cc96df968a Move cluster lock strategy SPI to cluster mode core (#40043)
8cc96df968a is described below

commit 8cc96df968ae0c3b2efb66facc4be90f23ebc3e1
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Sun Oct 4 11:48:18 2026 +0800

    Move cluster lock strategy SPI to cluster mode core (#40043)
    
    * Move cluster lock strategy SPI to cluster mode core
    
    * Validate sharding table schemas with Bean Validation
---
 features/sharding/api/pom.xml                      |  5 ++
 .../rule/ShardingTableRuleConfiguration.java       |  3 +
 .../ShardingTableRuleConfigurationValidator.java   | 54 +++++++++++++
 .../ValidShardingTableRuleConfiguration.java       | 57 ++++++++++++++
 .../api/config/ShardingRuleConfigurationTest.java  | 70 +++++++++++++++++
 ...hardingTableRuleConfigurationValidatorTest.java | 91 ++++++++++++++++++++++
 .../config/ShardingRuleConfigurationChecker.java   | 27 -------
 .../ShardingRuleConfigurationCheckerTest.java      | 40 ----------
 .../builder/database/DatabaseRulesBuilder.java     |  5 +-
 .../builder/database/DatabaseRulesBuilderTest.java | 30 +++++++
 .../ral/updatable/lock/LockClusterExecutor.java    |  2 +-
 .../cluster}/lock/spi/ClusterLockStrategy.java     |  2 +-
 .../lock/impl/ClusterReadWriteLockStrategy.java    |  2 +-
 .../lock/impl/ClusterWriteLockStrategy.java        |  2 +-
 ...e.manager.cluster.lock.spi.ClusterLockStrategy} |  0
 .../impl/ClusterReadWriteLockStrategyTest.java     |  2 +-
 .../lock/impl/ClusterWriteLockStrategyTest.java    |  2 +-
 17 files changed, 319 insertions(+), 75 deletions(-)

diff --git a/features/sharding/api/pom.xml b/features/sharding/api/pom.xml
index ef383e634de..38ff9148864 100644
--- a/features/sharding/api/pom.xml
+++ b/features/sharding/api/pom.xml
@@ -37,6 +37,11 @@
             <artifactId>shardingsphere-infra-binder-core</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.shardingsphere</groupId>
+            <artifactId>shardingsphere-infra-expr-entry</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         
         <dependency>
             <groupId>jakarta.validation</groupId>
diff --git a/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/rule/ShardingTableRuleConfiguration.java b/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/rule/ShardingTableRuleConfiguration.java
index ce391c705f1..7e55536ee9c 100644
--- a/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/rule/ShardingTableRuleConfiguration.java
+++ b/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/rule/ShardingTableRuleConfiguration.java
@@ -20,8 +20,10 @@ package org.apache.shardingsphere.sharding.api.config.rule;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.Setter;
+import org.apache.shardingsphere.infra.config.rule.validator.group.RuleConfigurationTypeValidationGroup;
 import org.apache.shardingsphere.sharding.api.config.strategy.audit.ShardingAuditStrategyConfiguration;
 import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ShardingStrategyConfiguration;
+import org.apache.shardingsphere.sharding.api.config.validator.ValidShardingTableRuleConfiguration;
 
 import javax.validation.Valid;
 import javax.validation.constraints.NotBlank;
@@ -29,6 +31,7 @@ import javax.validation.constraints.NotBlank;
 /**
  * Sharding table rule configuration.
  */
+@ValidShardingTableRuleConfiguration(groups = RuleConfigurationTypeValidationGroup.class)
 @RequiredArgsConstructor
 @Getter
 @Setter
diff --git a/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/validator/ShardingTableRuleConfigurationValidator.java b/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/validator/ShardingTableRuleConfigurationValidator.java
new file mode 100644
index 00000000000..12740d73ab7
--- /dev/null
+++ b/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/validator/ShardingTableRuleConfigurationValidator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.sharding.api.config.validator;
+
+import org.apache.shardingsphere.infra.datanode.DataNode;
+import org.apache.shardingsphere.infra.expr.entry.InlineExpressionParserFactory;
+import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Sharding table rule configuration validator.
+ */
+public final class ShardingTableRuleConfigurationValidator implements ConstraintValidator<ValidShardingTableRuleConfiguration, ShardingTableRuleConfiguration> {
+    
+    @Override
+    public boolean isValid(final ShardingTableRuleConfiguration value, final ConstraintValidatorContext context) {
+        Collection<String> actualDataNodes = InlineExpressionParserFactory.newInstance(value.getActualDataNodes()).splitAndEvaluate();
+        Map<String, String> schemaNamesByDataSource = new HashMap<>(actualDataNodes.size(), 1F);
+        for (String each : actualDataNodes) {
+            DataNode dataNode = new DataNode(each);
+            if (null == dataNode.getSchemaName()) {
+                continue;
+            }
+            String configuredSchemaName = schemaNamesByDataSource.putIfAbsent(dataNode.getDataSourceName(), dataNode.getSchemaName());
+            if (null != configuredSchemaName && !configuredSchemaName.equalsIgnoreCase(dataNode.getSchemaName())) {
+                context.disableDefaultConstraintViolation();
+                context.buildConstraintViolationWithTemplate(String.format("contains multiple schemas for storage unit `%s` in sharding table `%s`",
+                        dataNode.getDataSourceName(), value.getLogicTable())).addPropertyNode("actualDataNodes").addConstraintViolation();
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/validator/ValidShardingTableRuleConfiguration.java b/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/validator/ValidShardingTableRuleConfiguration.java
new file mode 100644
index 00000000000..578e18cf8bd
--- /dev/null
+++ b/features/sharding/api/src/main/java/org/apache/shardingsphere/sharding/api/config/validator/ValidShardingTableRuleConfiguration.java
@@ -0,0 +1,57 @@
+/*
+ * 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.sharding.api.config.validator;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Valid sharding table rule configuration constraint.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = ShardingTableRuleConfigurationValidator.class)
+@Documented
+public @interface ValidShardingTableRuleConfiguration {
+    
+    /**
+     * Get message.
+     *
+     * @return message
+     */
+    String message() default "contains multiple schemas for a storage unit";
+    
+    /**
+     * Get groups.
+     *
+     * @return groups
+     */
+    Class<?>[] groups() default {};
+    
+    /**
+     * Get payload.
+     *
+     * @return payload
+     */
+    Class<? extends Payload>[] payload() default {};
+}
diff --git a/features/sharding/api/src/test/java/org/apache/shardingsphere/sharding/api/config/ShardingRuleConfigurationTest.java b/features/sharding/api/src/test/java/org/apache/shardingsphere/sharding/api/config/ShardingRuleConfigurationTest.java
index cf3164d64d5..78f144f9b7a 100644
--- a/features/sharding/api/src/test/java/org/apache/shardingsphere/sharding/api/config/ShardingRuleConfigurationTest.java
+++ b/features/sharding/api/src/test/java/org/apache/shardingsphere/sharding/api/config/ShardingRuleConfigurationTest.java
@@ -22,7 +22,9 @@ import org.apache.shardingsphere.infra.config.keygen.KeyGenerateStrategiesConfig
 import org.apache.shardingsphere.infra.config.keygen.impl.ColumnKeyGenerateStrategiesRuleConfiguration;
 import org.apache.shardingsphere.infra.config.keygen.impl.SequenceKeyGenerateStrategiesRuleConfiguration;
 import org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
+import org.apache.shardingsphere.infra.exception.kernel.metadata.datanode.InvalidDataNodeFormatException;
 import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
+import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.RuleConfigurationValidationException;
 import org.apache.shardingsphere.sharding.api.config.rule.ShardingAutoTableRuleConfiguration;
 import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableReferenceRuleConfiguration;
 import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
@@ -36,6 +38,7 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 
+import javax.validation.ValidationException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -44,12 +47,79 @@ import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class ShardingRuleConfigurationTest {
     
+    @Test
+    void assertValidateBlankLogicTableBeforeDataNodes() {
+        ShardingRuleConfiguration ruleConfig = createRuleConfiguration(new ShardingTableRuleConfiguration("", "malformed"));
+        InvalidRuleConfigurationException actual = assertThrows(InvalidRuleConfigurationException.class, () -> RuleConfigurationValidator.validate(ruleConfig));
+        assertThat(actual.getMessage(), is("Invalid 'ShardingRuleConfiguration' rule, error message is: Property `tables[0].logicTable` must not be blank."));
+    }
+    
+    @Test
+    void assertValidateRuleItemWithBlankLogicTableBeforeDataNodes() {
+        ShardingTableRuleConfiguration tableConfig = new ShardingTableRuleConfiguration("", "malformed");
+        InvalidRuleConfigurationException actual = assertThrows(InvalidRuleConfigurationException.class,
+                () -> RuleConfigurationValidator.validateRuleItem(new ShardingRuleConfiguration(), tableConfig));
+        assertThat(actual.getMessage(), is("Invalid 'ShardingRuleConfiguration' rule, error message is: Property `logicTable` must not be blank."));
+    }
+    
+    @Test
+    void assertValidateMultipleSchemasForSameDataSource() {
+        ShardingRuleConfiguration ruleConfig = createRuleConfiguration(new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema_${0..1}.foo_tbl_${0..1}"));
+        InvalidRuleConfigurationException actual = assertThrows(InvalidRuleConfigurationException.class, () -> RuleConfigurationValidator.validate(ruleConfig));
+        assertThat(actual.getMessage(), is("Invalid 'ShardingRuleConfiguration' rule, error message is: "
+                + "Property `tables[0].actualDataNodes` contains multiple schemas for storage unit `ds_0` in sharding table `foo_tbl`."));
+    }
+    
+    @Test
+    void assertValidateRuleItemWithMultipleSchemasForSameDataSource() {
+        ShardingRuleConfiguration ruleConfig = new ShardingRuleConfiguration();
+        ShardingTableRuleConfiguration tableConfig = new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema_${0..1}.foo_tbl_${0..1}");
+        InvalidRuleConfigurationException actual = assertThrows(InvalidRuleConfigurationException.class, () -> RuleConfigurationValidator.validateRuleItem(ruleConfig, tableConfig));
+        assertThat(actual.getMessage(), is("Invalid 'ShardingRuleConfiguration' rule, error message is: "
+                + "Property `actualDataNodes` contains multiple schemas for storage unit `ds_0` in sharding table `foo_tbl`."));
+        assertTrue(ruleConfig.getTables().isEmpty());
+    }
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("validSchemaDataNodesArguments")
+    void assertValidateValidSchemaDataNodes(final String name, final ShardingTableRuleConfiguration tableConfig) {
+        assertDoesNotThrow(() -> RuleConfigurationValidator.validate(createRuleConfiguration(tableConfig)));
+        assertDoesNotThrow(() -> RuleConfigurationValidator.validateRuleItem(new ShardingRuleConfiguration(), tableConfig));
+    }
+    
+    private static Stream<Arguments> validSchemaDataNodesArguments() {
+        return Stream.of(
+                Arguments.of("null data nodes", new ShardingTableRuleConfiguration("foo_tbl", null)),
+                Arguments.of("empty data nodes", new ShardingTableRuleConfiguration("foo_tbl", "")),
+                Arguments.of("inline data nodes with consistent schemas", new ShardingTableRuleConfiguration("foo_tbl", "ds_${0..1}.foo_schema.foo_tbl_${0..1}")));
+    }
+    
+    @Test
+    void assertValidateDifferentSchemasForDifferentLogicTables() {
+        ShardingRuleConfiguration ruleConfig = new ShardingRuleConfiguration();
+        ruleConfig.getTables().add(new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema.foo_tbl_0"));
+        ruleConfig.getTables().add(new ShardingTableRuleConfiguration("bar_tbl", "ds_0.bar_schema.bar_tbl_0"));
+        assertDoesNotThrow(() -> RuleConfigurationValidator.validate(ruleConfig));
+    }
+    
+    @Test
+    void assertValidateMalformedDataNodes() {
+        ShardingRuleConfiguration ruleConfig = createRuleConfiguration(new ShardingTableRuleConfiguration("foo_tbl", "malformed"));
+        RuleConfigurationValidationException actual = assertThrows(RuleConfigurationValidationException.class, () -> RuleConfigurationValidator.validate(ruleConfig));
+        assertThat(actual.getMessage(), is("Can not validate 'ShardingRuleConfiguration' rule configuration.\n"
+                + "More details: javax.validation.ValidationException: org.apache.shardingsphere.infra.exception.kernel.metadata.datanode.InvalidDataNodeFormatException: "
+                + "Invalid format for actual data node 'malformed'."));
+        assertThat(actual.getCause(), isA(ValidationException.class));
+        assertThat(actual.getCause().getCause(), isA(InvalidDataNodeFormatException.class));
+    }
+    
     @Test
     void assertGetLogicTableNames() {
         ShardingRuleConfiguration ruleConfig = new ShardingRuleConfiguration();
diff --git a/features/sharding/api/src/test/java/org/apache/shardingsphere/sharding/api/config/validator/ShardingTableRuleConfigurationValidatorTest.java b/features/sharding/api/src/test/java/org/apache/shardingsphere/sharding/api/config/validator/ShardingTableRuleConfigurationValidatorTest.java
new file mode 100644
index 00000000000..ecdd2a71668
--- /dev/null
+++ b/features/sharding/api/src/test/java/org/apache/shardingsphere/sharding/api/config/validator/ShardingTableRuleConfigurationValidatorTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.sharding.api.config.validator;
+
+import org.apache.shardingsphere.infra.expr.entry.InlineExpressionParserFactory;
+import org.apache.shardingsphere.infra.expr.spi.InlineExpressionParser;
+import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
+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.Answers;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import javax.validation.ConstraintValidatorContext;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class ShardingTableRuleConfigurationValidatorTest {
+    
+    private final ShardingTableRuleConfigurationValidator validator = new ShardingTableRuleConfigurationValidator();
+    
+    @Mock
+    private InlineExpressionParser parser;
+    
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ConstraintValidatorContext context;
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("validDataNodesArguments")
+    void assertValidDataNodes(final String name, final List<String> actualDataNodes) {
+        try (MockedStatic<InlineExpressionParserFactory> mockedFactory = mockStatic(InlineExpressionParserFactory.class)) {
+            mockedFactory.when(() -> InlineExpressionParserFactory.newInstance("data_nodes")).thenReturn(parser);
+            when(parser.splitAndEvaluate()).thenReturn(actualDataNodes);
+            assertTrue(validator.isValid(new ShardingTableRuleConfiguration("foo_tbl", "data_nodes"), context));
+            verifyNoInteractions(context);
+        }
+    }
+    
+    private static Stream<Arguments> validDataNodesArguments() {
+        return Stream.of(
+                Arguments.of("no data nodes", Collections.emptyList()),
+                Arguments.of("omitted schema", Collections.singletonList("ds_0.foo_tbl_0")),
+                Arguments.of("same schema for same data source", Arrays.asList("ds_0.foo_schema.foo_tbl_0", "ds_0.foo_schema.foo_tbl_1")),
+                Arguments.of("same schema in different cases", Arrays.asList("ds_0.foo_schema.foo_tbl_0", "ds_0.FOO_SCHEMA.foo_tbl_1")),
+                Arguments.of("omitted schema before explicit schema", Arrays.asList("ds_0.foo_tbl_0", "ds_0.foo_schema.foo_tbl_1")),
+                Arguments.of("omitted schema after explicit schema", Arrays.asList("ds_0.foo_schema.foo_tbl_0", "ds_0.foo_tbl_1")),
+                Arguments.of("different schemas for different data sources", Arrays.asList("ds_0.foo_schema.foo_tbl_0", "ds_1.bar_schema.foo_tbl_1")),
+                Arguments.of("data source names remain case sensitive", Arrays.asList("ds_0.foo_schema.foo_tbl_0", "DS_0.bar_schema.foo_tbl_1")));
+    }
+    
+    @Test
+    void assertInvalidDataNodes() {
+        try (MockedStatic<InlineExpressionParserFactory> mockedFactory = mockStatic(InlineExpressionParserFactory.class)) {
+            mockedFactory.when(() -> InlineExpressionParserFactory.newInstance("data_nodes")).thenReturn(parser);
+            when(parser.splitAndEvaluate()).thenReturn(Arrays.asList("ds_0.foo_schema.foo_tbl_0", "ds_0.bar_schema.foo_tbl_1"));
+            assertFalse(validator.isValid(new ShardingTableRuleConfiguration("foo_tbl", "data_nodes"), context));
+            verify(context).disableDefaultConstraintViolation();
+            verify(context.buildConstraintViolationWithTemplate("contains multiple schemas for storage unit `ds_0` in sharding table `foo_tbl`")
+                    .addPropertyNode("actualDataNodes")).addConstraintViolation();
+        }
+    }
+}
diff --git a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationChecker.java b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationChecker.java
index bb53d75f9c6..71ce04f15bd 100644
--- a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationChecker.java
+++ b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationChecker.java
@@ -19,22 +19,15 @@ package org.apache.shardingsphere.sharding.checker.config;
 
 import org.apache.shardingsphere.infra.config.rule.checker.DatabaseRuleConfigurationChecker;
 import org.apache.shardingsphere.infra.datanode.DataNode;
-import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
-import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
 import org.apache.shardingsphere.infra.expr.entry.InlineExpressionParserFactory;
-import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
 import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
 import org.apache.shardingsphere.sharding.api.config.rule.ShardingAutoTableRuleConfiguration;
 import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
 import org.apache.shardingsphere.sharding.constant.ShardingOrder;
 
-import javax.sql.DataSource;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
-import java.util.Map;
 import java.util.stream.Collectors;
 
 /**
@@ -42,26 +35,6 @@ import java.util.stream.Collectors;
  */
 public final class ShardingRuleConfigurationChecker implements DatabaseRuleConfigurationChecker<ShardingRuleConfiguration> {
     
-    @Override
-    public void check(final String databaseName, final ShardingRuleConfiguration ruleConfig, final Map<String, DataSource> dataSourceMap, final Collection<ShardingSphereRule> builtRules) {
-        ruleConfig.getTables().forEach(this::checkDataNodeSchemas);
-    }
-    
-    private void checkDataNodeSchemas(final ShardingTableRuleConfiguration tableRuleConfig) {
-        Collection<String> actualDataNodes = InlineExpressionParserFactory.newInstance(tableRuleConfig.getActualDataNodes()).splitAndEvaluate();
-        Map<String, String> schemaNamesByDataSource = new HashMap<>(actualDataNodes.size(), 1F);
-        for (String each : actualDataNodes) {
-            DataNode dataNode = new DataNode(each);
-            if (null == dataNode.getSchemaName()) {
-                continue;
-            }
-            String configuredSchemaName = schemaNamesByDataSource.putIfAbsent(dataNode.getDataSourceName(), dataNode.getSchemaName());
-            ShardingSpherePreconditions.checkState(null == configuredSchemaName || configuredSchemaName.equalsIgnoreCase(dataNode.getSchemaName()),
-                    () -> new InvalidRuleConfigurationException("sharding table", Collections.singleton(tableRuleConfig.getLogicTable()),
-                            Collections.singleton(String.format("Multiple schemas are configured for storage unit '%s'.", dataNode.getDataSourceName()))));
-        }
-    }
-    
     @Override
     public Collection<String> getRequiredDataSourceNames(final ShardingRuleConfiguration ruleConfig) {
         Collection<String> result = new LinkedHashSet<>();
diff --git a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationCheckerTest.java b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationCheckerTest.java
index 5c01f007431..cde76e0929b 100644
--- a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationCheckerTest.java
+++ b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/checker/config/ShardingRuleConfigurationCheckerTest.java
@@ -18,27 +18,19 @@
 package org.apache.shardingsphere.sharding.checker.config;
 
 import org.apache.shardingsphere.infra.config.rule.checker.DatabaseRuleConfigurationChecker;
-import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
 import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPILoader;
 import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
 import org.apache.shardingsphere.sharding.api.config.rule.ShardingAutoTableRuleConfiguration;
 import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashSet;
-import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 class ShardingRuleConfigurationCheckerTest {
     
@@ -50,38 +42,6 @@ class ShardingRuleConfigurationCheckerTest {
                 DatabaseRuleConfigurationChecker.class, Collections.singleton(ShardingRuleConfiguration.class)).get(ShardingRuleConfiguration.class);
     }
     
-    @Test
-    void assertCheckWithMultipleSchemasForSameDataSource() {
-        ShardingRuleConfiguration ruleConfig = createRuleConfiguration();
-        ruleConfig.setTables(Collections.singleton(new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema_${0..1}.foo_tbl_${0..1}")));
-        InvalidRuleConfigurationException actual = assertThrows(InvalidRuleConfigurationException.class,
-                () -> checker.check("foo_db", ruleConfig, Collections.emptyMap(), Collections.emptyList()));
-        assertThat(actual.getMessage(), is("Invalid 'sharding table' rules 'foo_tbl', error messages are: Multiple schemas are configured for storage unit 'ds_0'."));
-    }
-    
-    @ParameterizedTest(name = "{0}")
-    @MethodSource("validSchemaDataNodesArguments")
-    void assertCheckWithValidSchemaDataNodes(final String name, final Collection<ShardingTableRuleConfiguration> tableRuleConfigs) {
-        ShardingRuleConfiguration ruleConfig = createRuleConfiguration();
-        ruleConfig.setTables(tableRuleConfigs);
-        assertDoesNotThrow(() -> checker.check("foo_db", ruleConfig, Collections.emptyMap(), Collections.emptyList()));
-    }
-    
-    private static Stream<Arguments> validSchemaDataNodesArguments() {
-        return Stream.of(
-                Arguments.of("same schema for same data source", Collections.singleton(
-                        new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema.foo_tbl_0,ds_0.foo_schema.foo_tbl_1"))),
-                Arguments.of("same schema in different cases for same data source", Collections.singleton(
-                        new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema.foo_tbl_0,ds_0.FOO_SCHEMA.foo_tbl_1"))),
-                Arguments.of("omitted schema and explicit schema for same data source", Collections.singleton(
-                        new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_tbl_0,ds_0.foo_schema.foo_tbl_1"))),
-                Arguments.of("different schemas for different data sources", Collections.singleton(
-                        new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema.foo_tbl_0,ds_1.bar_schema.foo_tbl_1"))),
-                Arguments.of("different schemas for different logic tables", Arrays.asList(
-                        new ShardingTableRuleConfiguration("foo_tbl", "ds_0.foo_schema.foo_tbl_0"),
-                        new ShardingTableRuleConfiguration("bar_tbl", "ds_0.bar_schema.bar_tbl_0"))));
-    }
-    
     @Test
     void assertGetRequiredDataSourceNames() {
         ShardingRuleConfiguration ruleConfig = createRuleConfiguration();
diff --git a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java
index 8c8ff05d30d..e238c6cb99e 100644
--- a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java
+++ b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java
@@ -67,7 +67,6 @@ public final class DatabaseRulesBuilder {
                                                        final ComputeNodeInstanceContext computeNodeInstanceContext, final ResourceMetaData resourceMetaData) {
         RuleConfigurationValidator.validate(databaseConfig.getRuleConfigurations());
         Map<RuleConfiguration, DatabaseRuleBuilder> ruleBuilderMap = getRuleBuilderMap(databaseConfig);
-        RuleConfigurationValidator.validate(ruleBuilderMap.keySet());
         Collection<ShardingSphereRule> result = new LinkedList<>();
         for (Entry<RuleConfiguration, DatabaseRuleBuilder> entry : ruleBuilderMap.entrySet()) {
             DatabaseRuleConfigurationChecker configChecker = OrderedSPILoader.getServicesByClass(
@@ -153,7 +152,9 @@ public final class DatabaseRulesBuilder {
         Map<RuleConfiguration, DatabaseRuleBuilder> result = new LinkedHashMap<>(defaultBuilders.size(), 1F);
         // TODO consider about order for new put items
         for (Entry<DatabaseRuleBuilder, DefaultDatabaseRuleConfigurationBuilder> entry : defaultBuilders.entrySet()) {
-            result.put(entry.getValue().build(), entry.getKey());
+            RuleConfiguration ruleConfig = entry.getValue().build();
+            RuleConfigurationValidator.validate(ruleConfig);
+            result.put(ruleConfig, entry.getKey());
         }
         return result;
     }
diff --git a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java
index fb6631b4dc4..f2f3c650ee6 100644
--- a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java
+++ b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java
@@ -20,6 +20,7 @@ package org.apache.shardingsphere.infra.rule.builder.database;
 import org.apache.shardingsphere.infra.config.database.impl.DataSourceProvidedDatabaseConfiguration;
 import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
 import org.apache.shardingsphere.infra.config.rule.checker.DatabaseRuleConfigurationChecker;
+import org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
 import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.DuplicateRuleException;
 import org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
 import org.apache.shardingsphere.infra.fixture.FixtureRule;
@@ -56,6 +57,7 @@ import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 class DatabaseRulesBuilderTest {
@@ -123,6 +125,34 @@ class DatabaseRulesBuilderTest {
                 new DataSourceProvidedDatabaseConfiguration(Collections.emptyMap(), Collections.singleton(ruleConfig)), null, EMPTY_RESOURCE_META_DATA));
     }
     
+    @Test
+    void assertBuildWithRuleConfigurationValidatedOnce() {
+        ToggleFixtureDatabaseRuleConfiguration ruleConfig = new ToggleFixtureDatabaseRuleConfiguration(false);
+        try (MockedStatic<RuleConfigurationValidator> mockedValidator = mockStatic(RuleConfigurationValidator.class, CALLS_REAL_METHODS)) {
+            Collection<ShardingSphereRule> actual = DatabaseRulesBuilder.build("foo_db", null,
+                    new DataSourceProvidedDatabaseConfiguration(Collections.emptyMap(), Collections.singleton(ruleConfig)), null, EMPTY_RESOURCE_META_DATA);
+            assertTrue(actual.stream().anyMatch(ToggleFixtureRule.class::isInstance));
+            mockedValidator.verify(() -> RuleConfigurationValidator.validate(ruleConfig));
+        }
+    }
+    
+    @Test
+    @SuppressWarnings("unchecked")
+    void assertBuildWithInvalidDefaultRuleConfiguration() {
+        FixtureDatabaseRuleConfiguration ruleConfig = new FixtureDatabaseRuleConfiguration();
+        ruleConfig.setName("");
+        DatabaseRuleBuilder<FixtureDatabaseRuleConfiguration> ruleBuilder = mock(DatabaseRuleBuilder.class);
+        DefaultDatabaseRuleConfigurationBuilder<FixtureDatabaseRuleConfiguration, ?> defaultBuilder = mock(DefaultDatabaseRuleConfigurationBuilder.class);
+        when(defaultBuilder.build()).thenReturn(ruleConfig);
+        try (MockedStatic<OrderedSPILoader> mockedLoader = mockStatic(OrderedSPILoader.class, CALLS_REAL_METHODS)) {
+            mockedLoader.when(() -> OrderedSPILoader.getServices(eq(DefaultDatabaseRuleConfigurationBuilder.class), anyCollection()))
+                    .thenReturn(Collections.singletonMap(ruleBuilder, defaultBuilder));
+            assertThrows(InvalidRuleConfigurationException.class, () -> DatabaseRulesBuilder.build("foo_db", null,
+                    new DataSourceProvidedDatabaseConfiguration(Collections.emptyMap(), Collections.emptyList()), null, EMPTY_RESOURCE_META_DATA));
+            verifyNoInteractions(ruleBuilder);
+        }
+    }
+    
     @Test
     void assertBuildSingleRule() {
         assertThat(DatabaseRulesBuilder.build("foo_db", null, Collections.emptyList(), new FixtureDatabaseRuleConfiguration(), null, EMPTY_RESOURCE_META_DATA), isA(FixtureRule.class));
diff --git a/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/ral/updatable/lock/LockClusterExecutor.java b/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/ral/updatable/lock/LockClusterExecutor.java
index 4a24bd2067b..5d0ce9cc893 100644
--- a/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/ral/updatable/lock/LockClusterExecutor.java
+++ b/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/ral/updatable/lock/LockClusterExecutor.java
@@ -18,7 +18,6 @@
 package org.apache.shardingsphere.distsql.handler.executor.ral.updatable.lock;
 
 import org.apache.shardingsphere.distsql.handler.engine.update.spi.DistSQLUpdateExecutor;
-import org.apache.shardingsphere.distsql.handler.executor.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.distsql.handler.required.DistSQLExecutorClusterModeRequired;
 import org.apache.shardingsphere.distsql.statement.type.ral.updatable.LockClusterStatement;
 import org.apache.shardingsphere.infra.algorithm.core.exception.MissingRequiredAlgorithmException;
@@ -27,6 +26,7 @@ import org.apache.shardingsphere.infra.exception.external.sql.identifier.SQLExce
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.mode.manager.ContextManager;
 import org.apache.shardingsphere.mode.manager.cluster.lock.exception.LockedClusterException;
+import org.apache.shardingsphere.mode.manager.cluster.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.mode.state.ShardingSphereState;
 
 import java.sql.SQLException;
diff --git a/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/lock/spi/ClusterLockStrategy.java b/mode/type/cluster/core/src/main/java/org/apache/shardingsphere/mode/manager/cluster/lock/spi/ClusterLockStrategy.java
similarity index 94%
rename from infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/lock/spi/ClusterLockStrategy.java
rename to mode/type/cluster/core/src/main/java/org/apache/shardingsphere/mode/manager/cluster/lock/spi/ClusterLockStrategy.java
index 30f87fc49e1..9fa4448f58e 100644
--- a/infra/distsql-handler/src/main/java/org/apache/shardingsphere/distsql/handler/executor/lock/spi/ClusterLockStrategy.java
+++ b/mode/type/cluster/core/src/main/java/org/apache/shardingsphere/mode/manager/cluster/lock/spi/ClusterLockStrategy.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.distsql.handler.executor.lock.spi;
+package org.apache.shardingsphere.mode.manager.cluster.lock.spi;
 
 import org.apache.shardingsphere.infra.spi.annotation.SingletonSPI;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPI;
diff --git a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategy.java b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategy.java
index 6db5210aff8..487151f3c17 100644
--- a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategy.java
+++ b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategy.java
@@ -17,7 +17,7 @@
 
 package org.apache.shardingsphere.proxy.backend.lock.impl;
 
-import org.apache.shardingsphere.distsql.handler.executor.lock.spi.ClusterLockStrategy;
+import org.apache.shardingsphere.mode.manager.cluster.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.mode.state.ShardingSphereState;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 
diff --git a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategy.java b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategy.java
index 5feb9a239ba..24affb5dce7 100644
--- a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategy.java
+++ b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategy.java
@@ -17,7 +17,7 @@
 
 package org.apache.shardingsphere.proxy.backend.lock.impl;
 
-import org.apache.shardingsphere.distsql.handler.executor.lock.spi.ClusterLockStrategy;
+import org.apache.shardingsphere.mode.manager.cluster.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.mode.state.ShardingSphereState;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 
diff --git a/proxy/backend/core/src/main/resources/META-INF/services/org.apache.shardingsphere.distsql.handler.executor.lock.spi.ClusterLockStrategy b/proxy/backend/core/src/main/resources/META-INF/services/org.apache.shardingsphere.mode.manager.cluster.lock.spi.ClusterLockStrategy
similarity index 100%
rename from proxy/backend/core/src/main/resources/META-INF/services/org.apache.shardingsphere.distsql.handler.executor.lock.spi.ClusterLockStrategy
rename to proxy/backend/core/src/main/resources/META-INF/services/org.apache.shardingsphere.mode.manager.cluster.lock.spi.ClusterLockStrategy
diff --git a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategyTest.java b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategyTest.java
index b4c4b1b650c..16be664878c 100644
--- a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategyTest.java
+++ b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterReadWriteLockStrategyTest.java
@@ -17,8 +17,8 @@
 
 package org.apache.shardingsphere.proxy.backend.lock.impl;
 
-import org.apache.shardingsphere.distsql.handler.executor.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.apache.shardingsphere.mode.manager.cluster.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.mode.state.ShardingSphereState;
 import org.apache.shardingsphere.mode.state.StatePersistService;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
diff --git a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategyTest.java b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategyTest.java
index b17f4dbd923..7995ef19162 100644
--- a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategyTest.java
+++ b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/lock/impl/ClusterWriteLockStrategyTest.java
@@ -17,8 +17,8 @@
 
 package org.apache.shardingsphere.proxy.backend.lock.impl;
 
-import org.apache.shardingsphere.distsql.handler.executor.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.apache.shardingsphere.mode.manager.cluster.lock.spi.ClusterLockStrategy;
 import org.apache.shardingsphere.mode.state.ShardingSphereState;
 import org.apache.shardingsphere.mode.state.StatePersistService;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;