This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 8303cc4f02 IGNITE-19935 Allow combining individual colocation hashes
(#2302)
8303cc4f02 is described below
commit 8303cc4f022451e932f65a2c6172112895cc1ce3
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Tue Jul 11 16:07:54 2023 +0300
IGNITE-19935 Allow combining individual colocation hashes (#2302)
Refactor hashing to change combined hash formula from `hash(col2, seed:
hash(col1))` to `hashCombine(hash(col1), hash(col2))`.
* This allows us to compute individual hash codes in any order, then
combine them in correct order (according to colocation key)
* Also this means we can combine hashing and serialization and avoid
duplicate value extraction (especially for strings, which require array
allocation to get the bytes)
* Collisions: 125 per 1M entries (`Row(0..99, 0..99, 0..99)`), up from 118
reported in [IGNITE-14769
comments](https://issues.apache.org/jira/browse/IGNITE-14769)
* Distribution:

---
.../ignite/client/PartitionAwarenessTest.java | 359 +++++++++++----------
.../ignite/internal/util/HashCalculator.java | 206 ++++++++++--
.../org/apache/ignite/internal/util/HashUtils.java | 52 ++-
.../cpp/tests/client-test/compute_test.cpp | 2 +-
.../Apache.Ignite.Tests/Compute/ComputeTests.cs | 18 +-
.../Apache.Ignite.Tests/PartitionAwarenessTests.cs | 18 +-
.../Proto/BinaryTuple/BinaryTupleBuilder.cs | 37 +--
.../Apache.Ignite/Internal/Proto/HashUtils.cs | 58 ++--
.../runner/app/client/ItThinClientComputeTest.java | 2 +-
.../table/ColocationHashCalculationTest.java | 67 +++-
10 files changed, 541 insertions(+), 278 deletions(-)
diff --git
a/modules/client/src/test/java/org/apache/ignite/client/PartitionAwarenessTest.java
b/modules/client/src/test/java/org/apache/ignite/client/PartitionAwarenessTest.java
index 490f0b12c5..99da4e97ac 100644
---
a/modules/client/src/test/java/org/apache/ignite/client/PartitionAwarenessTest.java
+++
b/modules/client/src/test/java/org/apache/ignite/client/PartitionAwarenessTest.java
@@ -54,6 +54,13 @@ import org.junit.jupiter.params.provider.ValueSource;
* Tests partition awareness.
*/
public class PartitionAwarenessTest extends AbstractClientTest {
+ private static final String nodeKey0 = "server-2";
+ private static final String nodeKey1 = "server-2";
+
+ private static final String nodeKey2 = "server-1";
+
+ private static final String nodeKey3 = "server-2";
+
private static TestServer testServer2;
private static Ignite server2;
@@ -112,40 +119,40 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
public void testGetTupleRoutesRequestToPrimaryNode() {
RecordView<Tuple> recordView = defaultTable().recordView();
- assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0L)));
- assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 1L)));
- assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 2L)));
- assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 3L)));
+ assertOpOnNode(nodeKey0, "get", x -> recordView.get(null,
Tuple.create().set("ID", 0L)));
+ assertOpOnNode(nodeKey1, "get", x -> recordView.get(null,
Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey2, "get", x -> recordView.get(null,
Tuple.create().set("ID", 2L)));
+ assertOpOnNode(nodeKey3, "get", x -> recordView.get(null,
Tuple.create().set("ID", 3L)));
}
@Test
public void testGetRecordRoutesRequestToPrimaryNode() {
RecordView<PersonPojo> pojoView =
defaultTable().recordView(Mapper.of(PersonPojo.class));
- assertOpOnNode("server-1", "get", x -> pojoView.get(null, new
PersonPojo(0L)));
- assertOpOnNode("server-2", "get", x -> pojoView.get(null, new
PersonPojo(1L)));
- assertOpOnNode("server-1", "get", x -> pojoView.get(null, new
PersonPojo(2L)));
- assertOpOnNode("server-2", "get", x -> pojoView.get(null, new
PersonPojo(3L)));
+ assertOpOnNode(nodeKey0, "get", x -> pojoView.get(null, new
PersonPojo(0L)));
+ assertOpOnNode(nodeKey1, "get", x -> pojoView.get(null, new
PersonPojo(1L)));
+ assertOpOnNode(nodeKey2, "get", x -> pojoView.get(null, new
PersonPojo(2L)));
+ assertOpOnNode(nodeKey3, "get", x -> pojoView.get(null, new
PersonPojo(3L)));
}
@Test
public void testGetKeyValueRoutesRequestToPrimaryNode() {
KeyValueView<Long, String> kvView =
defaultTable().keyValueView(Mapper.of(Long.class), Mapper.of(String.class));
- assertOpOnNode("server-1", "get", x -> kvView.get(null, 0L));
- assertOpOnNode("server-2", "get", x -> kvView.get(null, 1L));
- assertOpOnNode("server-1", "get", x -> kvView.get(null, 2L));
- assertOpOnNode("server-2", "get", x -> kvView.get(null, 3L));
+ assertOpOnNode(nodeKey0, "get", x -> kvView.get(null, 0L));
+ assertOpOnNode(nodeKey1, "get", x -> kvView.get(null, 1L));
+ assertOpOnNode(nodeKey2, "get", x -> kvView.get(null, 2L));
+ assertOpOnNode(nodeKey3, "get", x -> kvView.get(null, 3L));
}
@Test
public void testGetKeyValueBinaryRoutesRequestToPrimaryNode() {
KeyValueView<Tuple, Tuple> kvView = defaultTable().keyValueView();
- assertOpOnNode("server-1", "get", x -> kvView.get(null,
Tuple.create().set("ID", 0L)));
- assertOpOnNode("server-2", "get", x -> kvView.get(null,
Tuple.create().set("ID", 1L)));
- assertOpOnNode("server-1", "get", x -> kvView.get(null,
Tuple.create().set("ID", 2L)));
- assertOpOnNode("server-2", "get", x -> kvView.get(null,
Tuple.create().set("ID", 3L)));
+ assertOpOnNode(nodeKey0, "get", x -> kvView.get(null,
Tuple.create().set("ID", 0L)));
+ assertOpOnNode(nodeKey1, "get", x -> kvView.get(null,
Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey2, "get", x -> kvView.get(null,
Tuple.create().set("ID", 2L)));
+ assertOpOnNode(nodeKey3, "get", x -> kvView.get(null,
Tuple.create().set("ID", 3L)));
}
@Test
@@ -164,8 +171,8 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
// Check default assignment.
RecordView<Tuple> recordView = defaultTable().recordView();
- assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0L)));
- assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey1, "get", x -> recordView.get(null,
Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey2, "get", x -> recordView.get(null,
Tuple.create().set("ID", 2L)));
// Update partition assignment.
var assignments = new ArrayList<String>();
@@ -186,8 +193,8 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
}
// Check new assignment.
- assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0L)));
- assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey2, "get", x -> recordView.get(null,
Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey1, "get", x -> recordView.get(null,
Tuple.create().set("ID", 2L)));
}
@Test
@@ -198,20 +205,20 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0).set("COLO-1", "0")));
assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 2).set("COLO-1", "0")));
assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 3).set("COLO-1", "0")));
- assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 3).set("COLO-1", "2")));
+ assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 3).set("COLO-1", "4")));
// COLO-2 is set.
- assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0).set("COLO-1", "0").set("COLO-2", 1)));
- assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0).set("COLO-1", "0").set("COLO-2", 2)));
+ assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0).set("COLO-1", "0").set("COLO-2", 1)));
+ assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID", 0).set("COLO-1", "0").set("COLO-2", 8)));
}
@Test
public void testCompositeKey() {
RecordView<Tuple> recordView =
table(FakeIgniteTables.TABLE_COMPOSITE_KEY).recordView();
- assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 0).set("ID2", "0")));
- assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 1).set("ID2", "0")));
- assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 0).set("ID2", "1")));
+ assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 0).set("ID2", "0")));
+ assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 1).set("ID2", "0")));
+ assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 0).set("ID2", "1")));
assertOpOnNode("server-1", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 1).set("ID2", "1")));
assertOpOnNode("server-2", "get", x -> recordView.get(null,
Tuple.create().set("ID1", 1).set("ID2", "2")));
}
@@ -224,215 +231,215 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
var t1 = new PersonPojo(0L);
var t2 = new PersonPojo(1L);
- assertOpOnNode("server-1", "insert", x -> pojoView.insert(null, t1));
- assertOpOnNode("server-2", "insert", x -> pojoView.insert(null, t2));
+ assertOpOnNode(nodeKey0, "insert", x -> pojoView.insert(null, t1));
+ assertOpOnNode(nodeKey1, "insert", x -> pojoView.insert(null, t2));
- assertOpOnNode("server-1", "insertAll", x -> pojoView.insertAll(null,
List.of(t1)));
- assertOpOnNode("server-2", "insertAll", x -> pojoView.insertAll(null,
List.of(t2)));
+ assertOpOnNode(nodeKey0, "insertAll", x -> pojoView.insertAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey1, "insertAll", x -> pojoView.insertAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "upsert", x -> pojoView.upsert(null, t1));
- assertOpOnNode("server-2", "upsert", x -> pojoView.upsert(null, t2));
+ assertOpOnNode(nodeKey0, "upsert", x -> pojoView.upsert(null, t1));
+ assertOpOnNode(nodeKey1, "upsert", x -> pojoView.upsert(null, t2));
- assertOpOnNode("server-1", "upsertAll", x -> pojoView.upsertAll(null,
List.of(t1)));
- assertOpOnNode("server-2", "upsertAll", x -> pojoView.upsertAll(null,
List.of(t2)));
+ assertOpOnNode(nodeKey0, "upsertAll", x -> pojoView.upsertAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> pojoView.upsertAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "get", x -> pojoView.get(null, t1));
- assertOpOnNode("server-2", "get", x -> pojoView.get(null, t2));
+ assertOpOnNode(nodeKey0, "get", x -> pojoView.get(null, t1));
+ assertOpOnNode(nodeKey1, "get", x -> pojoView.get(null, t2));
- assertOpOnNode("server-1", "getAll", x -> pojoView.getAll(null,
List.of(t1)));
- assertOpOnNode("server-2", "getAll", x -> pojoView.getAll(null,
List.of(t2)));
+ assertOpOnNode(nodeKey0, "getAll", x -> pojoView.getAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey1, "getAll", x -> pojoView.getAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "getAndUpsert", x ->
pojoView.getAndUpsert(null, t1));
- assertOpOnNode("server-2", "getAndUpsert", x ->
pojoView.getAndUpsert(null, t2));
+ assertOpOnNode(nodeKey0, "getAndUpsert", x ->
pojoView.getAndUpsert(null, t1));
+ assertOpOnNode(nodeKey1, "getAndUpsert", x ->
pojoView.getAndUpsert(null, t2));
- assertOpOnNode("server-1", "getAndReplace", x ->
pojoView.getAndReplace(null, t1));
- assertOpOnNode("server-2", "getAndReplace", x ->
pojoView.getAndReplace(null, t2));
+ assertOpOnNode(nodeKey0, "getAndReplace", x ->
pojoView.getAndReplace(null, t1));
+ assertOpOnNode(nodeKey1, "getAndReplace", x ->
pojoView.getAndReplace(null, t2));
- assertOpOnNode("server-1", "getAndDelete", x ->
pojoView.getAndDelete(null, t1));
- assertOpOnNode("server-2", "getAndDelete", x ->
pojoView.getAndDelete(null, t2));
+ assertOpOnNode(nodeKey0, "getAndDelete", x ->
pojoView.getAndDelete(null, t1));
+ assertOpOnNode(nodeKey1, "getAndDelete", x ->
pojoView.getAndDelete(null, t2));
- assertOpOnNode("server-1", "replace", x -> pojoView.replace(null, t1));
- assertOpOnNode("server-2", "replace", x -> pojoView.replace(null, t2));
+ assertOpOnNode(nodeKey0, "replace", x -> pojoView.replace(null, t1));
+ assertOpOnNode(nodeKey1, "replace", x -> pojoView.replace(null, t2));
- assertOpOnNode("server-1", "replace", x -> pojoView.replace(null, t1,
t1));
- assertOpOnNode("server-2", "replace", x -> pojoView.replace(null, t2,
t2));
+ assertOpOnNode(nodeKey0, "replace", x -> pojoView.replace(null, t1,
t1));
+ assertOpOnNode(nodeKey1, "replace", x -> pojoView.replace(null, t2,
t2));
- assertOpOnNode("server-1", "delete", x -> pojoView.delete(null, t1));
- assertOpOnNode("server-2", "delete", x -> pojoView.delete(null, t2));
+ assertOpOnNode(nodeKey0, "delete", x -> pojoView.delete(null, t1));
+ assertOpOnNode(nodeKey1, "delete", x -> pojoView.delete(null, t2));
- assertOpOnNode("server-1", "deleteExact", x ->
pojoView.deleteExact(null, t1));
- assertOpOnNode("server-2", "deleteExact", x ->
pojoView.deleteExact(null, t2));
+ assertOpOnNode(nodeKey0, "deleteExact", x ->
pojoView.deleteExact(null, t1));
+ assertOpOnNode(nodeKey1, "deleteExact", x ->
pojoView.deleteExact(null, t2));
- assertOpOnNode("server-1", "deleteAll", x -> pojoView.deleteAll(null,
List.of(t1)));
- assertOpOnNode("server-2", "deleteAll", x -> pojoView.deleteAll(null,
List.of(t2)));
+ assertOpOnNode(nodeKey0, "deleteAll", x -> pojoView.deleteAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey1, "deleteAll", x -> pojoView.deleteAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "deleteAllExact", x ->
pojoView.deleteAllExact(null, List.of(t1)));
- assertOpOnNode("server-2", "deleteAllExact", x ->
pojoView.deleteAllExact(null, List.of(t2)));
+ assertOpOnNode(nodeKey0, "deleteAllExact", x ->
pojoView.deleteAllExact(null, List.of(t1)));
+ assertOpOnNode(nodeKey1, "deleteAllExact", x ->
pojoView.deleteAllExact(null, List.of(t2)));
}
@Test
public void testAllRecordBinaryViewOperations() {
RecordView<Tuple> recordView = defaultTable().recordView();
- Tuple t1 = Tuple.create().set("ID", 0L);
- Tuple t2 = Tuple.create().set("ID", 1L);
+ Tuple t1 = Tuple.create().set("ID", 1L);
+ Tuple t2 = Tuple.create().set("ID", 2L);
- assertOpOnNode("server-1", "insert", x -> recordView.insert(null, t1));
- assertOpOnNode("server-2", "insert", x -> recordView.insert(null, t2));
+ assertOpOnNode(nodeKey1, "insert", x -> recordView.insert(null, t1));
+ assertOpOnNode(nodeKey2, "insert", x -> recordView.insert(null, t2));
- assertOpOnNode("server-1", "insertAll", x ->
recordView.insertAll(null, List.of(t1)));
- assertOpOnNode("server-2", "insertAll", x ->
recordView.insertAll(null, List.of(t2)));
+ assertOpOnNode(nodeKey1, "insertAll", x -> recordView.insertAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey2, "insertAll", x -> recordView.insertAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "upsert", x -> recordView.upsert(null, t1));
- assertOpOnNode("server-2", "upsert", x -> recordView.upsert(null, t2));
+ assertOpOnNode(nodeKey1, "upsert", x -> recordView.upsert(null, t1));
+ assertOpOnNode(nodeKey2, "upsert", x -> recordView.upsert(null, t2));
- assertOpOnNode("server-1", "upsertAll", x ->
recordView.upsertAll(null, List.of(t1)));
- assertOpOnNode("server-2", "upsertAll", x ->
recordView.upsertAll(null, List.of(t2)));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> recordView.upsertAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey2, "upsertAll", x -> recordView.upsertAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "get", x -> recordView.get(null, t1));
- assertOpOnNode("server-2", "get", x -> recordView.get(null, t2));
+ assertOpOnNode(nodeKey1, "get", x -> recordView.get(null, t1));
+ assertOpOnNode(nodeKey2, "get", x -> recordView.get(null, t2));
- assertOpOnNode("server-1", "getAll", x -> recordView.getAll(null,
List.of(t1)));
- assertOpOnNode("server-2", "getAll", x -> recordView.getAll(null,
List.of(t2)));
+ assertOpOnNode(nodeKey1, "getAll", x -> recordView.getAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey2, "getAll", x -> recordView.getAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "getAndUpsert", x ->
recordView.getAndUpsert(null, t1));
- assertOpOnNode("server-2", "getAndUpsert", x ->
recordView.getAndUpsert(null, t2));
+ assertOpOnNode(nodeKey1, "getAndUpsert", x ->
recordView.getAndUpsert(null, t1));
+ assertOpOnNode(nodeKey2, "getAndUpsert", x ->
recordView.getAndUpsert(null, t2));
- assertOpOnNode("server-1", "getAndReplace", x ->
recordView.getAndReplace(null, t1));
- assertOpOnNode("server-2", "getAndReplace", x ->
recordView.getAndReplace(null, t2));
+ assertOpOnNode(nodeKey1, "getAndReplace", x ->
recordView.getAndReplace(null, t1));
+ assertOpOnNode(nodeKey2, "getAndReplace", x ->
recordView.getAndReplace(null, t2));
- assertOpOnNode("server-1", "getAndDelete", x ->
recordView.getAndDelete(null, t1));
- assertOpOnNode("server-2", "getAndDelete", x ->
recordView.getAndDelete(null, t2));
+ assertOpOnNode(nodeKey1, "getAndDelete", x ->
recordView.getAndDelete(null, t1));
+ assertOpOnNode(nodeKey2, "getAndDelete", x ->
recordView.getAndDelete(null, t2));
- assertOpOnNode("server-1", "replace", x -> recordView.replace(null,
t1));
- assertOpOnNode("server-2", "replace", x -> recordView.replace(null,
t2));
+ assertOpOnNode(nodeKey1, "replace", x -> recordView.replace(null, t1));
+ assertOpOnNode(nodeKey2, "replace", x -> recordView.replace(null, t2));
- assertOpOnNode("server-1", "replace", x -> recordView.replace(null,
t1, t1));
- assertOpOnNode("server-2", "replace", x -> recordView.replace(null,
t2, t2));
+ assertOpOnNode(nodeKey1, "replace", x -> recordView.replace(null, t1,
t1));
+ assertOpOnNode(nodeKey2, "replace", x -> recordView.replace(null, t2,
t2));
- assertOpOnNode("server-1", "delete", x -> recordView.delete(null, t1));
- assertOpOnNode("server-2", "delete", x -> recordView.delete(null, t2));
+ assertOpOnNode(nodeKey1, "delete", x -> recordView.delete(null, t1));
+ assertOpOnNode(nodeKey2, "delete", x -> recordView.delete(null, t2));
- assertOpOnNode("server-1", "deleteExact", x ->
recordView.deleteExact(null, t1));
- assertOpOnNode("server-2", "deleteExact", x ->
recordView.deleteExact(null, t2));
+ assertOpOnNode(nodeKey1, "deleteExact", x ->
recordView.deleteExact(null, t1));
+ assertOpOnNode(nodeKey2, "deleteExact", x ->
recordView.deleteExact(null, t2));
- assertOpOnNode("server-1", "deleteAll", x ->
recordView.deleteAll(null, List.of(t1)));
- assertOpOnNode("server-2", "deleteAll", x ->
recordView.deleteAll(null, List.of(t2)));
+ assertOpOnNode(nodeKey1, "deleteAll", x -> recordView.deleteAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey2, "deleteAll", x -> recordView.deleteAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "deleteAllExact", x ->
recordView.deleteAllExact(null, List.of(t1)));
- assertOpOnNode("server-2", "deleteAllExact", x ->
recordView.deleteAllExact(null, List.of(t2)));
+ assertOpOnNode(nodeKey1, "deleteAllExact", x ->
recordView.deleteAllExact(null, List.of(t1)));
+ assertOpOnNode(nodeKey2, "deleteAllExact", x ->
recordView.deleteAllExact(null, List.of(t2)));
}
@Test
public void testAllKeyValueViewOperations() {
KeyValueView<Long, String> kvView =
defaultTable().keyValueView(Mapper.of(Long.class), Mapper.of(String.class));
- var k1 = 0L;
- var k2 = 1L;
+ var k1 = 1L;
+ var k2 = 2L;
var v = "v";
- assertOpOnNode("server-1", "insert", x -> kvView.putIfAbsent(null, k1,
v));
- assertOpOnNode("server-2", "insert", x -> kvView.putIfAbsent(null, k2,
v));
+ assertOpOnNode(nodeKey1, "insert", x -> kvView.putIfAbsent(null, k1,
v));
+ assertOpOnNode(nodeKey2, "insert", x -> kvView.putIfAbsent(null, k2,
v));
- assertOpOnNode("server-1", "upsert", x -> kvView.put(null, k1, v));
- assertOpOnNode("server-2", "upsert", x -> kvView.put(null, k2, v));
+ assertOpOnNode(nodeKey1, "upsert", x -> kvView.put(null, k1, v));
+ assertOpOnNode(nodeKey2, "upsert", x -> kvView.put(null, k2, v));
- assertOpOnNode("server-1", "upsertAll", x -> kvView.putAll(null,
Map.of(k1, v)));
- assertOpOnNode("server-2", "upsertAll", x -> kvView.putAll(null,
Map.of(k2, v)));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> kvView.putAll(null,
Map.of(k1, v)));
+ assertOpOnNode(nodeKey2, "upsertAll", x -> kvView.putAll(null,
Map.of(k2, v)));
- assertOpOnNode("server-1", "get", x -> kvView.get(null, k1));
- assertOpOnNode("server-2", "get", x -> kvView.get(null, k2));
+ assertOpOnNode(nodeKey1, "get", x -> kvView.get(null, k1));
+ assertOpOnNode(nodeKey2, "get", x -> kvView.get(null, k2));
- assertOpOnNode("server-1", "get", x -> kvView.contains(null, k1));
- assertOpOnNode("server-2", "get", x -> kvView.contains(null, k2));
+ assertOpOnNode(nodeKey1, "get", x -> kvView.contains(null, k1));
+ assertOpOnNode(nodeKey2, "get", x -> kvView.contains(null, k2));
- assertOpOnNode("server-1", "getAll", x -> kvView.getAll(null,
List.of(k1)));
- assertOpOnNode("server-2", "getAll", x -> kvView.getAll(null,
List.of(k2)));
+ assertOpOnNode(nodeKey1, "getAll", x -> kvView.getAll(null,
List.of(k1)));
+ assertOpOnNode(nodeKey2, "getAll", x -> kvView.getAll(null,
List.of(k2)));
- assertOpOnNode("server-1", "getAndUpsert", x -> kvView.getAndPut(null,
k1, v));
- assertOpOnNode("server-2", "getAndUpsert", x -> kvView.getAndPut(null,
k2, v));
+ assertOpOnNode(nodeKey1, "getAndUpsert", x -> kvView.getAndPut(null,
k1, v));
+ assertOpOnNode(nodeKey2, "getAndUpsert", x -> kvView.getAndPut(null,
k2, v));
- assertOpOnNode("server-1", "getAndReplace", x ->
kvView.getAndReplace(null, k1, v));
- assertOpOnNode("server-2", "getAndReplace", x ->
kvView.getAndReplace(null, k2, v));
+ assertOpOnNode(nodeKey1, "getAndReplace", x ->
kvView.getAndReplace(null, k1, v));
+ assertOpOnNode(nodeKey2, "getAndReplace", x ->
kvView.getAndReplace(null, k2, v));
- assertOpOnNode("server-1", "getAndDelete", x ->
kvView.getAndRemove(null, k1));
- assertOpOnNode("server-2", "getAndDelete", x ->
kvView.getAndRemove(null, k2));
+ assertOpOnNode(nodeKey1, "getAndDelete", x ->
kvView.getAndRemove(null, k1));
+ assertOpOnNode(nodeKey2, "getAndDelete", x ->
kvView.getAndRemove(null, k2));
- assertOpOnNode("server-1", "replace", x -> kvView.replace(null, k1,
v));
- assertOpOnNode("server-2", "replace", x -> kvView.replace(null, k2,
v));
+ assertOpOnNode(nodeKey1, "replace", x -> kvView.replace(null, k1, v));
+ assertOpOnNode(nodeKey2, "replace", x -> kvView.replace(null, k2, v));
- assertOpOnNode("server-1", "replace", x -> kvView.replace(null, k1, v,
v));
- assertOpOnNode("server-2", "replace", x -> kvView.replace(null, k2, v,
v));
+ assertOpOnNode(nodeKey1, "replace", x -> kvView.replace(null, k1, v,
v));
+ assertOpOnNode(nodeKey2, "replace", x -> kvView.replace(null, k2, v,
v));
- assertOpOnNode("server-1", "delete", x -> kvView.remove(null, k1));
- assertOpOnNode("server-2", "delete", x -> kvView.remove(null, k2));
+ assertOpOnNode(nodeKey1, "delete", x -> kvView.remove(null, k1));
+ assertOpOnNode(nodeKey2, "delete", x -> kvView.remove(null, k2));
- assertOpOnNode("server-1", "deleteExact", x -> kvView.remove(null, k1,
v));
- assertOpOnNode("server-2", "deleteExact", x -> kvView.remove(null, k2,
v));
+ assertOpOnNode(nodeKey1, "deleteExact", x -> kvView.remove(null, k1,
v));
+ assertOpOnNode(nodeKey2, "deleteExact", x -> kvView.remove(null, k2,
v));
- assertOpOnNode("server-1", "deleteAll", x -> kvView.removeAll(null,
List.of(k1)));
- assertOpOnNode("server-2", "deleteAll", x -> kvView.removeAll(null,
List.of(k2)));
+ assertOpOnNode(nodeKey1, "deleteAll", x -> kvView.removeAll(null,
List.of(k1)));
+ assertOpOnNode(nodeKey2, "deleteAll", x -> kvView.removeAll(null,
List.of(k2)));
}
@Test
public void testAllKeyValueBinaryViewOperations() {
KeyValueView<Tuple, Tuple> kvView = defaultTable().keyValueView();
- Tuple t1 = Tuple.create().set("ID", 0L);
- Tuple t2 = Tuple.create().set("ID", 1L);
+ Tuple t1 = Tuple.create().set("ID", 1L);
+ Tuple t2 = Tuple.create().set("ID", 2L);
- assertOpOnNode("server-1", "insert", x -> kvView.putIfAbsent(null, t1,
t1));
- assertOpOnNode("server-2", "insert", x -> kvView.putIfAbsent(null, t2,
t2));
+ assertOpOnNode(nodeKey1, "insert", x -> kvView.putIfAbsent(null, t1,
t1));
+ assertOpOnNode(nodeKey2, "insert", x -> kvView.putIfAbsent(null, t2,
t2));
- assertOpOnNode("server-1", "upsert", x -> kvView.put(null, t1, t1));
- assertOpOnNode("server-2", "upsert", x -> kvView.put(null, t2, t2));
+ assertOpOnNode(nodeKey1, "upsert", x -> kvView.put(null, t1, t1));
+ assertOpOnNode(nodeKey2, "upsert", x -> kvView.put(null, t2, t2));
- assertOpOnNode("server-1", "upsertAll", x -> kvView.putAll(null,
Map.of(t1, t1)));
- assertOpOnNode("server-2", "upsertAll", x -> kvView.putAll(null,
Map.of(t2, t2)));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> kvView.putAll(null,
Map.of(t1, t1)));
+ assertOpOnNode(nodeKey2, "upsertAll", x -> kvView.putAll(null,
Map.of(t2, t2)));
- assertOpOnNode("server-1", "get", x -> kvView.get(null, t1));
- assertOpOnNode("server-2", "get", x -> kvView.get(null, t2));
+ assertOpOnNode(nodeKey1, "get", x -> kvView.get(null, t1));
+ assertOpOnNode(nodeKey2, "get", x -> kvView.get(null, t2));
- assertOpOnNode("server-1", "get", x -> kvView.contains(null, t1));
- assertOpOnNode("server-2", "get", x -> kvView.contains(null, t2));
+ assertOpOnNode(nodeKey1, "get", x -> kvView.contains(null, t1));
+ assertOpOnNode(nodeKey2, "get", x -> kvView.contains(null, t2));
- assertOpOnNode("server-1", "getAll", x -> kvView.getAll(null,
List.of(t1)));
- assertOpOnNode("server-2", "getAll", x -> kvView.getAll(null,
List.of(t2)));
+ assertOpOnNode(nodeKey1, "getAll", x -> kvView.getAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey2, "getAll", x -> kvView.getAll(null,
List.of(t2)));
- assertOpOnNode("server-1", "getAndUpsert", x -> kvView.getAndPut(null,
t1, t1));
- assertOpOnNode("server-2", "getAndUpsert", x -> kvView.getAndPut(null,
t2, t2));
+ assertOpOnNode(nodeKey1, "getAndUpsert", x -> kvView.getAndPut(null,
t1, t1));
+ assertOpOnNode(nodeKey2, "getAndUpsert", x -> kvView.getAndPut(null,
t2, t2));
- assertOpOnNode("server-1", "getAndReplace", x ->
kvView.getAndReplace(null, t1, t1));
- assertOpOnNode("server-2", "getAndReplace", x ->
kvView.getAndReplace(null, t2, t2));
+ assertOpOnNode(nodeKey1, "getAndReplace", x ->
kvView.getAndReplace(null, t1, t1));
+ assertOpOnNode(nodeKey2, "getAndReplace", x ->
kvView.getAndReplace(null, t2, t2));
- assertOpOnNode("server-1", "getAndDelete", x ->
kvView.getAndRemove(null, t1));
- assertOpOnNode("server-2", "getAndDelete", x ->
kvView.getAndRemove(null, t2));
+ assertOpOnNode(nodeKey1, "getAndDelete", x ->
kvView.getAndRemove(null, t1));
+ assertOpOnNode(nodeKey2, "getAndDelete", x ->
kvView.getAndRemove(null, t2));
- assertOpOnNode("server-1", "replace", x -> kvView.replace(null, t1,
t1));
- assertOpOnNode("server-2", "replace", x -> kvView.replace(null, t2,
t2));
+ assertOpOnNode(nodeKey1, "replace", x -> kvView.replace(null, t1, t1));
+ assertOpOnNode(nodeKey2, "replace", x -> kvView.replace(null, t2, t2));
- assertOpOnNode("server-1", "replace", x -> kvView.replace(null, t1,
t1, t1));
- assertOpOnNode("server-2", "replace", x -> kvView.replace(null, t2,
t2, t2));
+ assertOpOnNode(nodeKey1, "replace", x -> kvView.replace(null, t1, t1,
t1));
+ assertOpOnNode(nodeKey2, "replace", x -> kvView.replace(null, t2, t2,
t2));
- assertOpOnNode("server-1", "delete", x -> kvView.remove(null, t1));
- assertOpOnNode("server-2", "delete", x -> kvView.remove(null, t2));
+ assertOpOnNode(nodeKey1, "delete", x -> kvView.remove(null, t1));
+ assertOpOnNode(nodeKey2, "delete", x -> kvView.remove(null, t2));
- assertOpOnNode("server-1", "deleteExact", x -> kvView.remove(null, t1,
t1));
- assertOpOnNode("server-2", "deleteExact", x -> kvView.remove(null, t2,
t2));
+ assertOpOnNode(nodeKey1, "deleteExact", x -> kvView.remove(null, t1,
t1));
+ assertOpOnNode(nodeKey2, "deleteExact", x -> kvView.remove(null, t2,
t2));
- assertOpOnNode("server-1", "deleteAll", x -> kvView.removeAll(null,
List.of(t1)));
- assertOpOnNode("server-2", "deleteAll", x -> kvView.removeAll(null,
List.of(t2)));
+ assertOpOnNode(nodeKey1, "deleteAll", x -> kvView.removeAll(null,
List.of(t1)));
+ assertOpOnNode(nodeKey2, "deleteAll", x -> kvView.removeAll(null,
List.of(t2)));
}
@Test
public void testExecuteColocatedTupleKeyRoutesRequestToPrimaryNode() {
Table table = defaultTable();
- Tuple t1 = Tuple.create().set("ID", 0L);
- Tuple t2 = Tuple.create().set("ID", 1L);
+ Tuple t1 = Tuple.create().set("ID", 1L);
+ Tuple t2 = Tuple.create().set("ID", 2L);
- assertEquals("server-1", compute().executeColocated(table.name(), t1,
List.of(), "job").join());
- assertEquals("server-2", compute().executeColocated(table.name(), t2,
List.of(), "job").join());
+ assertEquals(nodeKey1, compute().executeColocated(table.name(), t1,
List.of(), "job").join());
+ assertEquals(nodeKey2, compute().executeColocated(table.name(), t2,
List.of(), "job").join());
}
@Test
@@ -440,8 +447,8 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
var mapper = Mapper.of(Long.class);
Table table = defaultTable();
- assertEquals("server-1", compute().executeColocated(table.name(), 0L,
mapper, List.of(), "job").join());
- assertEquals("server-2", compute().executeColocated(table.name(), 1L,
mapper, List.of(), "job").join());
+ assertEquals(nodeKey1, compute().executeColocated(table.name(), 1L,
mapper, List.of(), "job").join());
+ assertEquals(nodeKey2, compute().executeColocated(table.name(), 2L,
mapper, List.of(), "job").join());
}
@Test
@@ -456,10 +463,10 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
fut.join();
};
- assertOpOnNode("server-1", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 0L)));
- assertOpOnNode("server-2", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 1L)));
- assertOpOnNode("server-1", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 2L)));
- assertOpOnNode("server-2", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 3L)));
+ assertOpOnNode(nodeKey0, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 0L)));
+ assertOpOnNode(nodeKey1, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey2, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 2L)));
+ assertOpOnNode(nodeKey3, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 3L)));
}
@Test
@@ -474,10 +481,10 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
fut.join();
};
- assertOpOnNode("server-1", "upsertAll", x -> stream.accept(new
PersonPojo(0L)));
- assertOpOnNode("server-2", "upsertAll", x -> stream.accept(new
PersonPojo(1L)));
- assertOpOnNode("server-1", "upsertAll", x -> stream.accept(new
PersonPojo(2L)));
- assertOpOnNode("server-2", "upsertAll", x -> stream.accept(new
PersonPojo(3L)));
+ assertOpOnNode(nodeKey0, "upsertAll", x -> stream.accept(new
PersonPojo(0L)));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> stream.accept(new
PersonPojo(1L)));
+ assertOpOnNode(nodeKey2, "upsertAll", x -> stream.accept(new
PersonPojo(2L)));
+ assertOpOnNode(nodeKey3, "upsertAll", x -> stream.accept(new
PersonPojo(3L)));
}
@Test
@@ -492,10 +499,10 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
fut.join();
};
- assertOpOnNode("server-1", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 0L)));
- assertOpOnNode("server-2", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 1L)));
- assertOpOnNode("server-1", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 2L)));
- assertOpOnNode("server-2", "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 3L)));
+ assertOpOnNode(nodeKey0, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 0L)));
+ assertOpOnNode(nodeKey1, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 1L)));
+ assertOpOnNode(nodeKey2, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 2L)));
+ assertOpOnNode(nodeKey3, "upsertAll", x ->
stream.accept(Tuple.create().set("ID", 3L)));
}
@Test
@@ -510,10 +517,10 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
fut.join();
};
- assertOpOnNode("server-1", "upsertAll", x -> stream.accept(0L));
- assertOpOnNode("server-2", "upsertAll", x -> stream.accept(1L));
- assertOpOnNode("server-1", "upsertAll", x -> stream.accept(2L));
- assertOpOnNode("server-2", "upsertAll", x -> stream.accept(3L));
+ assertOpOnNode(nodeKey0, "upsertAll", x -> stream.accept(0L));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> stream.accept(1L));
+ assertOpOnNode(nodeKey2, "upsertAll", x -> stream.accept(2L));
+ assertOpOnNode(nodeKey3, "upsertAll", x -> stream.accept(3L));
}
@Test
@@ -537,8 +544,8 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
}
};
- assertOpOnNode("server-1", "upsertAll", x -> submit.accept(0L));
- assertOpOnNode("server-2", "upsertAll", x -> submit.accept(1L));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> submit.accept(1L));
+ assertOpOnNode(nodeKey2, "upsertAll", x -> submit.accept(2L));
// Update partition assignment.
var assignments = new ArrayList<String>();
@@ -550,16 +557,16 @@ public class PartitionAwarenessTest extends
AbstractClientTest {
// Send some batches so that the client receives updated assignment.
lastOpServerName = null;
- submit.accept(0L);
+ submit.accept(1L);
assertTrue(IgniteTestUtils.waitForCondition(() -> lastOpServerName !=
null, 1000));
lastOpServerName = null;
- submit.accept(1L);
+ submit.accept(2L);
assertTrue(IgniteTestUtils.waitForCondition(() -> lastOpServerName !=
null, 1000));
// Check updated assignment.
- assertOpOnNode("server-2", "upsertAll", x -> submit.accept(0L));
- assertOpOnNode("server-1", "upsertAll", x -> submit.accept(1L));
+ assertOpOnNode(nodeKey2, "upsertAll", x -> submit.accept(1L));
+ assertOpOnNode(nodeKey1, "upsertAll", x -> submit.accept(2L));
publisher.close();
fut.join();
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/util/HashCalculator.java
b/modules/core/src/main/java/org/apache/ignite/internal/util/HashCalculator.java
index 871ecb55a5..8657cd4f00 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/util/HashCalculator.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/util/HashCalculator.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.util;
+import static org.apache.ignite.internal.util.HashUtils.combine;
+
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
@@ -98,7 +100,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendByte(byte v) {
- hash = HashUtils.hash32(v, hash);
+ hash = combine(hash, hashByte(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashByte(byte v) {
+ return HashUtils.hash32(v);
}
/**
@@ -107,7 +118,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendShort(short v) {
- hash = HashUtils.hash32(v, hash);
+ hash = combine(hash, hashShort(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashShort(short v) {
+ return HashUtils.hash32(v);
}
/**
@@ -116,7 +136,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendInt(int v) {
- hash = HashUtils.hash32(v, hash);
+ hash = combine(hash, hashInt(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashInt(int v) {
+ return HashUtils.hash32(v);
}
/**
@@ -125,7 +154,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendLong(long v) {
- hash = HashUtils.hash32(v, hash);
+ hash = combine(hash, hashLong(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashLong(long v) {
+ return HashUtils.hash32(v);
}
/**
@@ -134,7 +172,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendFloat(float v) {
- appendInt(Float.floatToRawIntBits(v));
+ hash = combine(hash, hashFloat(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashFloat(float v) {
+ return HashUtils.hash32(Float.floatToRawIntBits(v));
}
/**
@@ -143,7 +190,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendDouble(double v) {
- appendLong(Double.doubleToRawLongBits(v));
+ hash = combine(hash, hashDouble(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashDouble(double v) {
+ return HashUtils.hash32(Double.doubleToRawLongBits(v));
}
/**
@@ -152,7 +208,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendDecimal(BigDecimal v, int columnScale) {
- appendBytes(v.setScale(columnScale,
RoundingMode.HALF_UP).unscaledValue().toByteArray());
+ hash = combine(hash, hashDecimal(v, columnScale));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashDecimal(BigDecimal v, int columnScale) {
+ return hashBytes(v.setScale(columnScale,
RoundingMode.HALF_UP).unscaledValue().toByteArray());
}
/**
@@ -161,7 +226,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendNumber(BigInteger v) {
- appendBytes(v.toByteArray());
+ hash = combine(hash, hashNumber(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashNumber(BigInteger v) {
+ return hashBytes(v.toByteArray());
}
/**
@@ -170,8 +244,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendUuid(UUID v) {
- appendLong(v.getMostSignificantBits());
- appendLong(v.getLeastSignificantBits());
+ hash = combine(hash, hashUuid(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashUuid(UUID v) {
+ return HashUtils.hash32(v.getLeastSignificantBits(),
HashUtils.hash32(v.getMostSignificantBits()));
}
/**
@@ -180,7 +262,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendString(String v) {
- appendBytes(v.getBytes(StandardCharsets.UTF_8));
+ hash = combine(hash, hashString(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashString(String v) {
+ return hashBytes(v.getBytes(StandardCharsets.UTF_8));
}
/**
@@ -189,7 +280,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendBytes(byte[] v) {
- hash = HashUtils.hash32(v, 0, v.length, hash);
+ hash = combine(hash, hashBytes(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashBytes(byte[] v) {
+ return HashUtils.hash32(v);
}
/**
@@ -198,7 +298,16 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendBitmask(BitSet v) {
- appendBytes(v.toByteArray());
+ hash = combine(hash, hashBitmask(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashBitmask(BitSet v) {
+ return hashBytes(v.toByteArray());
}
/**
@@ -207,9 +316,18 @@ public class HashCalculator {
* @param v Value to update hash.
*/
public void appendDate(LocalDate v) {
- appendLong(v.getYear());
- appendLong(v.getMonthValue());
- appendLong(v.getDayOfMonth());
+ hash = combine(hash, hashDate(v));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashDate(LocalDate v) {
+ return HashUtils.hash32(v.getDayOfMonth(),
+ HashUtils.hash32(v.getMonthValue(),
+ HashUtils.hash32(v.getYear())));
}
/**
@@ -219,10 +337,19 @@ public class HashCalculator {
* @param precision Precision.
*/
public void appendTime(LocalTime v, int precision) {
- appendLong(v.getHour());
- appendLong(v.getMinute());
- appendLong(v.getSecond());
- appendLong(TemporalTypeUtils.normalizeNanos(v.getNano(), precision));
+ hash = combine(hash, hashTime(v, precision));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashTime(LocalTime v, int precision) {
+ int hourHash = HashUtils.hash32(v.getHour());
+ int minuteHash = HashUtils.hash32(v.getMinute(), hourHash);
+ int secondHash = HashUtils.hash32(v.getSecond(), minuteHash);
+ return HashUtils.hash32(TemporalTypeUtils.normalizeNanos(v.getNano(),
precision), secondHash);
}
/**
@@ -232,8 +359,16 @@ public class HashCalculator {
* @param precision Precision.
*/
public void appendDateTime(LocalDateTime v, int precision) {
- appendDate(v.toLocalDate());
- appendTime(v.toLocalTime(), precision);
+ hash = combine(hash, hashDateTime(v, precision));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashDateTime(LocalDateTime v, int precision) {
+ return combine(hashDate(v.toLocalDate()), hashTime(v.toLocalTime(),
precision));
}
/**
@@ -243,8 +378,31 @@ public class HashCalculator {
* @param precision Precision.
*/
public void appendTimestamp(Instant v, int precision) {
- appendLong(v.getEpochSecond());
- appendLong(TemporalTypeUtils.normalizeNanos(v.getNano(), precision));
+ hash = combine(hash, hashTimestamp(v, precision));
+ }
+
+ /**
+ * Get value hash.
+ *
+ * @param v Value to hash.
+ */
+ public static int hashTimestamp(Instant v, int precision) {
+ return HashUtils.hash32(TemporalTypeUtils.normalizeNanos(v.getNano(),
precision), hashLong(v.getEpochSecond()));
+ }
+
+ /**
+ * Get сombined hash code.
+ *
+ * @param hashes Individual hash codes.
+ */
+ public static int combinedHash(int[] hashes) {
+ int hash = 0;
+
+ for (int h : hashes) {
+ hash = combine(hash, h);
+ }
+
+ return hash;
}
/**
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/util/HashUtils.java
b/modules/core/src/main/java/org/apache/ignite/internal/util/HashUtils.java
index fd53e87442..dc7924250b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/HashUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/HashUtils.java
@@ -64,42 +64,37 @@ public class HashUtils {
}
/**
- * Generates 32-bit hash from the byte array with the given offset, length
and seed.
+ * Generates 32-bit hash from the byte value.
*
- * @param data The input byte array.
- * @param offset The first element of array.
- * @param length The length of array.
- * @param seed The initial seed value.
+ * @param data The input byte.
* @return The 32-bit hash.
*/
- public static int hash32(byte[] data, int offset, int length, int seed) {
- long hash = hash64(data, offset, length, seed);
+ public static int hash32(byte data) {
+ long hash = hash64(data, 0);
return (int) (hash ^ (hash >>> 32));
}
/**
- * Generates 32-bit hash from the byte value.
+ * Generates 32-bit hash from the short value.
*
- * @param data The input byte.
+ * @param data The input short value.
* @return The 32-bit hash.
*/
- public static int hash32(byte data, int seed) {
- long hash = hash64(data, seed);
+ public static int hash32(short data) {
+ long hash = hash64(data, 0);
return (int) (hash ^ (hash >>> 32));
}
/**
- * Generates 32-bit hash from the short value.
+ * Generates 32-bit hash from the integer value.
*
- * @param data The input short value.
+ * @param data The input integer value.
* @return The 32-bit hash.
*/
- public static int hash32(short data, int seed) {
- long hash = hash64(data, seed);
-
- return (int) (hash ^ (hash >>> 32));
+ public static int hash32(int data) {
+ return hash32(data, 0);
}
/**
@@ -114,6 +109,16 @@ public class HashUtils {
return (int) (hash ^ (hash >>> 32));
}
+ /**
+ * Generates 32-bit hash from the long value.
+ *
+ * @param data The input long value.
+ * @return The 32-bit hash.
+ */
+ public static int hash32(long data) {
+ return hash32(data, 0);
+ }
+
/**
* Generates 32-bit hash from the long value.
*
@@ -126,6 +131,19 @@ public class HashUtils {
return (int) (hash ^ (hash >>> 32));
}
+ /**
+ * Combines two hash values, using second value as a seed for the hash of
the first one.
+ *
+ * <p>The operation is not commutative - the order of the arguments
matters.
+ *
+ * @param hash1 The first hash.
+ * @param hash2 The second hash.
+ * @return The combined 32-bit hash.
+ */
+ public static int combine(int hash1, int hash2) {
+ return hash32(hash1, hash2);
+ }
+
/**
* Generates 64-bit hash from the byte value.
*
diff --git a/modules/platforms/cpp/tests/client-test/compute_test.cpp
b/modules/platforms/cpp/tests/client-test/compute_test.cpp
index 69e6299889..b5c31a5021 100644
--- a/modules/platforms/cpp/tests/client-test/compute_test.cpp
+++ b/modules/platforms/cpp/tests/client-test/compute_test.cpp
@@ -239,7 +239,7 @@ TEST_F(compute_test, all_arg_types) {
}
TEST_F(compute_test, execute_colocated) {
- std::map<std::int32_t, std::string> nodes_for_values = {{1, "_4"}, {5,
"_2"}, {9, "_3"}, {10, ""}, {11, "_2"}};
+ std::map<std::int32_t, std::string> nodes_for_values = {{1, "_2"}, {5,
"_4"}, {9, ""}, {10, "_2"}, {11, "_4"}};
for (const auto &var : nodes_for_values) {
SCOPED_TRACE("key=" + std::to_string(var.first) + ", node=" +
var.second);
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
index 7d63d4bea6..ea6acc0c0b 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
@@ -233,16 +233,16 @@ namespace Apache.Ignite.Tests.Compute
}
[Test]
- [TestCase(1, 4)]
- [TestCase(2, 4)]
- [TestCase(4, 2)]
- [TestCase(5, 2)]
- [TestCase(6, 1)]
- [TestCase(7, 4)]
+ [TestCase(1, 2)]
+ [TestCase(2, 1)]
+ [TestCase(4, 3)]
+ [TestCase(5, 4)]
+ [TestCase(6, 3)]
+ [TestCase(7, 1)]
[TestCase(8, 2)]
- [TestCase(9, 3)]
- [TestCase(10, 1)]
- [TestCase(11, 2)]
+ [TestCase(9, 1)]
+ [TestCase(10, 2)]
+ [TestCase(11, 4)]
public async Task TestExecuteColocated(long key, int nodeIdx)
{
var proxies = GetProxies();
diff --git
a/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
index 5a793a086f..94779fe45a 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/PartitionAwarenessTests.cs
@@ -33,14 +33,14 @@ public class PartitionAwarenessTests
{
private static readonly object[] KeyNodeCases =
{
- new object[] { 3, 1 },
- new object[] { 5, 1 },
- new object[] { 8, 1 },
+ new object[] { 0, 1 },
new object[] { 1, 2 },
+ new object[] { 3, 1 },
new object[] { 4, 2 },
- new object[] { 0, 2 },
+ new object[] { 5, 2 },
+ new object[] { 8, 2 },
new object[] { int.MaxValue, 2 },
- new object[] { int.MaxValue - 1, 1 },
+ new object[] { int.MaxValue - 1, 2 },
new object[] { int.MinValue, 2 }
};
@@ -78,7 +78,7 @@ public class PartitionAwarenessTests
await AssertOpOnNode(async () => await recordView.UpsertAsync(null,
1), ClientOp.TupleUpsert, _server2, _server1);
await AssertOpOnNode(async () => await recordView.UpsertAsync(null,
3), ClientOp.TupleUpsert, _server1, _server2);
await AssertOpOnNode(async () => await recordView.UpsertAsync(null,
4), ClientOp.TupleUpsert, _server2, _server1);
- await AssertOpOnNode(async () => await recordView.UpsertAsync(null,
5), ClientOp.TupleUpsert, _server1, _server2);
+ await AssertOpOnNode(async () => await recordView.UpsertAsync(null,
7), ClientOp.TupleUpsert, _server1, _server2);
}
[Test]
@@ -265,8 +265,8 @@ public class PartitionAwarenessTests
await Test("1", Guid.Empty, _server1);
await Test("1", Guid.Parse("b0000000-0000-0000-0000-000000000000"),
_server2);
- await Test("a", Guid.Empty, _server2);
- await Test("a", Guid.Parse("b0000000-0000-0000-0000-000000000000"),
_server1);
+ await Test("c", Guid.Empty, _server2);
+ await Test("c", Guid.Parse("b0000000-0000-0000-0000-000000000000"),
_server1);
async Task Test(string idStr, Guid idGuid, FakeServer node) =>
await AssertOpOnNode(() => view.UpsertAsync(null, new
CompositeKey(idStr, idGuid)), ClientOp.TupleUpsert, node);
@@ -283,7 +283,7 @@ public class PartitionAwarenessTests
// Both columns are part of key, but only string column is colocation
key, so random Guid does not affect the hash.
await Test("1", Guid.NewGuid(), _server2);
- await Test("a", Guid.NewGuid(), _server1);
+ await Test("c", Guid.NewGuid(), _server1);
async Task Test(string idStr, Guid idGuid, FakeServer node) =>
await AssertOpOnNode(() => view.UpsertAsync(null, new
CompositeKey(idStr, idGuid)), ClientOp.TupleUpsert, node);
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
index 62972edead..0b88821997 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
@@ -105,7 +105,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32((sbyte)0, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32((sbyte)0));
}
OnWrite();
@@ -119,7 +119,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
PutByte(value);
@@ -150,7 +150,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
@@ -189,7 +189,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
@@ -232,7 +232,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
@@ -279,7 +279,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
PutFloat(value);
@@ -310,7 +310,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
// ReSharper disable once CompareOfFloatsByEqualityOperator
@@ -377,7 +377,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
PutBytes(value);
@@ -419,8 +419,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
var lo = BinaryPrimitives.ReadInt64LittleEndian(span[..8]);
var hi = BinaryPrimitives.ReadInt64LittleEndian(span[8..]);
+ var hash = HashUtils.Hash32(hi, HashUtils.Hash32(lo));
- _hash = HashUtils.Hash32(hi, HashUtils.Hash32(lo, _hash));
+ _hash = HashUtils.Combine(_hash, hash);
}
OnWrite();
@@ -465,7 +466,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
if (ShouldHash())
{
- _hash = HashUtils.Hash32(resBytes, _hash);
+ _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(resBytes));
}
PutBytes(resBytes);
@@ -531,7 +532,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
if (ShouldHash())
{
- _hash = HashUtils.Hash32(destination[..written], _hash);
+ _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(destination[..written]));
}
Debug.Assert(success, "success");
@@ -564,7 +565,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
}
PutDate(value);
@@ -596,7 +597,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, precision, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value,
precision));
}
PutTime(value, precision);
@@ -629,7 +630,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(value, precision, _hash);
+ _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value,
precision));
}
PutDate(value.Date);
@@ -665,8 +666,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
if (ShouldHash())
{
- _hash = HashUtils.Hash32(seconds, _hash);
- _hash = HashUtils.Hash32((long)nanos, _hash);
+ var hash = HashUtils.Hash32(nanos, HashUtils.Hash32(seconds));
+ _hash = HashUtils.Combine(_hash, hash);
}
OnWrite();
@@ -1050,7 +1051,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (ShouldHash())
{
- _hash = HashUtils.Hash32(Span<byte>.Empty, _hash);
+ _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(Span<byte>.Empty));
}
_buffer.GetSpan(1)[0] = BinaryTupleCommon.VarlenEmptyByte;
@@ -1065,7 +1066,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
if (ShouldHash())
{
- _hash = HashUtils.Hash32(span[..actualBytes], _hash);
+ _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(span[..actualBytes]));
}
_buffer.Advance(actualBytes);
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
index c386d535b2..ebb4d1c2e9 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
@@ -44,78 +44,85 @@ internal static class HashUtils
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(sbyte data, int seed) =>
Hash32Internal((ulong)(data & 0xffL), (ulong)seed, 1);
+ public static int Hash32(sbyte data) =>
Hash32Internal(unchecked((byte)data), 0, 1);
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(short data, int seed) =>
Hash32Internal((ulong)(data & 0xffffL), (ulong)seed, 2);
+ public static int Hash32(short data) =>
Hash32Internal(unchecked((ushort)data), 0, 2);
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(int data, int seed) =>
Hash32Internal((ulong)(data & 0xffffffffL), (ulong)seed, 4);
+ public static int Hash32(int data) => Hash32(data, 0);
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
+ /// <param name="seed">Seed.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(long data, int seed) =>
Hash32Internal((ulong)data, (ulong)seed, 8);
+ public static int Hash32(int data, int seed) =>
Hash32Internal(unchecked((uint)data), (ulong)seed, 4);
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(float data, int seed) =>
Hash32(BitConverter.SingleToInt32Bits(data), seed);
+ public static int Hash32(long data) => Hash32(data, 0);
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
+ /// <param name="seed">Seed.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(double data, int seed) =>
Hash32(BitConverter.DoubleToInt64Bits(data), seed);
+ public static int Hash32(long data, int seed) =>
Hash32Internal(unchecked((ulong)data), (ulong)seed, 8);
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(Span<byte> data, int seed) =>
Hash32Internal(data, (ulong)seed & 0xffffffffL);
+ public static int Hash32(float data) =>
Hash32(BitConverter.SingleToInt32Bits(data));
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(LocalDate data, int seed) =>
Hash32((long)data.Day, Hash32((long)data.Month, Hash32((long)data.Year, seed)));
+ public static int Hash32(double data) =>
Hash32(BitConverter.DoubleToInt64Bits(data));
+
+ /// <summary>
+ /// Generates 32-bit hash.
+ /// </summary>
+ /// <param name="data">Input data.</param>
+ /// <returns>Resulting hash.</returns>
+ public static int Hash32(Span<byte> data) => Hash32Internal(data, 0);
+
+ /// <summary>
+ /// Generates 32-bit hash.
+ /// </summary>
+ /// <param name="data">Input data.</param>
+ /// <returns>Resulting hash.</returns>
+ public static int Hash32(LocalDate data) => Hash32(data.Day,
Hash32(data.Month, Hash32(data.Year)));
/// <summary>
/// Generates 32-bit hash.
/// </summary>
/// <param name="data">Input data.</param>
/// <param name="precision">Precision.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(LocalTime data, int precision, int seed)
+ public static int Hash32(LocalTime data, int precision)
{
- var nanos =
(long)TemporalTypes.NormalizeNanos(data.NanosecondOfSecond, precision);
+ var nanos = TemporalTypes.NormalizeNanos(data.NanosecondOfSecond,
precision);
- return Hash32(nanos, Hash32((long)data.Second,
Hash32((long)data.Minute, Hash32((long)data.Hour, seed))));
+ return Hash32(nanos, Hash32(data.Second, Hash32(data.Minute,
Hash32(data.Hour))));
}
/// <summary>
@@ -123,9 +130,16 @@ internal static class HashUtils
/// </summary>
/// <param name="data">Input data.</param>
/// <param name="precision">Precision.</param>
- /// <param name="seed">Current hash.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(LocalDateTime data, int precision, int seed) =>
Hash32(data.TimeOfDay, precision, Hash32(data.Date, seed));
+ public static int Hash32(LocalDateTime data, int precision) =>
Combine(Hash32(data.Date), Hash32(data.TimeOfDay, precision));
+
+ /// <summary>
+ /// Combines two hashes.
+ /// </summary>
+ /// <param name="hash1">Hash 1.</param>
+ /// <param name="hash2">Hash 2.</param>
+ /// <returns>Combined hash.</returns>
+ public static int Combine(int hash1, int hash2) =>
Hash32Internal(unchecked((uint)hash1), unchecked((ulong)hash2), 4);
private static int Hash32Internal(ulong data, ulong seed, byte byteCount)
{
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java
index 6912c90e76..6c9bed0313 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientComputeTest.java
@@ -185,7 +185,7 @@ public class ItThinClientComputeTest extends
ItAbstractThinClientTest {
}
@ParameterizedTest
- @CsvSource({"1,3345", "2,3345", "3,3344", "4,3345"})
+ @CsvSource({"1,3344", "2,3345", "3,3345", "10,3344"})
void testExecuteColocatedRunsComputeJobOnKeyNode(int key, int port) {
var table = TABLE_NAME;
var keyTuple = Tuple.create().set(COLUMN_KEY, key);
diff --git
a/modules/table/src/test/java/org/apache/ignite/internal/table/ColocationHashCalculationTest.java
b/modules/table/src/test/java/org/apache/ignite/internal/table/ColocationHashCalculationTest.java
index f0aa965be3..935605659f 100644
---
a/modules/table/src/test/java/org/apache/ignite/internal/table/ColocationHashCalculationTest.java
+++
b/modules/table/src/test/java/org/apache/ignite/internal/table/ColocationHashCalculationTest.java
@@ -21,7 +21,10 @@ import static
org.apache.ignite.internal.schema.NativeTypes.INT32;
import static org.apache.ignite.internal.schema.NativeTypes.INT8;
import static org.apache.ignite.internal.schema.NativeTypes.STRING;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Random;
import java.util.stream.IntStream;
import org.apache.ignite.internal.logger.Loggers;
@@ -86,7 +89,13 @@ public class ColocationHashCalculationTest {
hashCalc.appendInt(2);
hashCalc.appendString("key_" + 3);
+ int[] hashes = new int[3];
+ hashes[0] = HashCalculator.hashByte((byte) 1);
+ hashes[1] = HashCalculator.hashInt(2);
+ hashes[2] = HashCalculator.hashString("key_" + 3);
+
assertEquals(hashCalc.hash(), colocationHash(r));
+ assertEquals(hashCalc.hash(), HashCalculator.combinedHash(hashes));
}
@Test
@@ -117,6 +126,62 @@ public class ColocationHashCalculationTest {
}
}
+ @Test
+ void collisions() {
+ var set = new HashSet<Integer>();
+ int collisions = 0;
+
+ for (var key1 = 0; key1 < 100; key1++) {
+ for (var key2 = 0; key2 < 100; key2++) {
+ for (var key3 = 0; key3 < 100; key3++) {
+ HashCalculator hashCalc = new HashCalculator();
+ hashCalc.appendInt(key1);
+ hashCalc.appendInt(key2);
+ hashCalc.appendInt(key3);
+
+ int hash = hashCalc.hash();
+ if (set.contains(hash)) {
+ collisions++;
+ } else {
+ set.add(hash);
+ }
+ }
+ }
+ }
+
+ assertEquals(125, collisions);
+ }
+
+ @Test
+ void distribution() {
+ int partitions = 100;
+ var map = new HashMap<Integer, Integer>();
+
+ for (var key1 = 0; key1 < 100; key1++) {
+ for (var key2 = 0; key2 < 100; key2++) {
+ for (var key3 = 0; key3 < 100; key3++) {
+ HashCalculator hashCalc = new HashCalculator();
+ hashCalc.appendInt(key1);
+ hashCalc.appendInt(key2);
+ hashCalc.appendInt(key3);
+
+ int hash = hashCalc.hash();
+ int partition = Math.abs(hash % partitions);
+
+ map.put(partition, map.getOrDefault(partition, 0) + 1);
+ }
+ }
+ }
+
+ var maxSkew = 326;
+
+ for (var entry : map.entrySet()) {
+ // CSV to plot: System.out.println(entry.getKey() + ", " +
entry.getValue());
+ assertTrue(entry.getValue() < 10_000 + maxSkew, "Partition " +
entry.getKey() + " keys: " + entry.getValue());
+ assertTrue(entry.getValue() > 10_000 - maxSkew, "Partition " +
entry.getKey() + " keys: " + entry.getValue());
+ }
+ }
+
private static Row generateRandomRow(Random rnd, @NotNull SchemaDescriptor
schema) throws TupleMarshallerException {
TupleMarshaller marshaller = new TupleMarshallerImpl(new
DummySchemaManagerImpl(schema));
@@ -131,7 +196,7 @@ public class ColocationHashCalculationTest {
return marshaller.marshal(t);
}
- private int colocationHash(Row r) {
+ private static int colocationHash(Row r) {
HashCalculator hashCalc = new HashCalculator();
for (Column c : r.schema().colocationColumns()) {
var scale = c.type() instanceof DecimalNativeType ?
((DecimalNativeType) c.type()).scale() : 0;