This is an automated email from the ASF dual-hosted git repository.
menghaoranss 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 4ff4642c9aa Implement mutable data node rule attribute with
copy-on-write support (#39355)
4ff4642c9aa is described below
commit 4ff4642c9aa9734a04c631bf11741e6a93eb91ec
Author: Haoran Meng <[email protected]>
AuthorDate: Thu Aug 6 10:39:52 2026 +0800
Implement mutable data node rule attribute with copy-on-write support
(#39355)
---
.../metadata/database/ShardingSphereDatabase.java | 38 +++++++++-
.../infra/metadata/database/rule/RuleMetaData.java | 46 ++++++++++++
.../datanode/MutableDataNodeRuleAttribute.java | 19 +++++
.../database/ShardingSphereDatabaseTest.java | 46 ++++++++++++
.../metadata/database/rule/RuleMetaDataTest.java | 51 +++++++++++++
.../shardingsphere/single/rule/SingleRule.java | 48 +++++++++++-
.../SingleMutableDataNodeRuleAttribute.java | 12 +++
.../shardingsphere/single/rule/SingleRuleTest.java | 86 ++++++++++++++++++++++
.../SingleMutableDataNodeRuleAttributeTest.java | 3 +-
.../manager/database/DatabaseMetaDataManager.java | 3 +-
.../database/DatabaseMetaDataManagerTest.java | 10 +--
.../CreateViewPushDownMetaDataRefresherTest.java | 12 ++-
12 files changed, 358 insertions(+), 16 deletions(-)
diff --git
a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabase.java
b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabase.java
index 63bf79dfb00..f71a4894033 100644
---
a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabase.java
+++
b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabase.java
@@ -61,7 +61,7 @@ public final class ShardingSphereDatabase {
private final ResourceMetaData resourceMetaData;
- private final RuleMetaData ruleMetaData;
+ private volatile RuleMetaData ruleMetaData;
private final DatabaseIdentifierContext identifierContext;
@@ -197,6 +197,39 @@ public final class ShardingSphereDatabase {
return !resourceMetaData.getStorageUnits().isEmpty();
}
+ /**
+ * Add data node and publish new rule meta data snapshot.
+ *
+ * @param dataSourceName data source name
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return whether rule meta data changed
+ */
+ public synchronized boolean putDataNode(final String dataSourceName, final
String schemaName, final String tableName) {
+ RuleMetaData newRuleMetaData =
ruleMetaData.copyAndPutDataNode(dataSourceName, schemaName, tableName);
+ if (newRuleMetaData == ruleMetaData) {
+ return false;
+ }
+ ruleMetaData = newRuleMetaData;
+ return true;
+ }
+
+ /**
+ * Remove data node and publish new rule meta data snapshot.
+ *
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return whether rule meta data changed
+ */
+ public synchronized boolean removeDataNode(final String schemaName, final
String tableName) {
+ RuleMetaData newRuleMetaData =
ruleMetaData.copyAndRemoveDataNode(schemaName, tableName);
+ if (newRuleMetaData == ruleMetaData) {
+ return false;
+ }
+ ruleMetaData = newRuleMetaData;
+ return true;
+ }
+
/**
* Reload rules.
*/
@@ -211,8 +244,7 @@ public final class ShardingSphereDatabase {
.collect(Collectors.toMap(Entry::getKey, entry ->
entry.getValue().getDataSource(), (oldValue, currentValue) -> oldValue,
LinkedHashMap::new));
rules.add(optional.getAttributes().getAttribute(MutableDataNodeRuleAttribute.class).reloadRule(ruleConfig,
name, dataSources, rules));
});
- ruleMetaData.getRules().clear();
- ruleMetaData.getRules().addAll(rules);
+ ruleMetaData = new RuleMetaData(rules);
}
/**
diff --git
a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
index 03607083bd0..5b1a70bd827 100644
---
a/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
+++
b/infra/common/src/main/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaData.java
@@ -24,6 +24,7 @@ import
org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
import org.apache.shardingsphere.infra.rule.attribute.RuleAttribute;
import
org.apache.shardingsphere.infra.rule.attribute.datanode.DataNodeRuleAttribute;
+import
org.apache.shardingsphere.infra.rule.attribute.datanode.MutableDataNodeRuleAttribute;
import
org.apache.shardingsphere.infra.rule.attribute.datasource.DataSourceMapperRuleAttribute;
import java.util.Collection;
@@ -57,6 +58,51 @@ public final class RuleMetaData {
this.rules = new CacheInvalidatingCopyOnWriteArrayList(rules);
}
+ /**
+ * Copy rule meta data and add data node.
+ *
+ * @param dataSourceName data source name
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return copied rule meta data, or current rule meta data if data node
exists
+ */
+ public RuleMetaData copyAndPutDataNode(final String dataSourceName, final
String schemaName, final String tableName) {
+ Collection<ShardingSphereRule> copiedRules = new LinkedList<>();
+ boolean changed = false;
+ for (ShardingSphereRule each : rules) {
+ Optional<MutableDataNodeRuleAttribute> attribute =
each.getAttributes().findAttribute(MutableDataNodeRuleAttribute.class);
+ if (attribute.isPresent() &&
!attribute.get().findTableDataNode(schemaName, tableName).isPresent()) {
+ copiedRules.add(attribute.get().copyRuleAndPut(dataSourceName,
schemaName, tableName));
+ changed = true;
+ } else {
+ copiedRules.add(each);
+ }
+ }
+ return changed ? new RuleMetaData(copiedRules) : this;
+ }
+
+ /**
+ * Copy rule meta data and remove data node.
+ *
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return copied rule meta data, or current rule meta data if data node
does not exist
+ */
+ public RuleMetaData copyAndRemoveDataNode(final String schemaName, final
String tableName) {
+ Collection<ShardingSphereRule> copiedRules = new LinkedList<>();
+ boolean changed = false;
+ for (ShardingSphereRule each : rules) {
+ Optional<MutableDataNodeRuleAttribute> attribute =
each.getAttributes().findAttribute(MutableDataNodeRuleAttribute.class);
+ if (attribute.isPresent() &&
attribute.get().findTableDataNode(schemaName, tableName).isPresent()) {
+ copiedRules.add(attribute.get().copyRuleAndRemove(schemaName,
tableName));
+ changed = true;
+ } else {
+ copiedRules.add(each);
+ }
+ }
+ return changed ? new RuleMetaData(copiedRules) : this;
+ }
+
/**
* Get rule configurations.
*
diff --git
a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/attribute/datanode/MutableDataNodeRuleAttribute.java
b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/attribute/datanode/MutableDataNodeRuleAttribute.java
index d022f204d6f..7a6d9620118 100644
---
a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/attribute/datanode/MutableDataNodeRuleAttribute.java
+++
b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/attribute/datanode/MutableDataNodeRuleAttribute.java
@@ -32,6 +32,25 @@ import java.util.Optional;
*/
public interface MutableDataNodeRuleAttribute extends RuleAttribute {
+ /**
+ * Copy rule and add data node.
+ *
+ * @param dataSourceName data source name
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return copied rule with added data node
+ */
+ ShardingSphereRule copyRuleAndPut(String dataSourceName, String
schemaName, String tableName);
+
+ /**
+ * Copy rule and remove data node.
+ *
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return copied rule with removed data node
+ */
+ ShardingSphereRule copyRuleAndRemove(String schemaName, String tableName);
+
/**
* Add data node.
*
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabaseTest.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabaseTest.java
index e8cd6597561..567837b4dc7 100644
---
a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabaseTest.java
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/ShardingSphereDatabaseTest.java
@@ -72,6 +72,8 @@ import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -178,6 +180,8 @@ class ShardingSphereDatabaseTest {
"foo_db", databaseType, new
ResourceMetaData(Collections.singletonMap("ds", new MockedDataSource())),
ruleMetaData, Collections.emptyList(),
new ConfigurationProperties(new Properties()));
database.reloadRules();
+ assertThat(database.getRuleMetaData(),
not(sameInstance(ruleMetaData)));
+ assertTrue(ruleMetaData.getRules().contains(mutableRule));
Collection<ShardingSphereRule> actualRules =
database.getRuleMetaData().getRules();
assertThat(actualRules.size(), is(2));
assertFalse(actualRules.contains(mutableRule));
@@ -185,6 +189,48 @@ class ShardingSphereDatabaseTest {
assertTrue(actualRules.contains(reloadedRule));
}
+ @Test
+ void assertPutDataNode() {
+ RuleMetaData original = mock(RuleMetaData.class);
+ RuleMetaData updated = mock(RuleMetaData.class);
+ when(original.copyAndPutDataNode("foo_ds", "foo_schema",
"foo_tbl")).thenReturn(updated);
+ ShardingSphereDatabase database =
createDatabaseWithRuleMetaData(original);
+ assertTrue(database.putDataNode("foo_ds", "foo_schema", "foo_tbl"));
+ assertThat(database.getRuleMetaData(), sameInstance(updated));
+ }
+
+ @Test
+ void assertPutDataNodeWhenUnchanged() {
+ RuleMetaData original = mock(RuleMetaData.class);
+ when(original.copyAndPutDataNode("foo_ds", "foo_schema",
"foo_tbl")).thenReturn(original);
+ ShardingSphereDatabase database =
createDatabaseWithRuleMetaData(original);
+ assertFalse(database.putDataNode("foo_ds", "foo_schema", "foo_tbl"));
+ assertThat(database.getRuleMetaData(), sameInstance(original));
+ }
+
+ @Test
+ void assertRemoveDataNode() {
+ RuleMetaData original = mock(RuleMetaData.class);
+ RuleMetaData updated = mock(RuleMetaData.class);
+ when(original.copyAndRemoveDataNode("foo_schema",
"foo_tbl")).thenReturn(updated);
+ ShardingSphereDatabase database =
createDatabaseWithRuleMetaData(original);
+ assertTrue(database.removeDataNode("foo_schema", "foo_tbl"));
+ assertThat(database.getRuleMetaData(), sameInstance(updated));
+ }
+
+ @Test
+ void assertRemoveDataNodeWhenUnchanged() {
+ RuleMetaData original = mock(RuleMetaData.class);
+ when(original.copyAndRemoveDataNode("foo_schema",
"foo_tbl")).thenReturn(original);
+ ShardingSphereDatabase database =
createDatabaseWithRuleMetaData(original);
+ assertFalse(database.removeDataNode("foo_schema", "foo_tbl"));
+ assertThat(database.getRuleMetaData(), sameInstance(original));
+ }
+
+ private ShardingSphereDatabase createDatabaseWithRuleMetaData(final
RuleMetaData ruleMetaData) {
+ return new ShardingSphereDatabase("foo_db", databaseType,
mock(ResourceMetaData.class), ruleMetaData, Collections.emptyList(), new
ConfigurationProperties(new Properties()));
+ }
+
@Test
void assertReloadRulesWithoutMutableDataNodeRuleAttribute() {
ShardingSphereRule rule = createRule(mock(RuleConfiguration.class),
new RuleAttributes());
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaDataTest.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaDataTest.java
index d422cd0756b..7b268a06917 100644
---
a/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaDataTest.java
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/metadata/database/rule/RuleMetaDataTest.java
@@ -22,6 +22,7 @@ import
org.apache.shardingsphere.infra.rule.ShardingSphereRule;
import org.apache.shardingsphere.infra.rule.attribute.RuleAttribute;
import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
import
org.apache.shardingsphere.infra.rule.attribute.datanode.DataNodeRuleAttribute;
+import
org.apache.shardingsphere.infra.rule.attribute.datanode.MutableDataNodeRuleAttribute;
import
org.apache.shardingsphere.infra.rule.attribute.datasource.DataSourceMapperRuleAttribute;
import org.apache.shardingsphere.infra.rule.scope.GlobalRule;
import org.junit.jupiter.api.BeforeEach;
@@ -40,6 +41,8 @@ import java.util.stream.Stream;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isA;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -163,6 +166,54 @@ class RuleMetaDataTest {
assertFalse(new
RuleMetaData(Collections.singleton(mock(ShardingSphereRule.class,
RETURNS_DEEP_STUBS))).findAttribute(DataSourceMapperRuleAttribute.class).isPresent());
}
+ @Test
+ void assertCopyAndPutDataNode() {
+ MutableDataNodeRuleAttribute attribute =
mock(MutableDataNodeRuleAttribute.class);
+ ShardingSphereRule mutableRule = mock(ShardingSphereRule.class);
+ ShardingSphereRule copiedRule = mock(ShardingSphereRule.class);
+ when(mutableRule.getAttributes()).thenReturn(new
RuleAttributes(attribute));
+ when(attribute.copyRuleAndPut("foo_ds", "foo_schema",
"foo_tbl")).thenReturn(copiedRule);
+ RuleMetaData original = new RuleMetaData(Arrays.asList(fixtureRule,
mutableRule, dataNodeRule));
+ RuleMetaData actual = original.copyAndPutDataNode("foo_ds",
"foo_schema", "foo_tbl");
+ assertThat(actual, not(sameInstance(original)));
+ assertThat(actual.getRules(), is(Arrays.asList(fixtureRule,
copiedRule, dataNodeRule)));
+ assertThat(original.getRules(), is(Arrays.asList(fixtureRule,
mutableRule, dataNodeRule)));
+ }
+
+ @Test
+ void assertCopyAndPutDataNodeWhenDataNodeExists() {
+ MutableDataNodeRuleAttribute attribute =
mock(MutableDataNodeRuleAttribute.class);
+ ShardingSphereRule mutableRule = mock(ShardingSphereRule.class);
+ when(mutableRule.getAttributes()).thenReturn(new
RuleAttributes(attribute));
+ when(attribute.findTableDataNode("foo_schema",
"foo_tbl")).thenReturn(Optional.of(new DataNode("foo_ds.foo_schema.foo_tbl")));
+ RuleMetaData original = new
RuleMetaData(Collections.singleton(mutableRule));
+ assertThat(original.copyAndPutDataNode("foo_ds", "foo_schema",
"foo_tbl"), sameInstance(original));
+ }
+
+ @Test
+ void assertCopyAndRemoveDataNode() {
+ MutableDataNodeRuleAttribute attribute =
mock(MutableDataNodeRuleAttribute.class);
+ ShardingSphereRule mutableRule = mock(ShardingSphereRule.class);
+ ShardingSphereRule copiedRule = mock(ShardingSphereRule.class);
+ when(mutableRule.getAttributes()).thenReturn(new
RuleAttributes(attribute));
+ when(attribute.findTableDataNode("foo_schema",
"foo_tbl")).thenReturn(Optional.of(new DataNode("foo_ds.foo_schema.foo_tbl")));
+ when(attribute.copyRuleAndRemove("foo_schema",
"foo_tbl")).thenReturn(copiedRule);
+ RuleMetaData original = new RuleMetaData(Arrays.asList(fixtureRule,
mutableRule, dataNodeRule));
+ RuleMetaData actual = original.copyAndRemoveDataNode("foo_schema",
"foo_tbl");
+ assertThat(actual, not(sameInstance(original)));
+ assertThat(actual.getRules(), is(Arrays.asList(fixtureRule,
copiedRule, dataNodeRule)));
+ assertThat(original.getRules(), is(Arrays.asList(fixtureRule,
mutableRule, dataNodeRule)));
+ }
+
+ @Test
+ void assertCopyAndRemoveDataNodeWhenDataNodeDoesNotExist() {
+ MutableDataNodeRuleAttribute attribute =
mock(MutableDataNodeRuleAttribute.class);
+ ShardingSphereRule mutableRule = mock(ShardingSphereRule.class);
+ when(mutableRule.getAttributes()).thenReturn(new
RuleAttributes(attribute));
+ RuleMetaData original = new
RuleMetaData(Collections.singleton(mutableRule));
+ assertThat(original.copyAndRemoveDataNode("foo_schema", "foo_tbl"),
sameInstance(original));
+ }
+
@Test
void assertInvalidateCacheWhenRuleAdded() {
ruleMetaData.findSingleRule(RuleMetaDataShardingSphereRuleFixture.class);
diff --git
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/SingleRule.java
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/SingleRule.java
index 6731bbc2705..d969f516c9d 100644
---
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/SingleRule.java
+++
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/SingleRule.java
@@ -48,8 +48,11 @@ import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.Iden
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
@@ -86,12 +89,55 @@ public final class SingleRule implements DatabaseRule {
this.protocolType = protocolType;
singleTableDataNodes = SingleTableDataNodeLoader.load(databaseName,
protocolType, aggregatedDataSources, builtRules, configuration.getTables());
SingleTableMapperRuleAttribute tableMapperRuleAttribute = new
SingleTableMapperRuleAttribute(singleTableDataNodes.values());
- mutableDataNodeRuleAttribute = new
SingleMutableDataNodeRuleAttribute(configuration, dataSourceNames,
singleTableDataNodes, protocolType, tableMapperRuleAttribute);
+ mutableDataNodeRuleAttribute = new
SingleMutableDataNodeRuleAttribute(this, configuration, dataSourceNames,
singleTableDataNodes, protocolType, tableMapperRuleAttribute);
attributes = new RuleAttributes(new
SingleDataNodeRuleAttribute(singleTableDataNodes), tableMapperRuleAttribute,
new SingleExportableRuleAttribute(tableMapperRuleAttribute),
mutableDataNodeRuleAttribute, new
AggregatedDataSourceRuleAttribute(aggregatedDataSources),
new SingleUnregisterStorageUnitRuleAttribute());
}
+ private SingleRule(final SingleRule original) {
+ configuration = new SingleRuleConfiguration(new
LinkedList<>(original.configuration.getTables()), original.defaultDataSource);
+ defaultDataSource = original.defaultDataSource;
+ dataSourceNames = new CaseInsensitiveSet<>(original.dataSourceNames);
+ singleTableDataNodes = new
LinkedHashMap<>(original.singleTableDataNodes.size(), 1F);
+ for (Entry<String, Collection<DataNode>> entry :
original.singleTableDataNodes.entrySet()) {
+ singleTableDataNodes.put(entry.getKey(), new
LinkedHashSet<>(entry.getValue()));
+ }
+ protocolType = original.protocolType;
+ SingleTableMapperRuleAttribute tableMapperRuleAttribute = new
SingleTableMapperRuleAttribute(singleTableDataNodes.values());
+ mutableDataNodeRuleAttribute = new
SingleMutableDataNodeRuleAttribute(this, configuration, dataSourceNames,
singleTableDataNodes, protocolType, tableMapperRuleAttribute);
+ attributes = new RuleAttributes(new
SingleDataNodeRuleAttribute(singleTableDataNodes), tableMapperRuleAttribute,
+ new SingleExportableRuleAttribute(tableMapperRuleAttribute),
mutableDataNodeRuleAttribute,
+
original.attributes.getAttribute(AggregatedDataSourceRuleAttribute.class), new
SingleUnregisterStorageUnitRuleAttribute());
+ }
+
+ /**
+ * Copy rule and add data node.
+ *
+ * @param dataSourceName data source name
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return copied rule with added data node
+ */
+ public SingleRule copyAndPut(final String dataSourceName, final String
schemaName, final String tableName) {
+ SingleRule result = new SingleRule(this);
+ result.mutableDataNodeRuleAttribute.put(dataSourceName, schemaName,
tableName);
+ return result;
+ }
+
+ /**
+ * Copy rule and remove data node.
+ *
+ * @param schemaName schema name
+ * @param tableName table name
+ * @return copied rule with removed data node
+ */
+ public SingleRule copyAndRemove(final String schemaName, final String
tableName) {
+ SingleRule result = new SingleRule(this);
+ result.mutableDataNodeRuleAttribute.remove(schemaName, tableName);
+ return result;
+ }
+
/**
* Assign new data source name.
*
diff --git
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
index 2eedd5f9dc1..191283b35d5 100644
---
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
+++
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
@@ -43,6 +43,8 @@ import java.util.Optional;
@RequiredArgsConstructor
public final class SingleMutableDataNodeRuleAttribute implements
MutableDataNodeRuleAttribute {
+ private final SingleRule singleRule;
+
private final SingleRuleConfiguration configuration;
private final Collection<String> dataSourceNames;
@@ -53,6 +55,16 @@ public final class SingleMutableDataNodeRuleAttribute
implements MutableDataNode
private final SingleTableMapperRuleAttribute tableMapperRuleAttribute;
+ @Override
+ public ShardingSphereRule copyRuleAndPut(final String dataSourceName,
final String schemaName, final String tableName) {
+ return singleRule.copyAndPut(dataSourceName, schemaName, tableName);
+ }
+
+ @Override
+ public ShardingSphereRule copyRuleAndRemove(final String schemaName, final
String tableName) {
+ return singleRule.copyAndRemove(schemaName, tableName);
+ }
+
@SuppressWarnings("CollectionWithoutInitialCapacity")
@Override
public void put(final String dataSourceName, final String schemaName,
final String tableName) {
diff --git
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/SingleRuleTest.java
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/SingleRuleTest.java
index 73a5ecc1213..c3a9eb8c226 100644
---
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/SingleRuleTest.java
+++
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/SingleRuleTest.java
@@ -22,8 +22,11 @@ import
org.apache.shardingsphere.database.connector.core.metadata.identifier.Ide
import
org.apache.shardingsphere.database.connector.core.metadata.identifier.LookupMode;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
+import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
import org.apache.shardingsphere.infra.datanode.DataNode;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
+import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
+import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
import org.apache.shardingsphere.infra.metadata.database.schema.QualifiedTable;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereTable;
@@ -63,11 +66,18 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Locale;
+import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -241,6 +251,82 @@ class SingleRuleTest {
assertTrue(actualLogicTableNames.contains("t_order_1"));
}
+ @Test
+ void assertCopyAndPut() {
+ SingleRule original = new SingleRule(ruleConfig, "foo_db",
databaseType, dataSourceMap,
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)));
+ SingleRule copied = original.copyAndPut("foo_ds", "foo_db", "teacher");
+ assertThat(copied, not(sameInstance(original)));
+ assertFalse(original.getSingleTableDataNodes().containsKey("teacher"));
+
assertFalse(original.getAttributes().getAttribute(TableMapperRuleAttribute.class).getLogicTableNames().contains("teacher"));
+
assertFalse(original.getConfiguration().getLogicTableNames().contains("teacher"));
+ assertTrue(copied.getSingleTableDataNodes().containsKey("teacher"));
+
assertTrue(copied.getAttributes().getAttribute(TableMapperRuleAttribute.class).getLogicTableNames().contains("teacher"));
+
assertTrue(copied.getConfiguration().getLogicTableNames().contains("teacher"));
+ }
+
+ @Test
+ void assertCopyAndPutWithWildcardConfiguration() {
+ SingleRuleConfiguration wildcardConfig = new
SingleRuleConfiguration(Collections.singleton("*.*"), null);
+ SingleRule original = new SingleRule(wildcardConfig, "foo_db",
databaseType, dataSourceMap,
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)));
+ SingleRule copied = original.copyAndPut("foo_ds", "foo_db", "teacher");
+ assertThat(copied.getConfiguration().getTables().size(), is(1));
+ assertTrue(copied.getConfiguration().getTables().contains("*.*"));
+ assertThat(original.getConfiguration().getTables().size(), is(1));
+ assertTrue(original.getConfiguration().getTables().contains("*.*"));
+ }
+
+ @Test
+ void assertCopyAndRemove() {
+ SingleRule original = new SingleRule(ruleConfig, "foo_db",
databaseType, dataSourceMap,
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)));
+ SingleRule copied = original.copyAndRemove("foo_db", "employee");
+ assertThat(copied, not(sameInstance(original)));
+ assertTrue(original.getSingleTableDataNodes().containsKey("employee"));
+
assertTrue(original.getAttributes().getAttribute(TableMapperRuleAttribute.class).getLogicTableNames().contains("employee"));
+
assertTrue(original.getConfiguration().getLogicTableNames().contains("employee"));
+ assertFalse(copied.getSingleTableDataNodes().containsKey("employee"));
+
assertFalse(copied.getAttributes().getAttribute(TableMapperRuleAttribute.class).getLogicTableNames().contains("employee"));
+
assertFalse(copied.getConfiguration().getLogicTableNames().contains("employee"));
+ }
+
+ @Test
+ void assertConcurrentRuleMetaDataPublication() throws Exception {
+ SingleRule original = new SingleRule(ruleConfig, "foo_db",
databaseType, dataSourceMap,
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)));
+ ShardingSphereDatabase database = new ShardingSphereDatabase("foo_db",
databaseType, mock(ResourceMetaData.class),
+ new RuleMetaData(Collections.singleton(original)),
Collections.emptyList(), new ConfigurationProperties(new Properties()));
+ CountDownLatch writing = new CountDownLatch(1);
+ ExecutorService executorService = Executors.newFixedThreadPool(2);
+ try {
+ Future<?> writer = executorService.submit(() -> {
+ try {
+ for (int i = 0; i < 1000; i++) {
+ database.putDataNode("foo_ds", "foo_db", "teacher");
+ database.removeDataNode("foo_db", "teacher");
+ }
+ } finally {
+ writing.countDown();
+ }
+ });
+ Future<?> reader = executorService.submit(() -> {
+ do {
+ assertSingleRuleSnapshot(database.getRuleMetaData(),
"teacher");
+ } while (0L < writing.getCount());
+ });
+ writer.get();
+ reader.get();
+ } finally {
+ executorService.shutdownNow();
+ }
+ assertFalse(original.getSingleTableDataNodes().containsKey("teacher"));
+ }
+
+ private void assertSingleRuleSnapshot(final RuleMetaData ruleMetaData,
final String tableName) {
+ assertFalse(ruleMetaData.getRules().isEmpty());
+ SingleRule rule = ruleMetaData.getSingleRule(SingleRule.class);
+ boolean dataNodePresent =
rule.getSingleTableDataNodes().containsKey(tableName);
+
assertThat(rule.getAttributes().getAttribute(TableMapperRuleAttribute.class).getLogicTableNames().contains(tableName),
is(dataNodePresent));
+
assertThat(rule.getConfiguration().getLogicTableNames().contains(tableName),
is(dataNodePresent));
+ }
+
@Test
void assertGetAllDataNodes() {
ShardingSphereRule builtRule = mock(ShardingSphereRule.class,
RETURNS_DEEP_STUBS);
diff --git
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
index cd208782b42..f1a05e91523 100644
---
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
+++
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
@@ -44,6 +44,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
class SingleMutableDataNodeRuleAttributeTest {
@@ -223,7 +224,7 @@ class SingleMutableDataNodeRuleAttributeTest {
private static SingleMutableDataNodeRuleAttribute
createRuleAttribute(final SingleRuleConfiguration configuration, final
Collection<String> dataSourceNames,
final Map<String, Collection<DataNode>> singleTableDataNodes,
final SingleTableMapperRuleAttribute tableMapperRuleAttribute) {
- return new SingleMutableDataNodeRuleAttribute(configuration, new
LinkedHashSet<>(dataSourceNames), singleTableDataNodes, DATABASE_TYPE,
tableMapperRuleAttribute);
+ return new SingleMutableDataNodeRuleAttribute(mock(SingleRule.class),
configuration, new LinkedHashSet<>(dataSourceNames), singleTableDataNodes,
DATABASE_TYPE, tableMapperRuleAttribute);
}
private static Collection<String> createDataNodeStrings(final
Collection<DataNode> dataNodes) {
diff --git
a/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManager.java
b/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManager.java
index 551cf2c06b1..96ce015953b 100644
---
a/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManager.java
+++
b/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManager.java
@@ -25,7 +25,6 @@ import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereTable;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereView;
-import
org.apache.shardingsphere.infra.rule.attribute.datanode.MutableDataNodeRuleAttribute;
import org.apache.shardingsphere.infra.rule.scope.GlobalRule;
import
org.apache.shardingsphere.infra.rule.scope.GlobalRule.GlobalRuleChangedType;
import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
@@ -193,7 +192,7 @@ public final class DatabaseMetaDataManager {
} else {
database.getSchema(schemaName).removeView(toBeDroppedTableOrViewName);
}
-
database.getRuleMetaData().getAttributes(MutableDataNodeRuleAttribute.class).forEach(each
-> each.remove(schemaName, toBeDroppedTableOrViewName));
+ database.removeDataNode(schemaName, toBeDroppedTableOrViewName);
metaDataContexts.getMetaData().getGlobalRuleMetaData().getRules()
.forEach(each -> ((GlobalRule)
each).refresh(metaDataContexts.getMetaData().getAllDatabases(),
GlobalRuleChangedType.SCHEMA_CHANGED));
}
diff --git
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManagerTest.java
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManagerTest.java
index b58c30b1ee4..534f1cb5f08 100644
---
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManagerTest.java
+++
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/database/DatabaseMetaDataManagerTest.java
@@ -26,7 +26,6 @@ import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSp
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereTable;
import
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereView;
import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
-import
org.apache.shardingsphere.infra.rule.attribute.datanode.MutableDataNodeRuleAttribute;
import org.apache.shardingsphere.infra.rule.scope.GlobalRule;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
@@ -230,17 +229,17 @@ class DatabaseMetaDataManagerTest {
@Test
void assertDropTable() {
when(metaDataContexts.getMetaData().getDatabase("foo_db").getAllSchemas()).thenReturn(Collections.singleton(createToBeAlteredSchema()));
- mockMutableDataNodeRuleAttribute();
databaseMetaDataManager.dropTable("foo_db", "foo_schema", "foo_tbl");
assertFalse(metaDataContexts.getMetaData().getDatabase("foo_db").getSchema("foo_schema").containsTable("foo_tbl"));
+
verify(metaDataContexts.getMetaData().getDatabase("foo_db")).removeDataNode("foo_schema",
"foo_tbl");
}
@Test
void assertDropView() {
when(metaDataContexts.getMetaData().getDatabase("foo_db").getAllSchemas()).thenReturn(Collections.singleton(createToBeAlteredSchema()));
- mockMutableDataNodeRuleAttribute();
databaseMetaDataManager.dropView("foo_db", "foo_schema", "foo_view");
assertFalse(metaDataContexts.getMetaData().getDatabase("foo_db").getSchema("foo_schema").containsView("foo_view"));
+
verify(metaDataContexts.getMetaData().getDatabase("foo_db")).removeDataNode("foo_schema",
"foo_view");
}
@Test
@@ -257,11 +256,6 @@ class DatabaseMetaDataManagerTest {
verify(metaDataContexts.getMetaData().getDatabase("foo_db"),
never()).getSchema(anyString());
}
- private void mockMutableDataNodeRuleAttribute() {
- MutableDataNodeRuleAttribute attribute =
mock(MutableDataNodeRuleAttribute.class);
-
when(metaDataContexts.getMetaData().getDatabase("foo_db").getRuleMetaData().getAttributes(MutableDataNodeRuleAttribute.class)).thenReturn(Collections.singleton(attribute));
- }
-
private ShardingSphereSchema createToBeAlteredSchema() {
ShardingSphereTable beforeChangedTable = new
ShardingSphereTable("foo_tbl", Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
ShardingSphereView beforeChangedView = new
ShardingSphereView("foo_view", "");
diff --git
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/refresher/pushdown/type/view/CreateViewPushDownMetaDataRefresherTest.java
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/refresher/pushdown/type/view/CreateViewPushDownMetaDataRefresherTest.java
index 1e4f99adc09..3084f9e8015 100644
---
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/refresher/pushdown/type/view/CreateViewPushDownMetaDataRefresherTest.java
+++
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/refresher/pushdown/type/view/CreateViewPushDownMetaDataRefresherTest.java
@@ -168,6 +168,16 @@ class CreateViewPushDownMetaDataRefresherTest {
private final DataNode dataNode;
+ @Override
+ public ShardingSphereRule copyRuleAndPut(final String dataSourceName,
final String schemaName, final String tableName) {
+ return new SingleTableRule(new DataNode(dataSourceName,
schemaName, tableName));
+ }
+
+ @Override
+ public ShardingSphereRule copyRuleAndRemove(final String schemaName,
final String tableName) {
+ return new SingleTableRule(dataNode);
+ }
+
@Override
public void put(final String dataSourceName, final String schemaName,
final String tableName) {
}
@@ -182,7 +192,7 @@ class CreateViewPushDownMetaDataRefresherTest {
@Override
public Optional<DataNode> findTableDataNode(final String schemaName,
final String tableName) {
- return Optional.of(dataNode);
+ return dataNode.getSchemaName().equals(schemaName) &&
dataNode.getTableName().equals(tableName) ? Optional.of(dataNode) :
Optional.empty();
}
@Override