You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2023/07/06 04:12:58 UTC
[ignite-3] branch main updated: IGNITE-19756 Java client: fix colocation column order (#2289)
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 61d3b03382 IGNITE-19756 Java client: fix colocation column order (#2289)
61d3b03382 is described below
commit 61d3b03382f34a5efd568fa6a2cb0f94c2722463
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Thu Jul 6 07:12:52 2023 +0300
IGNITE-19756 Java client: fix colocation column order (#2289)
Colocation key column order can be different from the schema column order, for example:
```sql
create table test(id integer, id0 bigint, id1 varchar, primary key(id, id0)) colocate by (id1, id0)
```
* Pass `colocationIndex` instead of `isColocation` to the client
* Fix Java client to respect colocation order when computing partition awareness hash
---
.../handler/requests/table/ClientTableCommon.java | 7 +---
.../internal/client/sql/ClientAsyncResultSet.java | 4 +-
.../ignite/internal/client/table/ClientColumn.java | 43 ++++++++++----------
.../ignite/internal/client/table/ClientSchema.java | 25 ++++++------
.../ignite/internal/client/table/ClientTable.java | 22 +++++++++--
.../org/apache/ignite/client/ClientTupleTest.java | 46 +++++++++++-----------
.../ignite/client/PartitionAwarenessTest.java | 12 +++---
.../apache/ignite/internal/util/ArrayUtils.java | 2 +-
.../SerializerHandlerBenchmarksBase.cs | 6 +--
.../dotnet/Apache.Ignite.Tests/FakeServer.cs | 10 ++---
.../Proto/ColocationHashTests.cs | 2 +-
.../Serialization/ObjectSerializerHandlerTests.cs | 4 +-
.../dotnet/Apache.Ignite/Internal/Table/Column.cs | 10 ++++-
.../dotnet/Apache.Ignite/Internal/Table/Table.cs | 11 ++++--
.../runner/app/PlatformTestNodeRunner.java | 27 +++++++++++++
.../internal/table/ItThinClientColocationTest.java | 42 ++++++++++++++++++--
.../ignite/internal/schema/SchemaDescriptor.java | 33 ++++++++++++++--
17 files changed, 210 insertions(+), 96 deletions(-)
diff --git a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java
index d417a5a805..b21764f65d 100644
--- a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java
+++ b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java
@@ -22,7 +22,6 @@ import static org.apache.ignite.lang.ErrorGroups.Client.TABLE_ID_NOT_FOUND_ERR;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Set;
import org.apache.ignite.client.handler.ClientResourceRegistry;
import org.apache.ignite.internal.binarytuple.BinaryTupleContainer;
import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
@@ -60,7 +59,7 @@ public class ClientTableCommon {
* @param schemaVer Schema version.
* @param schema Schema.
*/
- public static void writeSchema(ClientMessagePacker packer, int schemaVer, SchemaDescriptor schema) {
+ static void writeSchema(ClientMessagePacker packer, int schemaVer, SchemaDescriptor schema) {
packer.packInt(schemaVer);
if (schema == null) {
@@ -72,8 +71,6 @@ public class ClientTableCommon {
var colCnt = schema.columnNames().size();
packer.packArrayHeader(colCnt);
- var colocationCols = Set.of(schema.colocationColumns());
-
for (var colIdx = 0; colIdx < colCnt; colIdx++) {
var col = schema.column(colIdx);
@@ -82,7 +79,7 @@ public class ClientTableCommon {
packer.packInt(getColumnType(col.type().spec()).ordinal());
packer.packBoolean(schema.isKeyColumn(colIdx));
packer.packBoolean(col.nullable());
- packer.packBoolean(colocationCols.contains(col));
+ packer.packInt(schema.colocationIndex(col));
packer.packInt(getDecimalScale(col.type()));
packer.packInt(getPrecision(col.type()));
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
index de22ff0d8f..f002cd5847 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
@@ -318,7 +318,7 @@ class ClientAsyncResultSet<T> implements AsyncResultSet<T> {
metaColumn.type(),
metaColumn.nullable(),
true,
- false,
+ -1,
i,
metaColumn.scale(),
metaColumn.precision());
@@ -326,7 +326,7 @@ class ClientAsyncResultSet<T> implements AsyncResultSet<T> {
schemaColumns[i] = schemaColumn;
}
- var schema = new ClientSchema(0, schemaColumns);
+ var schema = new ClientSchema(0, schemaColumns, null);
return schema.getMarshaller(mapper, TuplePart.KEY_AND_VAL);
}
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientColumn.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientColumn.java
index 699d66a4ad..8484bf69c8 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientColumn.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientColumn.java
@@ -35,8 +35,8 @@ public class ClientColumn {
/** Key column flag. */
private final boolean isKey;
- /** Key column flag. */
- private final boolean isColocation;
+ /** Colocation index. */
+ private final int colocationIndex;
/** Index of the column in the schema. */
private final int schemaIndex;
@@ -50,33 +50,34 @@ public class ClientColumn {
/**
* Constructor.
*
- * @param name Column name.
- * @param type Column type.
- * @param nullable Nullable flag.
- * @param isKey Key column flag.
- * @param isColocation Colocation column flag.
- * @param schemaIndex Index of the column in the schema.
+ * @param name Column name.
+ * @param type Column type.
+ * @param nullable Nullable flag.
+ * @param isKey Key column flag.
+ * @param colocationIndex Colocation index.
+ * @param schemaIndex Index of the column in the schema.
*/
- public ClientColumn(String name, ColumnType type, boolean nullable, boolean isKey, boolean isColocation, int schemaIndex) {
- this(name, type, nullable, isKey, isColocation, schemaIndex, 0, 0);
+ public ClientColumn(String name, ColumnType type, boolean nullable, boolean isKey, int colocationIndex, int schemaIndex) {
+ this(name, type, nullable, isKey, colocationIndex, schemaIndex, 0, 0);
}
/**
* Constructor.
*
- * @param name Column name.
- * @param type Column type code.
- * @param nullable Nullable flag.
- * @param isKey Key column flag.
+ * @param name Column name.
+ * @param type Column type code.
+ * @param nullable Nullable flag.
+ * @param isKey Key column flag.
+ * @param colocationIndex Colocation index.
* @param schemaIndex Index of the column in the schema.
- * @param scale Scale of the column, if applicable.
+ * @param scale Scale of the column, if applicable.
*/
public ClientColumn(
String name,
ColumnType type,
boolean nullable,
boolean isKey,
- boolean isColocation,
+ int colocationIndex,
int schemaIndex,
int scale,
int precision) {
@@ -87,7 +88,7 @@ public class ClientColumn {
this.type = type;
this.nullable = nullable;
this.isKey = isKey;
- this.isColocation = isColocation;
+ this.colocationIndex = colocationIndex;
this.schemaIndex = schemaIndex;
this.scale = scale;
this.precision = precision;
@@ -125,12 +126,12 @@ public class ClientColumn {
}
/**
- * Gets a value indicating whether this column is a part of colocation key.
+ * Gets the colocation index, or -1 when not part of the colocation key.
*
- * @return Value indicating whether this column is a part of colocation key.
+ * @return Index within colocation key, or -1 when not part of the colocation key.
*/
- public boolean colocation() {
- return isColocation;
+ public int colocationIndex() {
+ return colocationIndex;
}
/**
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java
index 580cdd6ea2..79c373d460 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java
@@ -17,9 +17,7 @@
package org.apache.ignite.internal.client.table;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import org.apache.ignite.internal.client.proto.TuplePart;
import org.apache.ignite.internal.marshaller.BinaryMode;
@@ -48,7 +46,7 @@ public class ClientSchema {
private final ClientColumn[] columns;
/** Colocation columns. */
- private final List<ClientColumn> colocationColumns;
+ private final ClientColumn[] colocationColumns;
/** Columns map by name. */
private final Map<String, ClientColumn> map = new HashMap<>();
@@ -56,17 +54,16 @@ public class ClientSchema {
/**
* Constructor.
*
- * @param ver Schema version.
+ * @param ver Schema version.
* @param columns Columns.
+ * @param colocationColumns Colocation columns. When null, all key columns are used.
*/
- public ClientSchema(int ver, ClientColumn[] columns) {
+ public ClientSchema(int ver, ClientColumn[] columns, ClientColumn @Nullable [] colocationColumns) {
assert ver >= 0;
assert columns != null;
this.ver = ver;
this.columns = columns;
- this.colocationColumns = new ArrayList<>();
-
var keyCnt = 0;
for (var col : columns) {
@@ -75,13 +72,17 @@ public class ClientSchema {
}
map.put(col.name(), col);
-
- if (col.colocation()) {
- colocationColumns.add(col);
- }
}
keyColumnCount = keyCnt;
+
+ if (colocationColumns == null) {
+ this.colocationColumns = new ClientColumn[keyCnt];
+
+ System.arraycopy(columns, 0, this.colocationColumns, 0, keyCnt);
+ } else {
+ this.colocationColumns = colocationColumns;
+ }
}
/**
@@ -107,7 +108,7 @@ public class ClientSchema {
*
* @return Colocation columns.
*/
- public List<ClientColumn> colocationColumns() {
+ ClientColumn[] colocationColumns() {
return colocationColumns;
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
index f8b30195c7..e71e6cf786 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTable.java
@@ -200,8 +200,8 @@ public class ClientTable implements Table {
private ClientSchema readSchema(ClientMessageUnpacker in) {
var schemaVer = in.unpackInt();
var colCnt = in.unpackArrayHeader();
-
var columns = new ClientColumn[colCnt];
+ int colocationColumnCnt = 0;
for (int i = 0; i < colCnt; i++) {
var propCnt = in.unpackArrayHeader();
@@ -212,18 +212,32 @@ public class ClientTable implements Table {
var type = ColumnTypeConverter.fromOrdinalOrThrow(in.unpackInt());
var isKey = in.unpackBoolean();
var isNullable = in.unpackBoolean();
- var isColocation = in.unpackBoolean();
+ var colocationIndex = in.unpackInt();
var scale = in.unpackInt();
var precision = in.unpackInt();
// Skip unknown extra properties, if any.
in.skipValues(propCnt - 7);
- var column = new ClientColumn(name, type, isNullable, isKey, isColocation, i, scale, precision);
+ var column = new ClientColumn(name, type, isNullable, isKey, colocationIndex, i, scale, precision);
columns[i] = column;
+
+ if (colocationIndex >= 0) {
+ colocationColumnCnt++;
+ }
+ }
+
+ var colocationColumns = colocationColumnCnt > 0 ? new ClientColumn[colocationColumnCnt] : null;
+ if (colocationColumns != null) {
+ for (ClientColumn col : columns) {
+ int idx = col.colocationIndex();
+ if (idx >= 0) {
+ colocationColumns[idx] = col;
+ }
+ }
}
- var schema = new ClientSchema(schemaVer, columns);
+ var schema = new ClientSchema(schemaVer, columns, colocationColumns);
schemas.put(schemaVer, CompletableFuture.completedFuture(schema));
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientTupleTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientTupleTest.java
index ef39887a01..724bcfc39d 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientTupleTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientTupleTest.java
@@ -58,31 +58,31 @@ import org.junit.jupiter.api.Test;
*/
public class ClientTupleTest {
private static final ClientSchema SCHEMA = new ClientSchema(1, new ClientColumn[]{
- new ClientColumn("ID", ColumnType.INT64, false, true, true, 0),
- new ClientColumn("NAME", ColumnType.STRING, false, false, false, 1)
- });
+ new ClientColumn("ID", ColumnType.INT64, false, true, 0, 0),
+ new ClientColumn("NAME", ColumnType.STRING, false, false, -1, 1)
+ }, null);
private static final ClientSchema FULL_SCHEMA = new ClientSchema(100, new ClientColumn[]{
- new ClientColumn("I8", ColumnType.INT8, false, false, false, 0),
- new ClientColumn("I16", ColumnType.INT16, false, false, false, 1),
- new ClientColumn("I32", ColumnType.INT32, false, false, false, 2),
- new ClientColumn("I64", ColumnType.INT64, false, false, false, 3),
- new ClientColumn("FLOAT", ColumnType.FLOAT, false, false, false, 4),
- new ClientColumn("DOUBLE", ColumnType.DOUBLE, false, false, false, 5),
- new ClientColumn("UUID", ColumnType.UUID, false, false, false, 6),
- new ClientColumn("STR", ColumnType.STRING, false, false, false, 7),
- new ClientColumn("BITS", ColumnType.BITMASK, false, false, false, 8),
- new ClientColumn("DATE", ColumnType.DATE, false, false, false, 9),
- new ClientColumn("TIME", ColumnType.TIME, false, false, false, 10),
- new ClientColumn("DATETIME", ColumnType.DATETIME, false, false, false, 11),
- new ClientColumn("TIMESTAMP", ColumnType.TIMESTAMP, false, false, false, 12),
- new ClientColumn("BOOL", ColumnType.BOOLEAN, false, false, false, 13),
- new ClientColumn("DECIMAL", ColumnType.DECIMAL, false, false, false, 14),
- new ClientColumn("BYTES", ColumnType.BYTE_ARRAY, false, false, false, 15),
- new ClientColumn("PERIOD", ColumnType.PERIOD, false, false, false, 16),
- new ClientColumn("DURATION", ColumnType.DURATION, false, false, false, 17),
- new ClientColumn("NUMBER", ColumnType.NUMBER, false, false, false, 18)
- });
+ new ClientColumn("I8", ColumnType.INT8, false, false, -1, 0),
+ new ClientColumn("I16", ColumnType.INT16, false, false, -1, 1),
+ new ClientColumn("I32", ColumnType.INT32, false, false, -1, 2),
+ new ClientColumn("I64", ColumnType.INT64, false, false, -1, 3),
+ new ClientColumn("FLOAT", ColumnType.FLOAT, false, false, -1, 4),
+ new ClientColumn("DOUBLE", ColumnType.DOUBLE, false, false, -1, 5),
+ new ClientColumn("UUID", ColumnType.UUID, false, false, -1, 6),
+ new ClientColumn("STR", ColumnType.STRING, false, false, -1, 7),
+ new ClientColumn("BITS", ColumnType.BITMASK, false, false, -1, 8),
+ new ClientColumn("DATE", ColumnType.DATE, false, false, -1, 9),
+ new ClientColumn("TIME", ColumnType.TIME, false, false, -1, 10),
+ new ClientColumn("DATETIME", ColumnType.DATETIME, false, false, -1, 11),
+ new ClientColumn("TIMESTAMP", ColumnType.TIMESTAMP, false, false, -1, 12),
+ new ClientColumn("BOOL", ColumnType.BOOLEAN, false, false, -1, 13),
+ new ClientColumn("DECIMAL", ColumnType.DECIMAL, false, false, -1, 14),
+ new ClientColumn("BYTES", ColumnType.BYTE_ARRAY, false, false, -1, 15),
+ new ClientColumn("PERIOD", ColumnType.PERIOD, false, false, -1, 16),
+ new ClientColumn("DURATION", ColumnType.DURATION, false, false, -1, 17),
+ new ClientColumn("NUMBER", ColumnType.NUMBER, false, false, -1, 18)
+ }, null);
private static final UUID GUID = UUID.randomUUID();
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 871eccf2cb..490f0b12c5 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
@@ -195,14 +195,14 @@ public class PartitionAwarenessTest extends AbstractClientTest {
RecordView<Tuple> recordView = table(FakeIgniteTables.TABLE_COLOCATION_KEY).recordView();
// COLO-2 is nullable and not set.
- assertOpOnNode("server-2", "get", x -> recordView.get(null, Tuple.create().set("ID", 0).set("COLO-1", "0")));
- assertOpOnNode("server-2", "get", x -> recordView.get(null, Tuple.create().set("ID", 2).set("COLO-1", "0")));
- assertOpOnNode("server-2", "get", x -> recordView.get(null, Tuple.create().set("ID", 3).set("COLO-1", "0")));
- assertOpOnNode("server-1", "get", x -> recordView.get(null, Tuple.create().set("ID", 3).set("COLO-1", "1")));
+ 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")));
// COLO-2 is set.
- 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", 2)));
+ 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)));
}
@Test
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java
index 525d6af39b..6f3245ffd4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java
@@ -186,7 +186,7 @@ public final class ArrayUtils {
* @param <T> Array element type.
* @return {@code true} if {@code null} or an empty array is provided, {@code false} otherwise.
*/
- public static <T> boolean nullOrEmpty(T[] arr) {
+ public static <T> boolean nullOrEmpty(T @Nullable[] arr) {
return arr == null || arr.length == 0;
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
index be02cf68ce..02bc0f63f1 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
@@ -46,9 +46,9 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
internal static readonly Schema Schema = new(1, 1, 1, new[]
{
- new Column(nameof(Car.Id), ColumnType.Uuid, IsNullable: false, IsColocation: true, IsKey: true, SchemaIndex: 0, Scale: 0, Precision: 0),
- new Column(nameof(Car.BodyType), ColumnType.String, IsNullable: false, IsColocation: false, IsKey: false, SchemaIndex: 1, Scale: 0, Precision: 0),
- new Column(nameof(Car.Seats), ColumnType.Int32, IsNullable: false, IsColocation: false, IsKey: false, SchemaIndex: 2, Scale: 0, Precision: 0)
+ new Column(nameof(Car.Id), ColumnType.Uuid, IsNullable: false, ColocationIndex: 0, IsKey: true, SchemaIndex: 0, Scale: 0, Precision: 0),
+ new Column(nameof(Car.BodyType), ColumnType.String, IsNullable: false, ColocationIndex: -1, IsKey: false, SchemaIndex: 1, Scale: 0, Precision: 0),
+ new Column(nameof(Car.Seats), ColumnType.Int32, IsNullable: false, ColocationIndex: -1, IsKey: false, SchemaIndex: 2, Scale: 0, Precision: 0)
});
internal static readonly byte[] SerializedData = GetSerializedData();
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs
index 5b2fd625c3..909a6a6abb 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/FakeServer.cs
@@ -498,7 +498,7 @@ namespace Apache.Ignite.Tests
writer.Write((int)ColumnType.Int32);
writer.Write(true); // Key.
writer.Write(false); // Nullable.
- writer.Write(true); // Colocation.
+ writer.Write(0); // Colocation index.
writer.Write(0); // Scale.
writer.Write(0); // Precision.
}
@@ -511,7 +511,7 @@ namespace Apache.Ignite.Tests
writer.Write((int)ColumnType.String);
writer.Write(true); // Key.
writer.Write(false); // Nullable.
- writer.Write(true); // Colocation.
+ writer.Write(0); // Colocation index.
writer.Write(0); // Scale.
writer.Write(0); // Precision.
@@ -520,7 +520,7 @@ namespace Apache.Ignite.Tests
writer.Write((int)ColumnType.Uuid);
writer.Write(true); // Key.
writer.Write(false); // Nullable.
- writer.Write(true); // Colocation.
+ writer.Write(1); // Colocation index.
writer.Write(0); // Scale.
writer.Write(0); // Precision.
}
@@ -533,7 +533,7 @@ namespace Apache.Ignite.Tests
writer.Write((int)ColumnType.String);
writer.Write(true); // Key.
writer.Write(false); // Nullable.
- writer.Write(true); // Colocation.
+ writer.Write(0); // Colocation index.
writer.Write(0); // Scale.
writer.Write(0); // Precision.
@@ -542,7 +542,7 @@ namespace Apache.Ignite.Tests
writer.Write((int)ColumnType.Uuid);
writer.Write(true); // Key.
writer.Write(false); // Nullable.
- writer.Write(false); // Colocation.
+ writer.Write(-1); // Colocation index.
writer.Write(0); // Scale.
writer.Write(0); // Precision.
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
index 68434e9352..c63da30322 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
@@ -252,7 +252,7 @@ public class ColocationHashTests : IgniteTestsBase
var scale = value is decimal d ? BitConverter.GetBytes(decimal.GetBits(d)[3])[2] : 0;
- return new Column("m_Item" + (schemaIndex + 1), colType, false, true, true, schemaIndex, Scale: scale, precision);
+ return new Column("m_Item" + (schemaIndex + 1), colType, false, true, schemaIndex, schemaIndex, Scale: scale, precision);
}
private async Task AssertClientAndServerHashesAreEqual(int timePrecision = 9, int timestampPrecision = 6, params object[] keys)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
index 4ee9a143fb..8d3eb6d22c 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
@@ -34,8 +34,8 @@ namespace Apache.Ignite.Tests.Table.Serialization
{
private static readonly Schema Schema = new(1, 1, 1, new[]
{
- new Column("Key", ColumnType.Int64, IsNullable: false, IsColocation: true, IsKey: true, SchemaIndex: 0, Scale: 0, Precision: 0),
- new Column("Val", ColumnType.String, IsNullable: false, IsColocation: false, IsKey: false, SchemaIndex: 1, Scale: 0, Precision: 0)
+ new Column("Key", ColumnType.Int64, IsNullable: false, ColocationIndex: 0, IsKey: true, SchemaIndex: 0, Scale: 0, Precision: 0),
+ new Column("Val", ColumnType.String, IsNullable: false, ColocationIndex: -1, IsKey: false, SchemaIndex: 1, Scale: 0, Precision: 0)
});
[Test]
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
index a4686e699f..fe1a5f4bbc 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Column.cs
@@ -26,8 +26,14 @@ internal record Column(
string Name,
ColumnType Type,
bool IsNullable,
- bool IsColocation,
bool IsKey,
+ int ColocationIndex,
int SchemaIndex,
int Scale,
- int Precision);
+ int Precision)
+{
+ /// <summary>
+ /// Gets a value indicating whether this column participates in colocation.
+ /// </summary>
+ public bool IsColocation => ColocationIndex >= 0;
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
index 7dfacbdbc0..1d43e02588 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
@@ -337,13 +337,13 @@ namespace Apache.Ignite.Internal.Table
var type = r.ReadInt32();
var isKey = r.ReadBoolean();
var isNullable = r.ReadBoolean();
- var isColocation = r.ReadBoolean(); // IsColocation.
+ var colocationIndex = r.ReadInt32();
var scale = r.ReadInt32();
var precision = r.ReadInt32();
r.Skip(propertyCount - expectedCount);
- var column = new Column(name, (ColumnType)type, isNullable, isColocation, isKey, i, scale, precision);
+ var column = new Column(name, (ColumnType)type, isNullable, isKey, colocationIndex, i, scale, precision);
columns[i] = column;
@@ -353,7 +353,12 @@ namespace Apache.Ignite.Internal.Table
}
}
- var schema = new Schema(schemaVersion, Id, keyColumnCount, columns);
+ var schema = new Schema(
+ schemaVersion,
+ Id,
+ keyColumnCount,
+ columns);
+
_schemas[schemaVersion] = Task.FromResult(schema);
if (_logger?.IsEnabled(LogLevel.Debug) == true)
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
index c7eb34f143..4307c87641 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
@@ -53,11 +53,14 @@ import org.apache.ignite.internal.schema.testutils.builder.SchemaBuilders;
import org.apache.ignite.internal.schema.testutils.definition.ColumnType;
import org.apache.ignite.internal.schema.testutils.definition.ColumnType.TemporalColumnType;
import org.apache.ignite.internal.schema.testutils.definition.TableDefinition;
+import org.apache.ignite.internal.table.RecordBinaryViewImpl;
import org.apache.ignite.internal.table.distributed.TableManager;
import org.apache.ignite.internal.table.impl.DummySchemaManagerImpl;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
import org.apache.ignite.internal.testframework.TestIgnitionManager;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.sql.Session;
+import org.apache.ignite.table.Table;
import org.apache.ignite.table.Tuple;
import org.jetbrains.annotations.NotNull;
@@ -547,6 +550,30 @@ public class PlatformTestNodeRunner {
}
}
+ /**
+ * Compute job that computes row colocation hash according to the current table schema.
+ */
+ @SuppressWarnings({"unused"}) // Used by platform tests.
+ private static class TableRowColocationHashJob implements ComputeJob<Integer> {
+ @Override
+ public Integer execute(JobExecutionContext context, Object... args) {
+ String tableName = (String) args[0];
+ int i = (int) args[1];
+ Tuple key = Tuple.create().set("id", 1 + i).set("id0", 2L + i).set("id1", "3" + i);
+
+ @SuppressWarnings("resource")
+ Table table = context.ignite().tables().table(tableName);
+ RecordBinaryViewImpl view = (RecordBinaryViewImpl) table.recordView();
+ TupleMarshallerImpl marsh = IgniteTestUtils.getFieldValue(view, "marsh");
+
+ try {
+ return marsh.marshal(key).colocationHash();
+ } catch (TupleMarshallerException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
/**
* Compute job that enables or disables client authentication.
*/
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItThinClientColocationTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItThinClientColocationTest.java
index 5fad6aec13..b829700efc 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItThinClientColocationTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItThinClientColocationTest.java
@@ -22,7 +22,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
+import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.client.handler.requests.table.ClientTableCommon;
import org.apache.ignite.internal.client.table.ClientColumn;
import org.apache.ignite.internal.client.table.ClientSchema;
@@ -36,16 +39,20 @@ import org.apache.ignite.internal.schema.marshaller.TupleMarshaller;
import org.apache.ignite.internal.schema.marshaller.TupleMarshallerException;
import org.apache.ignite.internal.schema.marshaller.TupleMarshallerImpl;
import org.apache.ignite.internal.schema.row.Row;
+import org.apache.ignite.internal.sql.engine.ClusterPerClassIntegrationTest;
import org.apache.ignite.internal.table.impl.DummySchemaManagerImpl;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
+import org.apache.ignite.table.Table;
import org.apache.ignite.table.Tuple;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
/**
* Tests that client and server have matching colocation logic.
*/
-public class ItThinClientColocationTest {
+public class ItThinClientColocationTest extends ClusterPerClassIntegrationTest {
@ParameterizedTest
@MethodSource("nativeTypes")
public void testClientAndServerColocationHashesAreSame(NativeType type)
@@ -68,18 +75,47 @@ public class ItThinClientColocationTest {
}
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ public void testCustomColocationColumnOrder(boolean reverseColocationOrder) throws Exception {
+ String tableName = "testCustomColocationColumnOrder_" + reverseColocationOrder;
+
+ sql("create table " + tableName + "(id integer, id0 bigint, id1 varchar, v INTEGER, "
+ + "primary key(id, id0, id1)) colocate by " + (reverseColocationOrder ? "(id1, id0)" : "(id0, id1)"));
+
+ Table serverTable = CLUSTER_NODES.get(0).tables().table(tableName);
+ RecordBinaryViewImpl serverView = (RecordBinaryViewImpl) serverTable.recordView();
+ TupleMarshallerImpl marsh = IgniteTestUtils.getFieldValue(serverView, "marsh");
+
+ try (IgniteClient client = IgniteClient.builder().addresses("localhost").build()) {
+ // Perform get to populate schema.
+ Table clientTable = client.tables().table(tableName);
+ clientTable.recordView().get(null, Tuple.create().set("id", 1).set("id0", 2L).set("id1", "3"));
+
+ Map<Integer, CompletableFuture<ClientSchema>> schemas = IgniteTestUtils.getFieldValue(clientTable, "schemas");
+
+ for (int i = 0; i < 100; i++) {
+ Tuple key = Tuple.create().set("id", 1 + i).set("id0", 2L + i).set("id1", Integer.toString(3 + i));
+ int serverHash = marsh.marshal(key).colocationHash();
+ int clientHash = ClientTupleSerializer.getColocationHash(schemas.values().iterator().next().get(), key);
+
+ assertEquals(serverHash, clientHash);
+ }
+ }
+ }
+
private static ClientSchema clientSchema(NativeType type, String columnName) {
var clientColumn = new ClientColumn(
columnName,
ClientTableCommon.getColumnType(type.spec()),
false,
true,
- true,
+ -1,
0,
ClientTableCommon.getDecimalScale(type),
ClientTableCommon.getPrecision(type));
- return new ClientSchema(0, new ClientColumn[]{clientColumn});
+ return new ClientSchema(0, new ClientColumn[]{clientColumn}, null);
}
private static TupleMarshallerImpl tupleMarshaller(NativeType type, String columnName) {
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
index 95518c5e62..b9aaa5f47a 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.schema;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
@@ -48,6 +49,9 @@ public class SchemaDescriptor {
/** Colocation columns. */
private final Column[] colocationCols;
+ /** Colocation columns. */
+ private final @Nullable Map<Column, Integer> colocationColIndexes;
+
/** Mapping 'Column name' -> Column. */
private final Map<String, Column> colMap;
@@ -76,7 +80,7 @@ public class SchemaDescriptor {
* @param colocationCols Colocation column names.
* @param valCols Value columns.
*/
- public SchemaDescriptor(int ver, Column[] keyCols, @Nullable String[] colocationCols, Column[] valCols) {
+ public SchemaDescriptor(int ver, Column[] keyCols, String @Nullable[] colocationCols, Column[] valCols) {
assert keyCols.length > 0 : "No key columns are configured.";
this.ver = ver;
@@ -102,8 +106,19 @@ public class SchemaDescriptor {
// Preserving key chunk column order is not actually required.
// It is sufficient to has same column order for all nodes.
- this.colocationCols = (ArrayUtils.nullOrEmpty(colocationCols)) ? this.keyCols.columns() :
- Arrays.stream(colocationCols).map(colMap::get).toArray(Column[]::new);
+ if (ArrayUtils.nullOrEmpty(colocationCols)) {
+ this.colocationCols = this.keyCols.columns();
+ this.colocationColIndexes = null;
+ } else {
+ this.colocationCols = new Column[colocationCols.length];
+ this.colocationColIndexes = new HashMap<>(colocationCols.length);
+
+ for (int i = 0; i < colocationCols.length; i++) {
+ Column col = colMap.get(colocationCols[i]);
+ this.colocationCols[i] = col;
+ this.colocationColIndexes.put(col, i);
+ }
+ }
}
/**
@@ -185,6 +200,18 @@ public class SchemaDescriptor {
return colocationCols;
}
+ /**
+ * Get colocation index of the specified column.
+ *
+ * @param col Column.
+ * @return Index in the colocationColumns array, or -1 when not applicable.
+ */
+ public int colocationIndex(Column col) {
+ return colocationColIndexes == null
+ ? -1
+ : colocationColIndexes.getOrDefault(col, -1);
+ }
+
/**
* Get value columns.
*