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.DuplicateSharingActualDataNodeException;
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.Set;
48  import java.util.stream.Collectors;
49  
50  @RequiredArgsConstructor
51  public class ShardingRuleChecker {
52      
53      private final ShardingRule shardingRule;
54      
55      /**
56       * Check sharding rule.
57       *
58       * @param ruleConfig sharding rule configuration
59       */
60      public void check(final ShardingRuleConfiguration ruleConfig) {
61          checkUniqueActualDataNodesInTableRules();
62          checkBindingTableConfiguration(ruleConfig);
63          checkInlineShardingAlgorithmsInTableRules();
64      }
65      
66      private void checkUniqueActualDataNodesInTableRules() {
67          Set<DataNode> uniqueActualDataNodes = new HashSet<>(shardingRule.getShardingTables().size(), 1L);
68          shardingRule.getShardingTables().forEach((key, value) -> {
69              DataNode sampleActualDataNode = value.getActualDataNodes().iterator().next();
70              ShardingSpherePreconditions.checkState(!uniqueActualDataNodes.contains(sampleActualDataNode),
71                      () -> new DuplicateSharingActualDataNodeException(key, sampleActualDataNode.getDataSourceName(), sampleActualDataNode.getTableName()));
72              uniqueActualDataNodes.add(sampleActualDataNode);
73          });
74      }
75      
76      private void checkBindingTableConfiguration(final ShardingRuleConfiguration ruleConfig) {
77          ShardingSpherePreconditions.checkState(
78                  isValidBindingTableConfiguration(shardingRule.getShardingTables(), new BindingTableCheckedConfiguration(shardingRule.getDataSourceNames(), shardingRule.getShardingAlgorithms(),
79                          ruleConfig.getBindingTableGroups(), shardingRule.getDefaultDatabaseShardingStrategyConfig(), shardingRule.getDefaultTableShardingStrategyConfig(),
80                          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         if (null != result) {
108             return result;
109         }
110         throw new ShardingTableRuleNotFoundException(Collections.singleton(logicTableName));
111     }
112     
113     private boolean isValidActualDataSourceName(final ShardingTable sampleShardingTable, final ShardingTable shardingTable) {
114         return sampleShardingTable.getActualDataSourceNames().equals(shardingTable.getActualDataSourceNames());
115     }
116     
117     private boolean isValidActualTableName(final ShardingTable sampleShardingTable, final ShardingTable shardingTable) {
118         for (String each : sampleShardingTable.getActualDataSourceNames()) {
119             Collection<String> sampleActualTableNames =
120                     sampleShardingTable.getActualTableNames(each).stream().map(actualTableName -> actualTableName.replace(sampleShardingTable.getTableDataNode().getPrefix(), ""))
121                             .collect(Collectors.toSet());
122             Collection<String> actualTableNames =
123                     shardingTable.getActualTableNames(each).stream().map(optional -> optional.replace(shardingTable.getTableDataNode().getPrefix(), "")).collect(Collectors.toSet());
124             if (!sampleActualTableNames.equals(actualTableNames)) {
125                 return false;
126             }
127         }
128         return true;
129     }
130     
131     private boolean isBindingShardingAlgorithm(final ShardingTable sampleShardingTable, final ShardingTable shardingTable, final boolean databaseAlgorithm,
132                                                final BindingTableCheckedConfiguration checkedConfig) {
133         return getAlgorithmExpression(sampleShardingTable, databaseAlgorithm, checkedConfig).equals(getAlgorithmExpression(shardingTable, databaseAlgorithm, checkedConfig));
134     }
135     
136     private Optional<String> getAlgorithmExpression(final ShardingTable shardingTable, final boolean databaseAlgorithm, final BindingTableCheckedConfiguration checkedConfig) {
137         ShardingStrategyConfiguration shardingStrategyConfig = databaseAlgorithm
138                 ? shardingRule.getDatabaseShardingStrategyConfiguration(shardingTable)
139                 : shardingRule.getTableShardingStrategyConfiguration(shardingTable);
140         ShardingAlgorithm shardingAlgorithm = checkedConfig.getShardingAlgorithms().get(shardingStrategyConfig.getShardingAlgorithmName());
141         String dataNodePrefix = databaseAlgorithm ? shardingTable.getDataSourceDataNode().getPrefix() : shardingTable.getTableDataNode().getPrefix();
142         String shardingColumn = getShardingColumn(shardingStrategyConfig, shardingRule.getDefaultShardingColumn());
143         return null == shardingAlgorithm ? Optional.empty() : shardingAlgorithm.getAlgorithmStructure(dataNodePrefix, shardingColumn);
144     }
145     
146     private String getShardingColumn(final ShardingStrategyConfiguration shardingStrategyConfig, final String defaultShardingColumn) {
147         String shardingColumn = defaultShardingColumn;
148         if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
149             shardingColumn = ((ComplexShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumns();
150         }
151         if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
152             shardingColumn = ((StandardShardingStrategyConfiguration) shardingStrategyConfig).getShardingColumn();
153         }
154         return null == shardingColumn ? "" : shardingColumn;
155     }
156     
157     private void checkInlineShardingAlgorithmsInTableRules() {
158         shardingRule.getShardingTables().forEach((key, value) -> {
159             validateInlineShardingAlgorithm(value, shardingRule.getTableShardingStrategyConfiguration(value), value.getTableDataNode());
160             validateInlineShardingAlgorithm(value, shardingRule.getDatabaseShardingStrategyConfiguration(value), value.getDataSourceDataNode());
161         });
162     }
163     
164     private void validateInlineShardingAlgorithm(final ShardingTable shardingTable, final ShardingStrategyConfiguration shardingStrategy, final DataNodeInfo dataNodeInfo) {
165         if (null == shardingStrategy) {
166             return;
167         }
168         ShardingAlgorithm shardingAlgorithm = shardingRule.getShardingAlgorithms().get(shardingStrategy.getShardingAlgorithmName());
169         if (shardingAlgorithm instanceof InlineShardingAlgorithm) {
170             String shardingColumn = null == ((StandardShardingStrategyConfiguration) shardingStrategy).getShardingColumn() ? shardingRule.getDefaultShardingColumn()
171                     : ((StandardShardingStrategyConfiguration) shardingStrategy).getShardingColumn();
172             String result = null;
173             try {
174                 result = ((InlineShardingAlgorithm) shardingAlgorithm).doSharding(Collections.emptySet(), new PreciseShardingValue<>(shardingTable.getLogicTable(), shardingColumn, dataNodeInfo, 1));
175                 // CHECKSTYLE:OFF
176             } catch (final Exception ignored) {
177                 // CHECKSTYLE:ON
178             }
179             ShardingSpherePreconditions.checkState(null == result || result.startsWith(dataNodeInfo.getPrefix()),
180                     () -> new AlgorithmInitializationException(shardingAlgorithm, "`%s` sharding algorithm configuration of `%s` does not match the actual data nodes",
181                             shardingStrategy.getShardingAlgorithmName(), shardingTable.getLogicTable()));
182         }
183     }
184 }