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 88f305a0a59 Fix HASH_MOD routing mismatch for same negative numeric
value (#38327)
88f305a0a59 is described below
commit 88f305a0a598da754f15884dc991672b5ff74ac8
Author: ym0506 <[email protected]>
AuthorDate: Wed Mar 4 18:01:09 2026 +0900
Fix HASH_MOD routing mismatch for same negative numeric value (#38327)
* Fix HASH_MOD routing mismatch for same negative numeric value
* Add compatibility switch for HASH_MOD numeric normalization
---
.../common-config/builtin-algorithm/sharding.cn.md | 1 +
.../common-config/builtin-algorithm/sharding.en.md | 1 +
.../sharding/mod/HashModShardingAlgorithm.java | 30 +++++++++++++
.../sharding/mod/HashModShardingAlgorithmTest.java | 51 ++++++++++++++++++++++
.../driver/ShardingSphereDriverTest.java | 3 +-
.../config/driver/driver-fixture-h2-mysql.yaml | 1 +
6 files changed, 85 insertions(+), 2 deletions(-)
diff --git
a/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.cn.md
b/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.cn.md
index de4979ec3de..a383af9d1c3 100644
---
a/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.cn.md
+++
b/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.cn.md
@@ -32,6 +32,7 @@ ShardingSphere 内置提供了多种分片算法,按照类型可以划分为
| *属性名称* | *数据类型* | *说明* |
|----------------|--------|------|
| sharding-count | int | 分片数量 |
+| normalize-numeric-int-range (?) | boolean | 是否将整型范围内的 `Long` 和 `BigInteger`
按 `Integer` 语义统一计算,以保证相同数值跨类型路由一致 | false |
#### 基于分片容量的范围分片算法
diff --git
a/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.en.md
b/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.en.md
index c5987af34f1..1f2c4169da0 100644
---
a/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.en.md
+++
b/docs/document/content/user-manual/common-config/builtin-algorithm/sharding.en.md
@@ -34,6 +34,7 @@ Attributes:
| *Name* | *DataType* | *Description* |
|----------------|------------|----------------|
| sharding-count | int | Sharding count |
+| normalize-numeric-int-range (?) | boolean | Whether to normalize `Long` and
`BigInteger` values in integer range to integer semantics for consistent
routing across numeric types | false |
#### Volume Based Range Sharding Algorithm
diff --git
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithm.java
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithm.java
index adbdc7dda6a..60a8c26312e 100644
---
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithm.java
+++
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithm.java
@@ -26,6 +26,7 @@ import
org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingVal
import
org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import
org.apache.shardingsphere.sharding.exception.data.NullShardingValueException;
+import java.math.BigInteger;
import java.util.Collection;
import java.util.Properties;
@@ -36,11 +37,20 @@ public final class HashModShardingAlgorithm implements
StandardShardingAlgorithm
private static final String SHARDING_COUNT_KEY = "sharding-count";
+ private static final String NORMALIZE_NUMERIC_INT_RANGE_KEY =
"normalize-numeric-int-range";
+
+ private static final BigInteger INTEGER_MIN_VALUE =
BigInteger.valueOf(Integer.MIN_VALUE);
+
+ private static final BigInteger INTEGER_MAX_VALUE =
BigInteger.valueOf(Integer.MAX_VALUE);
+
private int shardingCount;
+ private boolean normalizeNumericIntRange;
+
@Override
public void init(final Properties props) {
shardingCount = getShardingCount(props);
+ normalizeNumericIntRange = isNormalizeNumericIntRange(props);
}
private int getShardingCount(final Properties props) {
@@ -50,6 +60,10 @@ public final class HashModShardingAlgorithm implements
StandardShardingAlgorithm
return result;
}
+ private boolean isNormalizeNumericIntRange(final Properties props) {
+ return
Boolean.parseBoolean(String.valueOf(props.getProperty(NORMALIZE_NUMERIC_INT_RANGE_KEY,
Boolean.FALSE.toString())));
+ }
+
@Override
public String doSharding(final Collection<String> availableTargetNames,
final PreciseShardingValue<Comparable<?>> shardingValue) {
ShardingSpherePreconditions.checkNotNull(shardingValue.getValue(),
NullShardingValueException::new);
@@ -63,9 +77,25 @@ public final class HashModShardingAlgorithm implements
StandardShardingAlgorithm
}
private long hashShardingValue(final Object shardingValue) {
+ if (normalizeNumericIntRange) {
+ if (shardingValue instanceof Long && isIntegerRange((Long)
shardingValue)) {
+ return Math.abs((long) ((Long) shardingValue).intValue());
+ }
+ if (shardingValue instanceof BigInteger &&
isIntegerRange((BigInteger) shardingValue)) {
+ return Math.abs((long) ((BigInteger)
shardingValue).intValue());
+ }
+ }
return Math.abs((long) shardingValue.hashCode());
}
+ private boolean isIntegerRange(final long shardingValue) {
+ return shardingValue >= Integer.MIN_VALUE && shardingValue <=
Integer.MAX_VALUE;
+ }
+
+ private boolean isIntegerRange(final BigInteger shardingValue) {
+ return shardingValue.compareTo(INTEGER_MIN_VALUE) >= 0 &&
shardingValue.compareTo(INTEGER_MAX_VALUE) <= 0;
+ }
+
@Override
public int getAutoTablesAmount() {
return shardingCount;
diff --git
a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithmTest.java
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithmTest.java
index ac54d49432f..a265a443893 100644
---
a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithmTest.java
+++
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/algorithm/sharding/mod/HashModShardingAlgorithmTest.java
@@ -29,6 +29,7 @@ import
org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -40,13 +41,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
class HashModShardingAlgorithmTest {
+ private static final String NORMALIZE_NUMERIC_INT_RANGE_KEY =
"normalize-numeric-int-range";
+
private static final DataNodeInfo DATA_NODE_INFO = new
DataNodeInfo("t_order_", 1, '0');
private HashModShardingAlgorithm shardingAlgorithm;
+ private HashModShardingAlgorithm normalizedShardingAlgorithm;
+
@BeforeEach
void setup() {
shardingAlgorithm = (HashModShardingAlgorithm)
TypedSPILoader.getService(ShardingAlgorithm.class, "HASH_MOD",
PropertiesBuilder.build(new Property("sharding-count", "4")));
+ normalizedShardingAlgorithm = (HashModShardingAlgorithm)
TypedSPILoader.getService(
+ ShardingAlgorithm.class, "HASH_MOD",
PropertiesBuilder.build(new Property("sharding-count", "4"), new
Property(NORMALIZE_NUMERIC_INT_RANGE_KEY, Boolean.TRUE.toString())));
}
@Test
@@ -55,6 +62,50 @@ class HashModShardingAlgorithmTest {
assertThat(shardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_type", DATA_NODE_INFO, "a")),
is("t_order_1"));
}
+ @Test
+ void assertPreciseDoShardingWithSameNegativeNumberValueForLegacyMode() {
+ List<String> availableTargetNames = Arrays.asList("t_order_0",
"t_order_1", "t_order_2", "t_order_3");
+ assertThat(shardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO, -1)),
is("t_order_1"));
+ assertThat(shardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO, -1L)),
is("t_order_0"));
+ assertThat(shardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO,
BigInteger.valueOf(-1L))), is("t_order_1"));
+ }
+
+ @Test
+ void assertPreciseDoShardingWithSameNegativeNumberValueForNormalizedMode()
{
+ List<String> availableTargetNames = Arrays.asList("t_order_0",
"t_order_1", "t_order_2", "t_order_3");
+
assertThat(normalizedShardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO, -1)),
is("t_order_1"));
+
assertThat(normalizedShardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO, -1L)),
is("t_order_1"));
+
assertThat(normalizedShardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO,
BigInteger.valueOf(-1L))), is("t_order_1"));
+ }
+
+ @Test
+ void
assertPreciseDoShardingWithLongValueOutOfIntegerRangeForNormalizedMode() {
+ List<String> availableTargetNames = Arrays.asList("t_order_0",
"t_order_1", "t_order_2", "t_order_3");
+
assertThat(normalizedShardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO, Long.MAX_VALUE)),
is("t_order_0"));
+ }
+
+ @Test
+ void
assertPreciseDoShardingWithBigIntegerBoundaryValuesForNormalizedMode() {
+ List<String> availableTargetNames = Arrays.asList("t_order_0",
"t_order_1", "t_order_2", "t_order_3");
+ assertThat(normalizedShardingAlgorithm.doSharding(
+ availableTargetNames, new PreciseShardingValue<>("t_order",
"order_id", DATA_NODE_INFO, BigInteger.valueOf(Integer.MIN_VALUE))),
is("t_order_0"));
+ assertThat(normalizedShardingAlgorithm.doSharding(
+ availableTargetNames, new PreciseShardingValue<>("t_order",
"order_id", DATA_NODE_INFO, BigInteger.valueOf(Integer.MAX_VALUE))),
is("t_order_3"));
+ }
+
+ @Test
+ void
assertPreciseDoShardingWithBigIntegerOutOfIntegerRangeForNormalizedMode() {
+ List<String> availableTargetNames = Arrays.asList("t_order_0",
"t_order_1", "t_order_2", "t_order_3");
+ BigInteger greaterThanIntegerMax =
BigInteger.valueOf(Integer.MAX_VALUE).add(BigInteger.ONE);
+ BigInteger lessThanIntegerMin =
BigInteger.valueOf(Integer.MIN_VALUE).subtract(BigInteger.ONE);
+ assertThat(normalizedShardingAlgorithm.doSharding(
+ availableTargetNames, new PreciseShardingValue<>("t_order",
"order_id", DATA_NODE_INFO, greaterThanIntegerMax)),
+ is(shardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO,
greaterThanIntegerMax))));
+ assertThat(normalizedShardingAlgorithm.doSharding(
+ availableTargetNames, new PreciseShardingValue<>("t_order",
"order_id", DATA_NODE_INFO, lessThanIntegerMin)),
+ is(shardingAlgorithm.doSharding(availableTargetNames, new
PreciseShardingValue<>("t_order", "order_id", DATA_NODE_INFO,
lessThanIntegerMin))));
+ }
+
@Test
void assertRangeDoSharding() {
List<String> availableTargetNames = Arrays.asList("t_order_0",
"t_order_1", "t_order_2", "t_order_3");
diff --git
a/jdbc/src/test/java/org/apache/shardingsphere/driver/ShardingSphereDriverTest.java
b/jdbc/src/test/java/org/apache/shardingsphere/driver/ShardingSphereDriverTest.java
index 627a8fed04a..0f34872b274 100644
---
a/jdbc/src/test/java/org/apache/shardingsphere/driver/ShardingSphereDriverTest.java
+++
b/jdbc/src/test/java/org/apache/shardingsphere/driver/ShardingSphereDriverTest.java
@@ -83,8 +83,7 @@ class ShardingSphereDriverTest {
statement.execute("DROP TABLE IF EXISTS t_order");
statement.execute("CREATE TABLE t_order (order_id INT PRIMARY
KEY, user_id INT)");
}
- // TODO Replace 1 to -1 after HASH_MOD algorithm improved
- int value = 1;
+ int value = -1;
try (PreparedStatement preparedStatement =
connection.prepareStatement("INSERT INTO t_order (order_id, user_id) VALUES (?,
?)")) {
preparedStatement.setObject(1, value);
preparedStatement.setObject(2, 101);
diff --git a/jdbc/src/test/resources/config/driver/driver-fixture-h2-mysql.yaml
b/jdbc/src/test/resources/config/driver/driver-fixture-h2-mysql.yaml
index c8c5a35c297..dbe6ce97cd5 100644
--- a/jdbc/src/test/resources/config/driver/driver-fixture-h2-mysql.yaml
+++ b/jdbc/src/test/resources/config/driver/driver-fixture-h2-mysql.yaml
@@ -48,6 +48,7 @@ rules:
type: HASH_MOD
props:
sharding-count: 2
+ normalize-numeric-int-range: true
keyGenerators:
snowflake: