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 7e1e0cf74c IGNITE-19914 .NET: Fix colocation column order (#2308)
7e1e0cf74c is described below
commit 7e1e0cf74c741a8d9c7c9e678535a4ca04fb8dc9
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Wed Jul 12 13:46:16 2023 +0300
IGNITE-19914 .NET: Fix colocation column order (#2308)
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)
```
Instead of computing the hash as we go in `BinaryTupleBuilder`, store
individual hashes in the right order into a preallocated area in the buffer and
combine them later.
---
.../SerializerHandlerBenchmarksBase.cs | 17 ++-
.../Proto/ColocationHashTests.cs | 61 ++++++++--
.../Serialization/ObjectSerializerHandlerTests.cs | 15 ++-
.../Proto/BinaryTuple/BinaryTupleBuilder.cs | 125 +++++++++++++--------
.../BinaryTuple/IHashedColumnIndexProvider.cs | 11 +-
.../Apache.Ignite/Internal/Proto/HashUtils.cs | 2 +-
.../Apache.Ignite/Internal/Table/DataStreamer.cs | 2 +-
.../dotnet/Apache.Ignite/Internal/Table/Schema.cs | 9 +-
.../Serialization/IRecordSerializerHandler.cs | 2 +-
.../dotnet/Apache.Ignite/Internal/Table/Table.cs | 15 ++-
10 files changed, 184 insertions(+), 75 deletions(-)
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 02bc0f63f1..bc94378f00 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
@@ -44,12 +44,17 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
[nameof(Car.Seats)] = Object.Seats
};
- internal static readonly Schema Schema = new(1, 1, 1, new[]
- {
- 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 Schema Schema = new(
+ Version: 1,
+ TableId: 1,
+ KeyColumnCount: 1,
+ ColocationColumnCount: 1,
+ Columns: new[]
+ {
+ 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/Proto/ColocationHashTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
index c63da30322..d0187bd90f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/ColocationHashTests.cs
@@ -29,6 +29,7 @@ using Ignite.Compute;
using Ignite.Sql;
using Ignite.Table;
using Internal.Buffers;
+using Internal.Proto;
using Internal.Proto.BinaryTuple;
using Internal.Proto.MsgPack;
using Internal.Table;
@@ -41,7 +42,11 @@ using NUnit.Framework;
/// </summary>
public class ColocationHashTests : IgniteTestsBase
{
- private const string ColocationHashJob =
"org.apache.ignite.internal.runner.app.PlatformTestNodeRunner$ColocationHashJob";
+ private const string PlatformTestNodeRunner =
"org.apache.ignite.internal.runner.app.PlatformTestNodeRunner";
+
+ private const string ColocationHashJob = PlatformTestNodeRunner +
"$ColocationHashJob";
+
+ private const string TableRowColocationHashJob = PlatformTestNodeRunner +
"$TableRowColocationHashJob";
private static readonly object[] TestCases =
{
@@ -161,16 +166,56 @@ public class ColocationHashTests : IgniteTestsBase
}
}
+ [Test]
+ public async Task TestCustomColocationColumnOrder([Values(true, false)]
bool reverseColocationOrder)
+ {
+ var tableName =
$"{nameof(TestCustomColocationColumnOrder)}_{reverseColocationOrder}";
+ var sql = $"create table if not exists {tableName} " +
+ $"(id integer, id0 bigint, id1 varchar, v INTEGER, primary
key(id, id0, id1)) " +
+ $"colocate by {(reverseColocationOrder ? "(id1, id0)" :
"(id0, id1)")}";
+
+ await Client.Sql.ExecuteAsync(null, sql);
+
+ // Perform get to populate schema.
+ var table = await Client.Tables.GetTableAsync(tableName);
+ var view = table!.RecordBinaryView;
+ await view.GetAsync(null, new IgniteTuple{["id"] = 1, ["id0"] = 2L,
["id1"] = "3", ["v"] = 4});
+
+ var ser = view.GetFieldValue<RecordSerializer<IIgniteTuple>>("_ser");
+ var schemas = table.GetFieldValue<IDictionary<int,
Task<Schema>>>("_schemas");
+ var schema = schemas[1].GetAwaiter().GetResult();
+ var clusterNodes = await Client.GetClusterNodesAsync();
+
+ for (int i = 0; i < 100; i++)
+ {
+ var key = new IgniteTuple { ["id"] = 1 + i, ["id0"] = 2L + i,
["id1"] = "3" + i, ["v"] = 4 + i };
+
+ using var writer = ProtoCommon.GetMessageWriter();
+ var clientColocationHash = ser.Write(writer, null, schema, key);
+
+ var serverColocationHash = await Client.Compute.ExecuteAsync<int>(
+ clusterNodes,
+ Array.Empty<DeploymentUnit>(),
+ TableRowColocationHashJob,
+ tableName,
+ i);
+
+ Assert.AreEqual(serverColocationHash, clientColocationHash,
key.ToString());
+ }
+ }
+
private static (byte[] Bytes, int Hash)
WriteAsBinaryTuple(IReadOnlyCollection<object> arr, int timePrecision, int
timestampPrecision)
{
- using var builder = new BinaryTupleBuilder(arr.Count * 3,
hashedColumnsPredicate: new TestIndexProvider(x => x % 3 == 2));
+ using var builder = new BinaryTupleBuilder(
+ numElements: arr.Count * 3,
+ hashedColumnsPredicate: new TestIndexProvider(x => x % 3 == 2 ? x
/ 3 : -1, arr.Count));
foreach (var obj in arr)
{
builder.AppendObjectWithType(obj, timePrecision,
timestampPrecision);
}
- return (builder.Build().ToArray(), builder.Hash);
+ return (builder.Build().ToArray(), Hash: builder.GetHash());
}
private static int WriteAsIgniteTuple(IReadOnlyCollection<object> arr, int
timePrecision, int timestampPrecision)
@@ -183,7 +228,7 @@ public class ColocationHashTests : IgniteTestsBase
igniteTuple["m_Item" + i++] = obj;
}
- var builder = new BinaryTupleBuilder(arr.Count,
hashedColumnsPredicate: new TestIndexProvider(_ => true));
+ var builder = new BinaryTupleBuilder(arr.Count,
hashedColumnsPredicate: new TestIndexProvider(idx => idx, arr.Count));
try
{
@@ -191,7 +236,7 @@ public class ColocationHashTests : IgniteTestsBase
var noValueSet = new byte[arr.Count].AsSpan();
TupleSerializerHandler.Instance.Write(ref builder, igniteTuple,
schema, arr.Count, noValueSet);
- return builder.Hash;
+ return builder.GetHash();
}
finally
{
@@ -216,7 +261,7 @@ public class ColocationHashTests : IgniteTestsBase
{
var columns = arr.Select((obj, ci) => GetColumn(obj, ci,
timePrecision, timestampPrecision)).ToArray();
- return new Schema(Version: 0, 0, arr.Count, columns);
+ return new Schema(Version: 0, 0, arr.Count, arr.Count, columns);
}
private static Column GetColumn(object value, int schemaIndex, int
timePrecision, int timestampPrecision)
@@ -291,8 +336,8 @@ public class ColocationHashTests : IgniteTestsBase
timestampPrecision);
}
- private record TestIndexProvider(Func<int, bool> Delegate) :
IHashedColumnIndexProvider
+ private record TestIndexProvider(Func<int, int> ColumnOrderDelegate, int
HashedColumnCount) : IHashedColumnIndexProvider
{
- public bool IsHashedColumnIndex(int index) => Delegate(index);
+ public int HashedColumnOrder(int index) => ColumnOrderDelegate(index);
}
}
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 8d3eb6d22c..58a08e61aa 100644
---
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
+++
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
@@ -32,11 +32,16 @@ namespace Apache.Ignite.Tests.Table.Serialization
// ReSharper disable NotAccessedPositionalProperty.Local
public class ObjectSerializerHandlerTests
{
- private static readonly Schema Schema = new(1, 1, 1, new[]
- {
- 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)
- });
+ private static readonly Schema Schema = new(
+ Version: 1,
+ TableId: 1,
+ KeyColumnCount: 1,
+ ColocationColumnCount: 1,
+ Columns: new[]
+ {
+ 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]
public void TestWrite()
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 0b88821997..7540b6c060 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
@@ -22,6 +22,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
using System.Collections;
using System.Diagnostics;
using System.Numerics;
+ using System.Runtime.InteropServices;
using Buffers;
using Ignite.Sql;
using NodaTime;
@@ -53,9 +54,6 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/** Current element. */
private int _elementIndex;
- /** Current element. */
- private int _hash;
-
/// <summary>
/// Initializes a new instance of the <see cref="BinaryTupleBuilder"/>
struct.
/// </summary>
@@ -72,11 +70,13 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
_numElements = numElements;
_hashedColumnsPredicate = hashedColumnsPredicate;
- _hash = 0;
_buffer = new();
_elementIndex = 0;
- _entryBase = BinaryTupleCommon.HeaderSize;
+ // Reserve buffer for individual hash codes.
+ _entryBase = _hashedColumnsPredicate != null
+ ? BinaryTupleCommon.HeaderSize +
_hashedColumnsPredicate.HashedColumnCount * 4
+ : BinaryTupleCommon.HeaderSize;
_entrySize = totalValueSize < 0
? 4
@@ -96,16 +96,34 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <summary>
/// Gets the hash from column values according to specified <see
cref="IHashedColumnIndexProvider"/>.
/// </summary>
- public int Hash => _hash;
+ /// <returns>Column hash according to specified <see
cref="IHashedColumnIndexProvider"/>.</returns>
+ public int GetHash()
+ {
+ if (_hashedColumnsPredicate == null)
+ {
+ return 0;
+ }
+
+ var hash = 0;
+ var hashes = GetHashSpan();
+
+ for (var i = 0; i < _hashedColumnsPredicate.HashedColumnCount; i++)
+ {
+ var colHash = hashes[i];
+ hash = HashUtils.Combine(hash, colHash);
+ }
+
+ return hash;
+ }
/// <summary>
/// Appends a null value.
/// </summary>
public void AppendNull()
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32((sbyte)0));
+ PutHash(hashOrder, HashUtils.Hash32((sbyte)0));
}
OnWrite();
@@ -117,9 +135,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendByte(sbyte value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
PutByte(value);
@@ -148,9 +166,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendShort(short value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
@@ -187,9 +205,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendInt(int value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
@@ -230,9 +248,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendLong(long value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
if (value >= sbyte.MinValue && value <= sbyte.MaxValue)
@@ -277,9 +295,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendFloat(float value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
PutFloat(value);
@@ -308,9 +326,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendDouble(double value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
// ReSharper disable once CompareOfFloatsByEqualityOperator
@@ -375,9 +393,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendBytes(Span<byte> value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
PutBytes(value);
@@ -415,13 +433,13 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
var span = GetSpan(16);
UuidSerializer.Write(value, span);
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
var lo = BinaryPrimitives.ReadInt64LittleEndian(span[..8]);
var hi = BinaryPrimitives.ReadInt64LittleEndian(span[8..]);
var hash = HashUtils.Hash32(hi, HashUtils.Hash32(lo));
- _hash = HashUtils.Combine(_hash, hash);
+ PutHash(hashOrder, hash);
}
OnWrite();
@@ -464,9 +482,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
var resBytes = arr.AsSpan()[..size];
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(resBytes));
+ PutHash(hashOrder, HashUtils.Hash32(resBytes));
}
PutBytes(resBytes);
@@ -530,9 +548,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
var destination = GetSpan(size);
var success = value.TryWriteBytes(destination, out int written,
isBigEndian: true);
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(destination[..written]));
+ PutHash(hashOrder, HashUtils.Hash32(destination[..written]));
}
Debug.Assert(success, "success");
@@ -563,9 +581,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendDate(LocalDate value)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value));
+ PutHash(hashOrder, HashUtils.Hash32(value));
}
PutDate(value);
@@ -595,9 +613,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="precision">Precision.</param>
public void AppendTime(LocalTime value, int precision)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value,
precision));
+ PutHash(hashOrder, HashUtils.Hash32(value, precision));
}
PutTime(value, precision);
@@ -628,9 +646,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="precision">Precision.</param>
public void AppendDateTime(LocalDateTime value, int precision)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash, HashUtils.Hash32(value,
precision));
+ PutHash(hashOrder, HashUtils.Hash32(value, precision));
}
PutDate(value.Date);
@@ -664,10 +682,10 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
var (seconds, nanos) = PutTimestamp(value, precision);
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
var hash = HashUtils.Hash32(nanos, HashUtils.Hash32(seconds));
- _hash = HashUtils.Combine(_hash, hash);
+ PutHash(hashOrder, hash);
}
OnWrite();
@@ -696,7 +714,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendDuration(Duration value)
{
- if (ShouldHash())
+ if (GetHashOrder() is not null)
{
// Colocation keys can't include Duration.
throw new NotSupportedException("Duration hashing is not
supported.");
@@ -728,7 +746,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <param name="value">Value.</param>
public void AppendPeriod(Period value)
{
- if (ShouldHash())
+ if (GetHashOrder() is not null)
{
// Colocation keys can't include Period.
throw new NotSupportedException("Period hashing is not
supported.");
@@ -953,7 +971,8 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// <returns>Resulting memory.</returns>
public Memory<byte> Build()
{
- int offset = 0;
+ int baseOffset = _entryBase - BinaryTupleCommon.HeaderSize;
+ int offset = baseOffset;
int valueSize = _buffer.Position - _valueBase;
byte flags = BinaryTupleCommon.ValueSizeToFlags(valueSize);
@@ -991,7 +1010,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
}
}
- offset = (_entrySize - desiredEntrySize) * _numElements;
+ offset = baseOffset + (_entrySize - desiredEntrySize) *
_numElements;
}
_buffer.WriteByte(flags, offset);
@@ -1049,9 +1068,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
if (value.Length == 0)
{
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder)
{
- _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(Span<byte>.Empty));
+ PutHash(hashOrder, HashUtils.Hash32(Span<byte>.Empty));
}
_buffer.GetSpan(1)[0] = BinaryTupleCommon.VarlenEmptyByte;
@@ -1063,10 +1082,11 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
var span = _buffer.GetSpan(maxByteCount);
var actualBytes = ProtoCommon.StringEncoding.GetBytes(value, span);
+ span = span[..actualBytes];
- if (ShouldHash())
+ if (GetHashOrder() is { } hashOrder2)
{
- _hash = HashUtils.Combine(_hash,
HashUtils.Hash32(span[..actualBytes]));
+ PutHash(hashOrder2, HashUtils.Hash32(span));
}
_buffer.Advance(actualBytes);
@@ -1236,6 +1256,21 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
return span;
}
- private bool ShouldHash() =>
_hashedColumnsPredicate?.IsHashedColumnIndex(_elementIndex) == true;
+ private int? GetHashOrder() =>
_hashedColumnsPredicate?.HashedColumnOrder(_elementIndex) switch
+ {
+ null or < 0 => null,
+ { } order => order
+ };
+
+ private void PutHash(int index, int hash)
+ {
+ Debug.Assert(_hashedColumnsPredicate != null,
"_hashedColumnsPredicate != null");
+ Debug.Assert(index >= 0, "index >= 0");
+ Debug.Assert(index < _hashedColumnsPredicate.HashedColumnCount,
"index < _hashedColumnsPredicate.HashedColumnCount");
+
+ GetHashSpan()[index] = hash;
+ }
+
+ private Span<int> GetHashSpan() => MemoryMarshal.Cast<byte,
int>(_buffer.GetWrittenMemory().Span);
}
}
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/IHashedColumnIndexProvider.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/IHashedColumnIndexProvider.cs
index 2aef8d986b..6f76e6fc61 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/IHashedColumnIndexProvider.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/IHashedColumnIndexProvider.cs
@@ -23,9 +23,14 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple;
internal interface IHashedColumnIndexProvider
{
/// <summary>
- /// Gets a value indicating whether the value of a column at specified
index should be hashed.
+ /// Gets the number of hashed columns.
+ /// </summary>
+ int HashedColumnCount { get; }
+
+ /// <summary>
+ /// Gets a value indicating the hash order for the column at specified
index.
/// </summary>
/// <param name="index">Column index.</param>
- /// <returns>True when hashed column; false otherwise.</returns>
- bool IsHashedColumnIndex(int index);
+ /// <returns>The order of the column within the hash, when applicable; -1
otherwise.</returns>
+ int HashedColumnOrder(int index);
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
index ebb4d1c2e9..cebc6b9fd6 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/HashUtils.cs
@@ -103,7 +103,7 @@ internal static class HashUtils
/// </summary>
/// <param name="data">Input data.</param>
/// <returns>Resulting hash.</returns>
- public static int Hash32(Span<byte> data) => Hash32Internal(data, 0);
+ public static int Hash32(Span<byte> data) => data.IsEmpty ? 0 :
Hash32Internal(data, 0);
/// <summary>
/// Generates 32-bit hash.
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
index 13b43ddb36..63a702f992 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/DataStreamer.cs
@@ -157,7 +157,7 @@ internal static class DataStreamer
var partition = partitionAssignment == null
? string.Empty // Default connection.
- : partitionAssignment[Math.Abs(tupleBuilder.Hash %
partitionAssignment.Length)];
+ : partitionAssignment[Math.Abs(tupleBuilder.GetHash() %
partitionAssignment.Length)];
var batch = GetOrCreateBatch(partition);
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
index c4bf0fac06..8fa9c84238 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Schema.cs
@@ -26,11 +26,13 @@ namespace Apache.Ignite.Internal.Table
/// <param name="Version">Version.</param>
/// <param name="TableId">Table id.</param>
/// <param name="KeyColumnCount">Key column count.</param>
+ /// <param name="ColocationColumnCount">Colocation column count.</param>
/// <param name="Columns">Columns in schema order.</param>
internal sealed record Schema(
int Version,
int TableId,
int KeyColumnCount,
+ int ColocationColumnCount,
IReadOnlyList<Column> Columns) : IHashedColumnIndexProvider
{
/// <summary>
@@ -39,6 +41,11 @@ namespace Apache.Ignite.Internal.Table
public int ValueColumnCount => Columns.Count - KeyColumnCount;
/// <inheritdoc/>
- public bool IsHashedColumnIndex(int index) => index < KeyColumnCount
&& Columns[index].IsColocation;
+ public int HashedColumnCount => ColocationColumnCount;
+
+ /// <inheritdoc/>
+ public int HashedColumnOrder(int index) => index < KeyColumnCount
+ ? Columns[index].ColocationIndex
+ : -1;
}
}
diff --git
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
index 785f39899b..4b8219bb59 100644
---
a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
+++
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/IRecordSerializerHandler.cs
@@ -60,7 +60,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
var binaryTupleMemory = tupleBuilder.Build();
writer.Write(binaryTupleMemory.Span);
- return tupleBuilder.Hash;
+ return tupleBuilder.GetHash();
}
finally
{
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
index 1d43e02588..859872dcb0 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Table.cs
@@ -323,6 +323,7 @@ namespace Apache.Ignite.Internal.Table
var schemaVersion = r.ReadInt32();
var columnCount = r.ReadArrayHeader();
var keyColumnCount = 0;
+ var colocationColumnCount = 0;
var columns = new Column[columnCount];
@@ -351,13 +352,19 @@ namespace Apache.Ignite.Internal.Table
{
keyColumnCount++;
}
+
+ if (colocationIndex >= 0)
+ {
+ colocationColumnCount++;
+ }
}
var schema = new Schema(
- schemaVersion,
- Id,
- keyColumnCount,
- columns);
+ Version: schemaVersion,
+ TableId: Id,
+ KeyColumnCount: keyColumnCount,
+ ColocationColumnCount: colocationColumnCount,
+ Columns: columns);
_schemas[schemaVersion] = Task.FromResult(schema);