View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.sharding.rule.checker;
19  
20  import com.google.common.base.Splitter;
21  import lombok.RequiredArgsConstructor;
22  import org.apache.shardingsphere.infra.algorithm.core.exception.AlgorithmInitializationException;
23  import org.apache.shardingsphere.infra.datanode.DataNode;
24  import org.apache.shardingsphere.infra.datanode.DataNodeInfo;
25  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
26  import org.apache.shardingsphere.sharding.algorithm.sharding.inline.InlineShardingAlgorithm;
27  import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
28  import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableReferenceRuleConfiguration;
29  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ComplexShardingStrategyConfiguration;
30  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.ShardingStrategyConfiguration;
31  import org.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;
32  import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
33  import org.apache.shardingsphere.sharding.exception.metadata.DuplicateShardingActualDataNodeException;
34  import org.apache.shardingsphere.sharding.exception.metadata.InvalidBindingTablesException;
35  import org.apache.shardingsphere.sharding.exception.metadata.ShardingTableRuleNotFoundException;
36  import org.apache.shardingsphere.sharding.rule.BindingTableCheckedConfiguration;
37  import org.apache.shardingsphere.sharding.rule.ShardingRule;
38  import org.apache.shardingsphere.sharding.rule.ShardingTable;
39  import org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
40  
41  import java.util.Collection;
42  import java.util.Collections;
43  import java.util.HashSet;
44  import java.util.Iterator;
45  import java.util.Map;
46  import java.util.Optional;
47  import java.util.stream.Collectors;
48  
49  @RequiredArgsConstructor
50  public class ShardingRuleChecker {
51      
52      private final ShardingRule shardingRule;
53      
54      /**
55       * Check sharding rule.
56       *
57       * @param ruleConfig sharding rule configuration
58       */
59      public void check(final ShardingRuleConfiguration ruleConfig) {
60          checkUniqueActualDataNodesInTableRules();
61          checkBindingTableConfiguration(ruleConfig);
62          checkInlineShardingAlgorithmsInTableRules();
63      }
64      
65      private void checkUniqueActualDataNodesInTableRules() {
66          Collection<DataNode> uniqueActualDataNodes = new HashSet<>(shardingRule.getShardingTables().size(), 1F);
67          shardingRule.getShardingTables().forEach((key, value) -> checkUniqueActualDataNodes(uniqueActualDataNodes, key, value.getActualDataNodes().iterator().next()));
68      }
69      
70      private void checkUniqueActualDataNodes(final Collection<DataNode> uniqueActualDataNodes, final String logicTable, final DataNode sampleActualDataNode) {
71          ShardingSpherePreconditions.checkNotContains(uniqueActualDataNodes, sampleActualDataNode,
72                  () -> new DuplicateShardingActualDataNodeException(logicTable, sampleActualDataNode.getDataSourceName(), sampleActualDataNode.getTableName()));
73          uniqueActualDataNodes.add(sampleActualDataNode);
74      }
75      
76      private void checkBindingTableConfiguration(final ShardingRuleConfiguration ruleConfig) {
77          ShardingSpherePreconditions.checkState(
78                  isValidBindingTableConfiguration(shardingRule.getShardingTables(),
79                          new BindingTableCheckedConfiguration(shardingRule.getDataSourceNames(), shardingRule.getShardingAlgorithms(), ruleConfig.getBindingTableGroups(),
80                                  shardingRule.getDefaultDatabaseShardingStrategyConfig(), shardingRule.getDefaultTableShardingStrategyConfig(), shardingRule.getDefaultShardingColumn())),
81                  InvalidBindingTablesException::new);
82      }
83      
84      private boolean isValidBindingTableConfiguration(final Map<String, ShardingTable> shardingTables, final BindingTableCheckedConfiguration checkedConfig) {
85          for (ShardingTableReferenceRuleConfiguration each : checkedConfig.getBindingTableGroups()) {
86              Collection<String> bindingTables = Splitter.on(",").trimResults().splitToList(each.getReference());
87              if (bindingTables.size() <= 1) {
88                  continue;
89              }
90              Iterator<String> iterator = bindingTables.iterator();
91              ShardingTable sampleShardingTable = getShardingTable(iterator.next(), shardingTables);
92              while (iterator.hasNext()) {
93                  ShardingTable shardingTable = getShardingTable(iterator.next(), shardingTables);
94                  if (!isValidActualDataSourceName(sampleShardingTable, shardingTable) || !isValidActualTableName(sampleShardingTable, shardingTable)) {
95                      return false;
96                  }
97                  if (!isBindingShardingAlgorithm(sampleShardingTable, shardingTable, true, checkedConfig) || !isBindingShardingAlgorithm(sampleShardingTable, shardingTable, false, checkedConfig)) {
98                      return false;
99                  }
100             }
101         }
102         return true;
103     }
104     
105     private ShardingTable getShardingTable(final String logicTableName, final Map<String, ShardingTable> shardingTables) {
106         ShardingTable result = shardingTables.get(logicTableName);
107         ShardingSpherePreconditions.checkNotNull(result, () -> new ShardingTableRuleNotFoundException(Collections.singleton(logicTableName)));
108         return result;
109     }
110     
111     private boolean isValidActualDataSourceName(final ShardingTable sampleShardingTable, final ShardingTable shardingTable) {
112         return sampleShardingTable.getActualDataSourceNames().equals(shardingTable.getActualDataSourceNames());
113     }
114     
115     private boolean isValidActualTableName(final ShardingTable sampleShardingTable, final ShardingTable shardingTable) {
116         for (String each : sampleShardingTable.getActualDataSourceNames()) {
117             Collection<String> sampleActualTableNames = sampleShardingTable.getActualTableNames(each).stream()
118                     .map(actualTableName -> actualTableName.replace(sampleShardingTable.getTableDataNode().getPrefix(), "")).collect(Collectors.toSet());
119             Collection<String> actualTableNames =
120                     shardingTable.getActualTableNames(each).stream().map(optional -> optional.replace(shardingTable.getTableDataNode().getPrefix(), "")).collect(Collectors.toSet());
121             if (!sampleActualTableNames.equals(actualTableNames)) {
122                 return false;
123             }
124         }
125         return true;
126     }
127     
128     private boolean isBindingShardingAlgorithm(final ShardingTable sampleShardingTable, final ShardingTable shardingTable, final boolean databaseAlgorithm,
129                                                final BindingTableCheckedConfiguration checkedConfig) {
130         return getAlgorithmExpression(sampleShardingTable, databaseAlgorithm, checkedConfig).equals(getAlgorithmExpression(shardingTable, databaseAlgorithm, checkedConfig));
131     }
132     
133     private Optional<String> getAlgorithmExpression(final ShardingTable shardingTable, final boolean databaseAlgorithm, final BindingTableCheckedConfiguration checkedConfig) {
134         ShardingStrategyConfiguration shardingStrategyConfig = databaseAlgorithm
135                 ? shardingRule.getDatabaseShardingStrategyConfiguration(shardingTable)
136                 : shardingRule.getTableShardingStrategyConfiguration(shardingTable);
137         ShardingAlgorithm shardingAlgorithm = checkedConfig.getShardingAlgorithms().get(shardingStrategyConfig.getShardingAlgorithmName());
138         String dataNodePrefix = databaseAlgorithm ? shardingTable.getDataSourceDataNode().getPrefix() : shardingTable.getTableDataNode().getPrefix();
139         String shardingColumn = getShardingColumn(shardingStrategyConfig, shardingRule.getDefaultShardingColumn());
140         return null == shardingAlgorithm ? Optional.empty() : shardingAlgorithm.getAlgorithmStructure(dataNodePrefix, shardingColumn);
141     }
142     
143     private String getShardingColumn(final ShardingStrategyConfiguration shardingStrategyConfig, final String defaultShardingColumn) {
144         String shardingColumn = defaultShardingColumn;
145         if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
146             shardingColumn = ((ComplexShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumns();
147         }
148         if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
149             shardingColumn = ((StandardShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumn();
150         }
151         return null == shardingColumn ? "" : shardingColumn;
152     }
153     
154     private void checkInlineShardingAlgorithmsInTableRules() {
155         shardingRule.getShardingTables().forEach((key, value) -> {
156             validateInlineShardingAlgorithm(value, shardingRule.getTableShardingStrategyConfiguration(value), value.getTableDataNode());
157             validateInlineShardingAlgorithm(value, shardingRule.getDatabaseShardingStrategyConfiguration(value), value.getDataSourceDataNode());
158         });
159     }
160     
161     private void validateInlineShardingAlgorithm(final ShardingTable shardingTable, final ShardingStrategyConfiguration shardingStrategy, final DataNodeInfo dataNodeInfo) {
162         if (null == shardingStrategy) {
163             return;
164         }
165         ShardingAlgorithm shardingAlgorithm = shardingRule.getShardingAlgorithms().get(shardingStrategy.getShardingAlgorithmName());
166         if (shardingAlgorithm instanceof InlineShardingAlgorithm) {
167             String shardingColumn = null == ((StandardShardingStrategyConfiguration) shardingStrategy).getShardingColumn() ? shardingRule.getDefaultShardingColumn()
168                     : ((StandardShardingStrategyConfiguration) shardingStrategy).getShardingColumn();
169             String result = null;
170             try {
171                 result = ((InlineShardingAlgorithm) shardingAlgorithm).doSharding(Collections.emptySet(), new PreciseShardingValue<>(shardingTable.getLogicTable(), shardingColumn, dataNodeInfo, 1));
172                 // CHECKSTYLE:OFF
173             } catch (final Exception ignored) {
174                 // CHECKSTYLE:ON
175             }
176             ShardingSpherePreconditions.checkState(null == result || result.startsWith(dataNodeInfo.getPrefix()),
177                     () -> new AlgorithmInitializationException(shardingAlgorithm, "`%s` sharding algorithm configuration of `%s` does not match the actual data nodes",
178                             shardingStrategy.getShardingAlgorithmName(), shardingTable.getLogicTable()));
179         }
180     }
181     
182     /**
183      * Check to be added data nodes.
184      *
185      * @param toBeAddedDataNodes to be added data nodes
186      * @param isAlteration is alteration
187      */
188     public void checkToBeAddedDataNodes(final Map<String, Collection<DataNode>> toBeAddedDataNodes, final boolean isAlteration) {
189         Collection<DataNode> uniqueActualDataNodes = new HashSet<>(shardingRule.getShardingTables().size() + toBeAddedDataNodes.size(), 1F);
190         shardingRule.getShardingTables().forEach((key, value) -> {
191             if (isAlteration && toBeAddedDataNodes.containsKey(key)) {
192                 return;
193             }
194             checkUniqueActualDataNodes(uniqueActualDataNodes, key, value.getActualDataNodes().iterator().next());
195         });
196         toBeAddedDataNodes.forEach((key, value) -> checkUniqueActualDataNodes(uniqueActualDataNodes, key, value.iterator().next()));
197     }
198 }