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.cache.route;
19  
20  import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
21  import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
22  import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
23  import org.apache.shardingsphere.infra.route.context.RouteContext;
24  import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
25  import org.apache.shardingsphere.infra.session.query.QueryContext;
26  import org.apache.shardingsphere.sharding.cache.ShardingCache;
27  import org.apache.shardingsphere.sharding.cache.checker.ShardingRouteCacheableCheckResult;
28  import org.apache.shardingsphere.sharding.cache.route.cache.ShardingRouteCacheKey;
29  import org.apache.shardingsphere.sharding.cache.route.cache.ShardingRouteCacheValue;
30  import org.apache.shardingsphere.sharding.rule.ShardingRule;
31  
32  import java.util.ArrayList;
33  import java.util.List;
34  import java.util.Optional;
35  
36  /**
37   * Cached sharding SQL router.
38   */
39  public final class CachedShardingSQLRouter {
40      
41      /**
42       * Find {@link RouteContext} from cache or calculate and try caching.
43       *
44       * @param originSQLRouter origin SQL router
45       * @param queryContext query context
46       * @param globalRuleMetaData global rule meta data
47       * @param database database
48       * @param shardingCache sharding cache
49       * @param props configuration properties
50       * @param connectionContext connection context
51       * @return route context
52       */
53      public Optional<RouteContext> loadRouteContext(final OriginSQLRouter originSQLRouter, final QueryContext queryContext, final RuleMetaData globalRuleMetaData,
54                                                     final ShardingSphereDatabase database, final ShardingCache shardingCache, final ConfigurationProperties props,
55                                                     final ConnectionContext connectionContext) {
56          if (queryContext.getSql().length() > shardingCache.getConfiguration().getAllowedMaxSqlLength()) {
57              return Optional.empty();
58          }
59          ShardingRouteCacheableCheckResult cacheableCheckResult = shardingCache.getRouteCacheableChecker().check(database, queryContext);
60          if (!cacheableCheckResult.isProbablyCacheable()) {
61              return Optional.empty();
62          }
63          List<Object> shardingConditionParams = new ArrayList<>(cacheableCheckResult.getShardingConditionParameterMarkerIndexes().size());
64          for (int each : cacheableCheckResult.getShardingConditionParameterMarkerIndexes()) {
65              if (each >= queryContext.getParameters().size()) {
66                  return Optional.empty();
67              }
68              shardingConditionParams.add(queryContext.getParameters().get(each));
69          }
70          Optional<RouteContext> cachedResult = shardingCache.getRouteCache().get(new ShardingRouteCacheKey(queryContext.getSql(), shardingConditionParams))
71                  .flatMap(ShardingRouteCacheValue::getCachedRouteContext);
72          RouteContext result = cachedResult.orElseGet(
73                  () -> originSQLRouter.createRouteContext(queryContext, globalRuleMetaData, database, shardingCache.getShardingRule(), props, connectionContext));
74          if (!cachedResult.isPresent() && hitOneShardOnly(result)) {
75              shardingCache.getRouteCache().put(new ShardingRouteCacheKey(queryContext.getSql(), shardingConditionParams), new ShardingRouteCacheValue(result));
76          }
77          return Optional.of(result);
78      }
79      
80      private boolean hitOneShardOnly(final RouteContext routeContext) {
81          return 1 == routeContext.getRouteUnits().size() && 1 == routeContext.getRouteUnits().iterator().next().getTableMappers().size()
82                  && 1 == routeContext.getOriginalDataNodes().size() && 1 == routeContext.getOriginalDataNodes().iterator().next().size();
83      }
84      
85      @FunctionalInterface
86      public interface OriginSQLRouter {
87          
88          /**
89           * Create route context.
90           *
91           * @param queryContext query context
92           * @param globalRuleMetaData global rule meta data
93           * @param database database
94           * @param rule rule
95           * @param props configuration properties
96           * @param connectionContext connection context
97           * @return route context
98           */
99          RouteContext createRouteContext(QueryContext queryContext, RuleMetaData globalRuleMetaData, ShardingSphereDatabase database, ShardingRule rule,
100                                         ConfigurationProperties props, ConnectionContext connectionContext);
101     }
102 }