You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2022/07/14 14:14:58 UTC

[shardingsphere] branch master updated: Provide support for JSR-310 Year and Month in IntervalShardingAlgorithm (#19139)

This is an automated email from the ASF dual-hosted git repository.

zhangliang 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 58dced0e9ca Provide support for JSR-310 Year and Month in IntervalShardingAlgorithm (#19139)
58dced0e9ca is described below

commit 58dced0e9ca1420b389ffef6bbb7c626254029a9
Author: Ling Hengqian <li...@outlook.com>
AuthorDate: Thu Jul 14 22:14:46 2022 +0800

    Provide support for JSR-310 Year and Month in IntervalShardingAlgorithm (#19139)
    
    * Provide support for JSR-310 Year and Month in IntervalShardingAlgorithm
    
    * fix checkstyle.
    
    * update doc for Interval Sharding Algorithm.
    
    * Provide support for java.time.YearMonth in IntervalShardingAlgorithm.
    
    * fix checkstyle again.
---
 .../builtin-algorithm/sharding.cn.md               |   2 +
 .../builtin-algorithm/sharding.en.md               |   2 +
 .../datetime/IntervalShardingAlgorithm.java        | 132 +++++++++++++++++++--
 .../datetime/IntervalShardingAlgorithmTest.java    |  93 +++++++++++++++
 4 files changed, 216 insertions(+), 13 deletions(-)

diff --git a/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.cn.md b/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.cn.md
index ab7644191c1..89ac3d0edf3 100644
--- a/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.cn.md
+++ b/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.cn.md
@@ -86,6 +86,8 @@ Apache ShardingSphere 内置的标准分片算法实现类包括:
 
 #### 时间范围分片算法
 
+当传入的分片键为 `java.time.Instant` 时存在特例处理,其会携带上系统的时区信息后转化为 `datetime-pattern` 的字符串格式, 再进行下一步分片。
+
 类型:INTERVAL
 
 可配置属性:
diff --git a/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.en.md b/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.en.md
index 011222416fe..4bdf853174c 100644
--- a/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.en.md
+++ b/docs/document/content/user-manual/shardingsphere-jdbc/builtin-algorithm/sharding.en.md
@@ -89,6 +89,8 @@ Attributes:
 
 #### Interval Sharding Algorithm
 
+When the incoming sharding key is `java.time.Instant`, there is a special case, which will carry the time zone information of the system and convert it into the string format of `datetime-pattern`, and then proceed to the next sharding.
+
 Type: INTERVAL
 
 Attributes:
diff --git a/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithm.java b/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithm.java
index 892af21ce0c..e5afee2d6eb 100644
--- a/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithm.java
+++ b/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithm.java
@@ -26,13 +26,17 @@ import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingV
 import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
 import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
 
-import java.time.ZoneId;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.Month;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.TemporalAccessor;
 import java.time.temporal.TemporalQueries;
@@ -138,12 +142,21 @@ public final class IntervalShardingAlgorithm implements StandardShardingAlgorith
     
     private Collection<String> doSharding(final Collection<String> availableTargetNames, final Range<Comparable<?>> range) {
         TemporalAccessor calculateTime = dateTimeLower;
-        LocalDate queryToLocalDate = calculateTime.query(TemporalQueries.localDate());
-        LocalTime queryToLocalTime = calculateTime.query(TemporalQueries.localTime());
-        if (null == queryToLocalTime) {
-            return doShardingInLocalDate(availableTargetNames, range, calculateTime);
+        if (!calculateTime.isSupported(ChronoField.NANO_OF_DAY)) {
+            if (calculateTime.isSupported(ChronoField.EPOCH_DAY)) {
+                return doShardingInLocalDate(availableTargetNames, range, calculateTime);
+            }
+            if (calculateTime.isSupported(ChronoField.YEAR) && calculateTime.isSupported(ChronoField.MONTH_OF_YEAR)) {
+                return doShardingInYearMonth(availableTargetNames, range, calculateTime);
+            }
+            if (calculateTime.isSupported(ChronoField.YEAR)) {
+                return doShardingInYear(availableTargetNames, range, calculateTime);
+            }
+            if (calculateTime.isSupported(ChronoField.MONTH_OF_YEAR)) {
+                return doShardingInMonth(availableTargetNames, range, calculateTime);
+            }
         }
-        if (null == queryToLocalDate) {
+        if (!calculateTime.isSupported(ChronoField.EPOCH_DAY)) {
             return doShardingInLocalTime(availableTargetNames, range, calculateTime);
         }
         return doShardingInLocalDateTime(availableTargetNames, range, calculateTime);
@@ -190,6 +203,48 @@ public final class IntervalShardingAlgorithm implements StandardShardingAlgorith
         }
         return result;
     }
+
+    private Collection<String> doShardingInYear(final Collection<String> availableTargetNames, final Range<Comparable<?>> range, final TemporalAccessor calculateTime) {
+        Set<String> result = new HashSet<>();
+        Year dateTimeUpperAsYear = dateTimeUpper.query(Year::from);
+        Year dateTimeLowerAsYear = dateTimeLower.query(Year::from);
+        Year calculateTimeAsView = calculateTime.query(Year::from);
+        while (!calculateTimeAsView.isAfter(dateTimeUpperAsYear)) {
+            if (hasIntersection(Range.closedOpen(calculateTimeAsView, calculateTimeAsView.plus(stepAmount, stepUnit)), range, dateTimeLowerAsYear, dateTimeUpperAsYear)) {
+                result.addAll(getMatchedTables(calculateTimeAsView, availableTargetNames));
+            }
+            calculateTimeAsView = calculateTimeAsView.plus(stepAmount, stepUnit);
+        }
+        return result;
+    }
+
+    private Collection<String> doShardingInMonth(final Collection<String> availableTargetNames, final Range<Comparable<?>> range, final TemporalAccessor calculateTime) {
+        Set<String> result = new HashSet<>();
+        Month dateTimeUpperAsMonth = dateTimeUpper.query(Month::from);
+        Month dateTimeLowerAsMonth = dateTimeLower.query(Month::from);
+        Month calculateTimeAsView = calculateTime.query(Month::from);
+        while (!(calculateTimeAsView.getValue() > dateTimeUpperAsMonth.getValue()) && (calculateTimeAsView.getValue() + stepAmount) <= Month.DECEMBER.getValue()) {
+            if (hasIntersection(Range.closedOpen(calculateTimeAsView, calculateTimeAsView.plus(stepAmount)), range, dateTimeLowerAsMonth, dateTimeUpperAsMonth)) {
+                result.addAll(getMatchedTables(calculateTimeAsView, availableTargetNames));
+            }
+            calculateTimeAsView = calculateTimeAsView.plus(stepAmount);
+        }
+        return result;
+    }
+
+    private Collection<String> doShardingInYearMonth(final Collection<String> availableTargetNames, final Range<Comparable<?>> range, final TemporalAccessor calculateTime) {
+        Set<String> result = new HashSet<>();
+        YearMonth dateTimeUpperAsYearMonth = dateTimeUpper.query(YearMonth::from);
+        YearMonth dateTimeLowerAsYearMonth = dateTimeLower.query(YearMonth::from);
+        YearMonth calculateTimeAsView = calculateTime.query(YearMonth::from);
+        while (!calculateTimeAsView.isAfter(dateTimeUpperAsYearMonth)) {
+            if (hasIntersection(Range.closedOpen(calculateTimeAsView, calculateTimeAsView.plus(stepAmount, stepUnit)), range, dateTimeLowerAsYearMonth, dateTimeUpperAsYearMonth)) {
+                result.addAll(getMatchedTables(calculateTimeAsView, availableTargetNames));
+            }
+            calculateTimeAsView = calculateTimeAsView.plus(stepAmount, stepUnit);
+        }
+        return result;
+    }
     
     private boolean hasIntersection(final Range<LocalDateTime> calculateRange, final Range<Comparable<?>> range, final LocalDateTime dateTimeLower, final LocalDateTime dateTimeUpper) {
         LocalDateTime lower = range.hasLowerBound() ? parseLocalDateTime(range.lowerEndpoint()) : dateTimeLower;
@@ -217,6 +272,33 @@ public final class IntervalShardingAlgorithm implements StandardShardingAlgorith
         Range<LocalTime> dateTimeRange = Range.range(lower, lowerBoundType, upper, upperBoundType);
         return calculateRange.isConnected(dateTimeRange) && !calculateRange.intersection(dateTimeRange).isEmpty();
     }
+
+    private boolean hasIntersection(final Range<Year> calculateRange, final Range<Comparable<?>> range, final Year dateTimeLower, final Year dateTimeUpper) {
+        Year lower = range.hasLowerBound() ? parseYear(range.lowerEndpoint()) : dateTimeLower;
+        Year upper = range.hasUpperBound() ? parseYear(range.upperEndpoint()) : dateTimeUpper;
+        BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : BoundType.CLOSED;
+        BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : BoundType.CLOSED;
+        Range<Year> dateTimeRange = Range.range(lower, lowerBoundType, upper, upperBoundType);
+        return calculateRange.isConnected(dateTimeRange) && !calculateRange.intersection(dateTimeRange).isEmpty();
+    }
+
+    private boolean hasIntersection(final Range<Month> calculateRange, final Range<Comparable<?>> range, final Month dateTimeLower, final Month dateTimeUpper) {
+        Month lower = range.hasLowerBound() ? parseMonth(range.lowerEndpoint()) : dateTimeLower;
+        Month upper = range.hasUpperBound() ? parseMonth(range.upperEndpoint()) : dateTimeUpper;
+        BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : BoundType.CLOSED;
+        BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : BoundType.CLOSED;
+        Range<Month> dateTimeRange = Range.range(lower, lowerBoundType, upper, upperBoundType);
+        return calculateRange.isConnected(dateTimeRange) && !calculateRange.intersection(dateTimeRange).isEmpty();
+    }
+
+    private boolean hasIntersection(final Range<YearMonth> calculateRange, final Range<Comparable<?>> range, final YearMonth dateTimeLower, final YearMonth dateTimeUpper) {
+        YearMonth lower = range.hasLowerBound() ? parseYearMonth(range.lowerEndpoint()) : dateTimeLower;
+        YearMonth upper = range.hasUpperBound() ? parseYearMonth(range.upperEndpoint()) : dateTimeUpper;
+        BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : BoundType.CLOSED;
+        BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : BoundType.CLOSED;
+        Range<YearMonth> dateTimeRange = Range.range(lower, lowerBoundType, upper, upperBoundType);
+        return calculateRange.isConnected(dateTimeRange) && !calculateRange.intersection(dateTimeRange).isEmpty();
+    }
     
     private LocalDateTime parseLocalDateTime(final Comparable<?> endpoint) {
         return LocalDateTime.parse(getDateTimeText(endpoint).substring(0, dateTimePatternLength), dateTimeFormatter);
@@ -229,6 +311,18 @@ public final class IntervalShardingAlgorithm implements StandardShardingAlgorith
     private LocalTime parseLocalTime(final Comparable<?> endpoint) {
         return LocalTime.parse(getDateTimeText(endpoint).substring(0, dateTimePatternLength), dateTimeFormatter);
     }
+
+    private Year parseYear(final Comparable<?> endpoint) {
+        return Year.parse(getDateTimeText(endpoint).substring(0, dateTimePatternLength), dateTimeFormatter);
+    }
+
+    private Month parseMonth(final Comparable<?> endpoint) {
+        return (Month) endpoint;
+    }
+
+    private YearMonth parseYearMonth(final Comparable<?> endpoint) {
+        return YearMonth.parse(getDateTimeText(endpoint).substring(0, dateTimePatternLength), dateTimeFormatter);
+    }
     
     private String getDateTimeText(final Comparable<?> endpoint) {
         if (endpoint instanceof Instant) {
@@ -244,15 +338,27 @@ public final class IntervalShardingAlgorithm implements StandardShardingAlgorith
     }
     
     private Collection<String> getMatchedTables(final TemporalAccessor dateTime, final Collection<String> availableTargetNames) {
-        LocalDate localDate = dateTime.query(TemporalQueries.localDate());
-        LocalTime localTime = dateTime.query(TemporalQueries.localTime());
         String tableSuffix;
-        if (null == localTime) {
-            tableSuffix = localDate.format(tableSuffixPattern);
-            return availableTargetNames.parallelStream().filter(each -> each.endsWith(tableSuffix)).collect(Collectors.toSet());
+        if (!dateTime.isSupported(ChronoField.NANO_OF_DAY)) {
+            if (dateTime.isSupported(ChronoField.EPOCH_DAY)) {
+                tableSuffix = tableSuffixPattern.format(dateTime.query(TemporalQueries.localDate()));
+                return availableTargetNames.parallelStream().filter(each -> each.endsWith(tableSuffix)).collect(Collectors.toSet());
+            }
+            if (dateTime.isSupported(ChronoField.YEAR) && dateTime.isSupported(ChronoField.MONTH_OF_YEAR)) {
+                tableSuffix = tableSuffixPattern.format(dateTime.query(YearMonth::from));
+                return availableTargetNames.parallelStream().filter(each -> each.endsWith(tableSuffix)).collect(Collectors.toSet());
+            }
+            if (dateTime.isSupported(ChronoField.YEAR)) {
+                tableSuffix = tableSuffixPattern.format(dateTime.query(Year::from));
+                return availableTargetNames.parallelStream().filter(each -> each.endsWith(tableSuffix)).collect(Collectors.toSet());
+            }
+            if (dateTime.isSupported(ChronoField.MONTH_OF_YEAR)) {
+                tableSuffix = tableSuffixPattern.format(dateTime.query(Month::from));
+                return availableTargetNames.parallelStream().filter(each -> each.endsWith(tableSuffix)).collect(Collectors.toSet());
+            }
         }
-        if (null == localDate) {
-            tableSuffix = localTime.format(tableSuffixPattern);
+        if (!dateTime.isSupported(ChronoField.EPOCH_DAY)) {
+            tableSuffix = dateTime.query(TemporalQueries.localTime()).format(tableSuffixPattern);
             return availableTargetNames.parallelStream().filter(each -> each.endsWith(tableSuffix)).collect(Collectors.toSet());
         }
         tableSuffix = LocalDateTime.from(dateTime).format(tableSuffixPattern);
diff --git a/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithmTest.java b/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithmTest.java
index 5c11b55ec0f..5fb64997b19 100644
--- a/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithmTest.java
+++ b/shardingsphere-features/shardingsphere-sharding/shardingsphere-sharding-core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/datetime/IntervalShardingAlgorithmTest.java
@@ -33,8 +33,11 @@ import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.Month;
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
+import java.time.Year;
+import java.time.YearMonth;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
@@ -60,6 +63,12 @@ public final class IntervalShardingAlgorithmTest {
     
     private final Collection<String> availableTablesForJDBCTimeDataSources = new LinkedList<>();
     
+    private final Collection<String> availableTablesForYearDataSources = new LinkedList<>();
+    
+    private final Collection<String> availableTablesForYearMonthDataSources = new LinkedList<>();
+    
+    private final Collection<String> availableTablesForMonthInJSR310DataSources = new LinkedList<>();
+    
     private final Collection<String> availableTablesForDayWithMillisecondDataSources = new LinkedList<>();
     
     private IntervalShardingAlgorithm shardingAlgorithmByQuarter;
@@ -74,6 +83,12 @@ public final class IntervalShardingAlgorithmTest {
     
     private IntervalShardingAlgorithm shardingAlgorithmByDayWithMillisecond;
     
+    private IntervalShardingAlgorithm shardingAlgorithmByYear;
+    
+    private IntervalShardingAlgorithm shardingAlgorithmByYearMonth;
+    
+    private IntervalShardingAlgorithm shardingAlgorithmByMonthInJSR310;
+    
     @Before
     public void setup() {
         initShardStrategyByMonth();
@@ -82,6 +97,9 @@ public final class IntervalShardingAlgorithmTest {
         initShardStrategyByDayWithMillisecond();
         initShardingStrategyByJDBCDate();
         initShardingStrategyByJDBCTime();
+        initShardingStrategyByYear();
+        initShardingStrategyByYearMonth();
+        initShardingStrategyByMonthInJSR310();
     }
     
     private void initShardStrategyByQuarter() {
@@ -207,6 +225,68 @@ public final class IntervalShardingAlgorithmTest {
         result.setProperty("datetime-interval-unit", "Hours");
         return result;
     }
+
+    private void initShardingStrategyByYear() {
+        int stepAmount = 2;
+        shardingAlgorithmByYear = (IntervalShardingAlgorithm) ShardingAlgorithmFactory.newInstance(
+                new ShardingSphereAlgorithmConfiguration("INTERVAL", createYearProperties(stepAmount)));
+        for (int i = 2000; i < 2023; i++) {
+            availableTablesForYearDataSources.add(String.format("t_order_%04d", i));
+        }
+    }
+
+    private Properties createYearProperties(final int stepAmount) {
+        Properties result = new Properties();
+        result.setProperty("datetime-pattern", "yyyy");
+        result.setProperty("datetime-lower", "2000");
+        result.setProperty("datetime-upper", "2022");
+        result.setProperty("sharding-suffix-pattern", "yyyy");
+        result.setProperty("datetime-interval-amount", Integer.toString(stepAmount));
+        result.setProperty("datetime-interval-unit", "Years");
+        return result;
+    }
+
+    private void initShardingStrategyByYearMonth() {
+        int stepAmount = 2;
+        shardingAlgorithmByYearMonth = (IntervalShardingAlgorithm) ShardingAlgorithmFactory.newInstance(
+                new ShardingSphereAlgorithmConfiguration("INTERVAL", createYearMonthProperties(stepAmount)));
+        for (int i = 2016; i <= 2021; i++) {
+            for (int j = 1; j <= 12; j++) {
+                availableTablesForYearMonthDataSources.add(String.format("t_order_%04d%02d", i, j));
+            }
+        }
+    }
+
+    private Properties createYearMonthProperties(final int stepAmount) {
+        Properties result = new Properties();
+        result.setProperty("datetime-pattern", "yyyy-MM");
+        result.setProperty("datetime-lower", "2016-01");
+        result.setProperty("datetime-upper", "2021-12");
+        result.setProperty("sharding-suffix-pattern", "yyyyMM");
+        result.setProperty("datetime-interval-amount", Integer.toString(stepAmount));
+        result.setProperty("datetime-interval-unit", "Years");
+        return result;
+    }
+
+    private void initShardingStrategyByMonthInJSR310() {
+        int stepAmount = 2;
+        shardingAlgorithmByMonthInJSR310 = (IntervalShardingAlgorithm) ShardingAlgorithmFactory.newInstance(
+                new ShardingSphereAlgorithmConfiguration("INTERVAL", createMonthInJSR310Properties(stepAmount)));
+        for (int i = 2; i < 13; i++) {
+            availableTablesForMonthInJSR310DataSources.add(String.format("t_order_%02d", i));
+        }
+    }
+
+    private Properties createMonthInJSR310Properties(final int stepAmount) {
+        Properties result = new Properties();
+        result.setProperty("datetime-pattern", "MM");
+        result.setProperty("datetime-lower", "02");
+        result.setProperty("datetime-upper", "12");
+        result.setProperty("sharding-suffix-pattern", "MM");
+        result.setProperty("datetime-interval-amount", Integer.toString(stepAmount));
+        result.setProperty("datetime-interval-unit", "Months");
+        return result;
+    }
     
     @Test
     public void assertPreciseDoShardingByQuarter() {
@@ -352,4 +432,17 @@ public final class IntervalShardingAlgorithmTest {
                                 OffsetTime.of(12, 25, 27, 0, OffsetDateTime.now().getOffset()))));
         assertThat(actualAsOffsetTime.size(), is(6));
     }
+
+    @Test
+    public void assertIntegerInJDBCType() {
+        Collection<String> actualAsYear = shardingAlgorithmByYear.doSharding(availableTablesForYearDataSources,
+                new RangeShardingValue<>("t_order", "create_time", DATA_NODE_INFO, Range.closed(Year.of(2001), Year.of(2013))));
+        assertThat(actualAsYear.size(), is(7));
+        Collection<String> actualAsYearMonth = shardingAlgorithmByYearMonth.doSharding(availableTablesForYearMonthDataSources,
+                new RangeShardingValue<>("t_order", "create_time", DATA_NODE_INFO, Range.closed(YearMonth.of(2016, 1), YearMonth.of(2020, 1))));
+        assertThat(actualAsYearMonth.size(), is(3));
+        Collection<String> actualAsMonth = shardingAlgorithmByMonthInJSR310.doSharding(availableTablesForMonthInJSR310DataSources,
+                new RangeShardingValue<>("t_order", "create_time", DATA_NODE_INFO, Range.closed(Month.of(4), Month.of(10))));
+        assertThat(actualAsMonth.size(), is(4));
+    }
 }