From 043963d1415bcb98a12ab4309f4d71cb199a18a0 Mon Sep 17 00:00:00 2001 From: Liang Zhang Date: Wed, 8 May 2024 00:51:06 +0800 Subject: [PATCH] Add PartialRuleUpdateSupported feature and try in encrypt rule first (#31162) * Add PartialRuleUpdateSupported feature and try in encrypt rule first * Add PartialRuleUpdateSupported feature and try in encrypt rule first --- .../encrypt/rule/EncryptRule.java | 73 +++++++++++++++++-- .../rule/PartialRuleUpdateSupported.java | 44 +++++++++++ .../context/ConfigurationContextManager.java | 35 +++++---- 3 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 infra/common/src/main/java/org/apache/shardingsphere/infra/rule/PartialRuleUpdateSupported.java diff --git a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rule/EncryptRule.java b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rule/EncryptRule.java index e2e5f4f6f9727..ea67c80bc6066 100644 --- a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rule/EncryptRule.java +++ b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/rule/EncryptRule.java @@ -17,6 +17,7 @@ package org.apache.shardingsphere.encrypt.rule; +import com.google.common.base.Preconditions; import lombok.Getter; import org.apache.shardingsphere.encrypt.api.config.EncryptRuleConfiguration; import org.apache.shardingsphere.encrypt.api.config.rule.EncryptColumnRuleConfiguration; @@ -25,27 +26,31 @@ import org.apache.shardingsphere.encrypt.exception.metadata.MismatchedEncryptAlgorithmTypeException; import org.apache.shardingsphere.encrypt.rule.attribute.EncryptTableMapperRuleAttribute; import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm; +import org.apache.shardingsphere.infra.algorithm.core.config.AlgorithmConfiguration; import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions; +import org.apache.shardingsphere.infra.rule.PartialRuleUpdateSupported; import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes; import org.apache.shardingsphere.infra.rule.scope.DatabaseRule; import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader; import java.util.Collection; -import java.util.LinkedHashMap; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** * Encrypt rule. */ -public final class EncryptRule implements DatabaseRule { +public final class EncryptRule implements DatabaseRule, PartialRuleUpdateSupported { private final String databaseName; - @Getter - private final EncryptRuleConfiguration configuration; + private final AtomicReference ruleConfig = new AtomicReference<>(); private final Map tables; @@ -54,8 +59,8 @@ public final class EncryptRule implements DatabaseRule { public EncryptRule(final String databaseName, final EncryptRuleConfiguration ruleConfig) { this.databaseName = databaseName; - configuration = ruleConfig; - tables = new LinkedHashMap<>(); + this.ruleConfig.set(ruleConfig); + tables = new ConcurrentHashMap<>(); Map encryptors = ruleConfig.getEncryptors().entrySet().stream() .collect(Collectors.toMap(Entry::getKey, entry -> TypedSPILoader.getService(EncryptAlgorithm.class, entry.getValue().getType(), entry.getValue().getProps()))); for (EncryptTableRuleConfiguration each : ruleConfig.getTables()) { @@ -107,4 +112,60 @@ public EncryptTable getEncryptTable(final String tableName) { ShardingSpherePreconditions.checkState(encryptTable.isPresent(), () -> new EncryptTableNotFoundException(tableName)); return encryptTable.get(); } + + @Override + public EncryptRuleConfiguration getConfiguration() { + return ruleConfig.get(); + } + + @Override + public void updateConfiguration(final EncryptRuleConfiguration toBeUpdatedRuleConfig) { + ruleConfig.set(toBeUpdatedRuleConfig); + } + + @Override + public boolean partialUpdateRule(final EncryptRuleConfiguration toBeUpdatedRuleConfig) { + Collection toBeAddedTableNames = toBeUpdatedRuleConfig.getTables().stream().map(EncryptTableRuleConfiguration::getName).collect(Collectors.toList()); + toBeAddedTableNames.removeAll(tables.keySet()); + if (!toBeAddedTableNames.isEmpty()) { + for (String each : toBeAddedTableNames) { + EncryptTableRuleConfiguration tableRuleConfig = getEncryptTableRuleConfiguration(each, toBeUpdatedRuleConfig); + Map encryptorConfigs = getEncryptorConfigurations(tableRuleConfig, toBeUpdatedRuleConfig.getEncryptors()); + Map encryptors = encryptorConfigs.entrySet().stream() + .collect(Collectors.toMap(Entry::getKey, entry -> TypedSPILoader.getService(EncryptAlgorithm.class, entry.getValue().getType(), entry.getValue().getProps()))); + tableRuleConfig.getColumns().forEach(columnRuleConfig -> checkEncryptorType(columnRuleConfig, encryptors)); + tables.put(each.toLowerCase(), new EncryptTable(tableRuleConfig, encryptors)); + } + return true; + } + Collection toBeRemovedTableNames = new HashSet<>(tables.keySet()); + toBeRemovedTableNames.removeAll(toBeUpdatedRuleConfig.getTables().stream().map(EncryptTableRuleConfiguration::getName).collect(Collectors.toList())); + if (!toBeRemovedTableNames.isEmpty()) { + toBeRemovedTableNames.stream().map(String::toLowerCase).forEach(tables::remove); + return true; + } + // TODO Process update table + // TODO Process update encryptors + return false; + } + + private EncryptTableRuleConfiguration getEncryptTableRuleConfiguration(final String tableName, final EncryptRuleConfiguration toBeUpdatedRuleConfig) { + Optional result = toBeUpdatedRuleConfig.getTables().stream().filter(table -> table.getName().equals(tableName)).findFirst(); + Preconditions.checkState(result.isPresent()); + return result.get(); + } + + private Map getEncryptorConfigurations(final EncryptTableRuleConfiguration tableRuleConfig, final Map encryptors) { + Map result = new HashMap<>(encryptors.size(), 1F); + for (EncryptColumnRuleConfiguration each : tableRuleConfig.getColumns()) { + result.put(each.getCipher().getEncryptorName(), encryptors.get(each.getCipher().getEncryptorName())); + if (each.getAssistedQuery().isPresent()) { + result.put(each.getAssistedQuery().get().getEncryptorName(), encryptors.get(each.getAssistedQuery().get().getEncryptorName())); + } + if (each.getLikeQuery().isPresent()) { + result.put(each.getLikeQuery().get().getEncryptorName(), encryptors.get(each.getLikeQuery().get().getEncryptorName())); + } + } + return result; + } } diff --git a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/PartialRuleUpdateSupported.java b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/PartialRuleUpdateSupported.java new file mode 100644 index 0000000000000..48b9773f997c0 --- /dev/null +++ b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/PartialRuleUpdateSupported.java @@ -0,0 +1,44 @@ +/* + * 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.infra.rule; + +import org.apache.shardingsphere.infra.config.rule.RuleConfiguration; + +/** + * Partial rule update supported. + * + * @param rule configuration type + */ +public interface PartialRuleUpdateSupported { + + /** + * Update rule configuration. + * + * @param toBeUpdatedRuleConfig to be updated configuration + */ + void updateConfiguration(T toBeUpdatedRuleConfig); + + /** + * Partial update. + * + * @param toBeUpdatedRuleConfig to be updated configuration + * @return update success or not + */ + // TODO remove return value when support alter + boolean partialUpdateRule(T toBeUpdatedRuleConfig); +} diff --git a/mode/core/src/main/java/org/apache/shardingsphere/mode/manager/context/ConfigurationContextManager.java b/mode/core/src/main/java/org/apache/shardingsphere/mode/manager/context/ConfigurationContextManager.java index 32adc980d528e..f79844c401354 100644 --- a/mode/core/src/main/java/org/apache/shardingsphere/mode/manager/context/ConfigurationContextManager.java +++ b/mode/core/src/main/java/org/apache/shardingsphere/mode/manager/context/ConfigurationContextManager.java @@ -35,6 +35,7 @@ import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData; import org.apache.shardingsphere.infra.metadata.database.schema.manager.GenericSchemaManager; import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema; +import org.apache.shardingsphere.infra.rule.PartialRuleUpdateSupported; import org.apache.shardingsphere.infra.rule.ShardingSphereRule; import org.apache.shardingsphere.infra.rule.builder.database.DatabaseRulesBuilder; import org.apache.shardingsphere.infra.rule.builder.global.GlobalRulesBuilder; @@ -154,15 +155,18 @@ private void persistSchemaMetaData(final String databaseName, final MetaDataCont * @param databaseName database name * @param ruleConfig rule configurations */ + @SuppressWarnings({"unchecked", "rawtypes"}) public synchronized void alterRuleConfiguration(final String databaseName, final RuleConfiguration ruleConfig) { - // TODO add feature for partly refresh - // 1. Judge if impl partly interface - // 2. compare diff with current and ruleConfig - // 3. Do partly refresh - // 4. return + ShardingSphereDatabase database = metaDataContexts.get().getMetaData().getDatabase(databaseName); + Collection rules = new LinkedList<>(database.getRuleMetaData().getRules()); + Optional toBeChangedRule = rules.stream().filter(each -> each.getConfiguration().getClass().equals(ruleConfig.getClass())).findFirst(); + if (toBeChangedRule.isPresent() && toBeChangedRule.get() instanceof PartialRuleUpdateSupported) { + if (((PartialRuleUpdateSupported) toBeChangedRule.get()).partialUpdateRule(ruleConfig)) { + ((PartialRuleUpdateSupported) toBeChangedRule.get()).updateConfiguration(ruleConfig); + return; + } + } try { - ShardingSphereDatabase database = metaDataContexts.get().getMetaData().getDatabase(databaseName); - Collection rules = new LinkedList<>(database.getRuleMetaData().getRules()); rules.removeIf(each -> each.getConfiguration().getClass().isAssignableFrom(ruleConfig.getClass())); rules.addAll(DatabaseRulesBuilder.build(databaseName, database.getProtocolType(), database.getResourceMetaData().getStorageUnits().entrySet().stream() @@ -180,15 +184,18 @@ public synchronized void alterRuleConfiguration(final String databaseName, final * @param databaseName database name * @param ruleConfig rule configurations */ + @SuppressWarnings({"unchecked", "rawtypes"}) public synchronized void dropRuleConfiguration(final String databaseName, final RuleConfiguration ruleConfig) { - // TODO add feature for partly refresh - // 1. Judge if impl partly interface - // 2. compare diff with current and ruleConfig - // 3. Remove to be removed partial config - // 4. return + ShardingSphereDatabase database = metaDataContexts.get().getMetaData().getDatabase(databaseName); + Collection rules = new LinkedList<>(database.getRuleMetaData().getRules()); + Optional toBeChangedRule = rules.stream().filter(each -> each.getConfiguration().getClass().equals(ruleConfig.getClass())).findFirst(); + if (toBeChangedRule.isPresent() && toBeChangedRule.get() instanceof PartialRuleUpdateSupported) { + if (((PartialRuleUpdateSupported) toBeChangedRule.get()).partialUpdateRule(ruleConfig)) { + ((PartialRuleUpdateSupported) toBeChangedRule.get()).updateConfiguration(ruleConfig); + return; + } + } try { - ShardingSphereDatabase database = metaDataContexts.get().getMetaData().getDatabase(databaseName); - Collection rules = new LinkedList<>(database.getRuleMetaData().getRules()); rules.removeIf(each -> each.getConfiguration().getClass().isAssignableFrom(ruleConfig.getClass())); if (!((DatabaseRuleConfiguration) ruleConfig).isEmpty()) { rules.addAll(DatabaseRulesBuilder.build(databaseName, database.getProtocolType(),