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 2022/09/09 11:35:21 UTC
[ignite-3] branch main updated: IGNITE-17297 Adopt BinaryTuple format in the client protocol (#1058)
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 91f9cee9ab IGNITE-17297 Adopt BinaryTuple format in the client protocol (#1058)
91f9cee9ab is described below
commit 91f9cee9ab7130bad9b24458d563f4b06ec835a7
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Fri Sep 9 14:35:15 2022 +0300
IGNITE-17297 Adopt BinaryTuple format in the client protocol (#1058)
Encode tuples and rows using BinaryTuple format in the thin client protocol. Update server, Java client, .NET client.
---
modules/binary-tuple/pom.xml | 25 ----
.../internal/binarytuple/BinaryTupleBuilder.java | 9 ++
modules/client-common/pom.xml | 4 +
.../client/proto/ClientBinaryTupleUtils.java | 120 +++++++++++++++++
.../internal/client/proto/ClientMessagePacker.java | 32 ++++-
.../client/proto/ClientMessageUnpacker.java | 23 ++++
.../proto/ClientMessagePackerUnpackerTest.java | 2 +-
.../handler/requests/table/ClientTableCommon.java | 146 ++++++++-------------
modules/client/pom.xml | 5 +
.../internal/client/table/ClientKeyValueView.java | 49 +++++--
.../client/table/ClientRecordSerializer.java | 69 +++++++---
.../ignite/internal/client/table/ClientSchema.java | 7 -
.../client/table/ClientTupleSerializer.java | 144 +++++++++++++++++---
.../marshaller/ClientMarshallerReader.java | 71 ++++++----
.../marshaller/ClientMarshallerWriter.java | 47 ++++---
.../ignite/client/AbstractClientTableTest.java | 2 +-
.../ignite/client/ClientKeyValueViewTest.java | 14 +-
.../apache/ignite/client/ClientRecordViewTest.java | 14 +-
.../org/apache/ignite/client/ClientTableTest.java | 3 +-
.../org/apache/ignite/client/RetryPolicyTest.java | 12 +-
.../ignite/client/fakes/FakeIgniteTables.java | 4 +-
.../SerializerHandlerBenchmarksBase.cs | 4 +-
.../SerializerHandlerReadBenchmarks.cs | 40 ++++--
.../SerializerHandlerWriteBenchmarks.cs | 13 +-
.../Apache.Ignite.Tests/Compute/ComputeTests.cs | 4 +-
.../Proto/BinaryTuple/BinaryTupleTests.cs | 86 +++++++-----
.../Table/CustomTestIgniteTuple.cs | 2 +-
.../Apache.Ignite.Tests/Table/IgniteTupleTests.cs | 2 +-
.../Table/{CustomTestIgniteTuple.cs => Poco2.cs} | 37 +++---
.../Table/RecordViewPocoTests.cs | 45 +++++++
.../Serialization/ObjectSerializerHandlerTests.cs | 51 +++----
.../Proto/BinaryTuple/BinaryTupleBuilder.cs | 104 +++++++++++++--
.../Proto/BinaryTuple/BinaryTupleCommon.cs | 15 ++-
.../Proto/BinaryTuple/BinaryTupleReader.cs | 43 +++++-
.../Internal/Proto/MessagePackReaderExtensions.cs | 14 ++
.../Internal/Proto/MessagePackWriterExtensions.cs | 18 +++
.../Serialization/BinaryTupleBuilderExtensions.cs} | 36 ++---
.../Table/Serialization/BinaryTupleMethods.cs | 99 ++++++++++++++
.../Table/Serialization/ByteSpanExtensions.cs} | 35 ++---
.../Table/Serialization/MessagePackMethods.cs | 131 ------------------
.../Table/Serialization/ObjectSerializerHandler.cs | 118 +++++++++++++----
.../Table/Serialization/TupleSerializerHandler.cs | 50 +++----
42 files changed, 1144 insertions(+), 605 deletions(-)
diff --git a/modules/binary-tuple/pom.xml b/modules/binary-tuple/pom.xml
index 98effa4a21..6a3388b71f 100644
--- a/modules/binary-tuple/pom.xml
+++ b/modules/binary-tuple/pom.xml
@@ -52,29 +52,4 @@
</dependency>
</dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <dependencies>
- <dependency>
- <groupId>org.apache.ignite</groupId>
- <artifactId>ignite-configuration-annotation-processor</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
- <configuration>
- <annotationProcessorPaths>
- <path>
- <groupId>org.apache.ignite</groupId>
- <artifactId>ignite-configuration-annotation-processor</artifactId>
- <version>${project.version}</version>
- </path>
- </annotationProcessorPaths>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
</project>
diff --git a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
index 49737f650e..59b246aaa8 100644
--- a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
+++ b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
@@ -142,6 +142,15 @@ public class BinaryTupleBuilder {
return proceed();
}
+ /**
+ * Append a default (empty) value for the current element.
+ *
+ * @return {@code this} for chaining.
+ */
+ public BinaryTupleBuilder appendDefault() {
+ return proceed();
+ }
+
/**
* Append a value for the current element.
*
diff --git a/modules/client-common/pom.xml b/modules/client-common/pom.xml
index 582912c670..ea1a667d2f 100644
--- a/modules/client-common/pom.xml
+++ b/modules/client-common/pom.xml
@@ -119,6 +119,10 @@
<artifactId>jmh-generator-annprocess</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-binary-tuple</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientBinaryTupleUtils.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientBinaryTupleUtils.java
new file mode 100644
index 0000000000..919bc74e7d
--- /dev/null
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientBinaryTupleUtils.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.client.proto;
+
+import static org.apache.ignite.lang.ErrorGroups.Client.PROTOCOL_ERR;
+
+import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
+import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.table.Tuple;
+
+/**
+ * Client binary tuple utils.
+ */
+public class ClientBinaryTupleUtils {
+ /**
+ * Reads a binary tuple column and sets the value in the specified tuple.
+ *
+ * @param reader Binary tuple reader.
+ * @param readerIndex Column index in the binary tuple.
+ * @param tuple Target tuple.
+ * @param columnName Column name.
+ * @param clientDataType Client data type (see {@link ClientDataType}).
+ */
+ public static void readAndSetColumnValue(
+ BinaryTupleReader reader,
+ int readerIndex,
+ Tuple tuple,
+ String columnName,
+ int clientDataType) {
+ if (reader.hasNullValue(readerIndex)) {
+ tuple.set(columnName, null);
+ return;
+ }
+
+ switch (clientDataType) {
+ case ClientDataType.INT8:
+ tuple.set(columnName, reader.byteValue(readerIndex));
+ break;
+
+ case ClientDataType.INT16:
+ tuple.set(columnName, reader.shortValue(readerIndex));
+ break;
+
+ case ClientDataType.INT32:
+ tuple.set(columnName, reader.intValue(readerIndex));
+ break;
+
+ case ClientDataType.INT64:
+ tuple.set(columnName, reader.longValue(readerIndex));
+ break;
+
+ case ClientDataType.FLOAT:
+ tuple.set(columnName, reader.floatValue(readerIndex));
+ break;
+
+ case ClientDataType.DOUBLE:
+ tuple.set(columnName, reader.doubleValue(readerIndex));
+ break;
+
+ case ClientDataType.DECIMAL:
+ // TODO IGNITE-17632: Get scale from schema.
+ tuple.set(columnName, reader.decimalValue(readerIndex, 100));
+ break;
+
+ case ClientDataType.UUID:
+ tuple.set(columnName, reader.uuidValue(readerIndex));
+ break;
+
+ case ClientDataType.STRING:
+ tuple.set(columnName, reader.stringValue(readerIndex));
+ break;
+
+ case ClientDataType.BYTES:
+ tuple.set(columnName, reader.bytesValue(readerIndex));
+ break;
+
+ case ClientDataType.BITMASK:
+ tuple.set(columnName, reader.bitmaskValue(readerIndex));
+ break;
+
+ case ClientDataType.NUMBER:
+ tuple.set(columnName, reader.numberValue(readerIndex));
+ break;
+
+ case ClientDataType.DATE:
+ tuple.set(columnName, reader.dateValue(readerIndex));
+ break;
+
+ case ClientDataType.TIME:
+ tuple.set(columnName, reader.timeValue(readerIndex));
+ break;
+
+ case ClientDataType.DATETIME:
+ tuple.set(columnName, reader.dateTimeValue(readerIndex));
+ break;
+
+ case ClientDataType.TIMESTAMP:
+ tuple.set(columnName, reader.timestampValue(readerIndex));
+ break;
+
+ default:
+ throw new IgniteException(PROTOCOL_ERR, "Unsupported type: " + clientDataType);
+ }
+ }
+}
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java
index 2d0ff7063f..694cb494db 100644
--- a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessagePacker.java
@@ -25,6 +25,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
@@ -34,6 +35,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.BitSet;
import java.util.UUID;
+import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
/**
* ByteBuf-based MsgPack implementation. Replaces {@link org.msgpack.core.MessagePacker} to avoid
@@ -468,6 +470,20 @@ public class ClientMessagePacker implements AutoCloseable {
buf.writeBytes(src);
}
+ /**
+ * Writes a byte buffer to the output.
+ *
+ * <p>This method is used with {@link #packRawStringHeader(int)} or {@link #packBinaryHeader(int)}
+ * methods.
+ *
+ * @param src the data to add.
+ */
+ public void writePayload(ByteBuffer src) {
+ assert !closed : "Packer is closed";
+
+ buf.writeBytes(src);
+ }
+
/**
* Writes a byte array to the output.
*
@@ -861,7 +877,7 @@ public class ClientMessagePacker implements AutoCloseable {
packInt(ClientDataType.BITMASK);
packBitSet((BitSet) obj);
} else {
- throw new UnsupportedOperationException("Custom objects are not supported");
+ throw new UnsupportedOperationException("Custom objects are not supported: " + cls);
}
}
@@ -887,6 +903,20 @@ public class ClientMessagePacker implements AutoCloseable {
}
}
+ /**
+ * Packs binary tuple with no-value set.
+ *
+ * @param builder Builder.
+ * @param noValueSet No-value bit set.
+ */
+ public void packBinaryTuple(BinaryTupleBuilder builder, BitSet noValueSet) {
+ packBitSet(noValueSet);
+
+ var buf = builder.build();
+ packBinaryHeader(buf.limit() - buf.position());
+ writePayload(buf);
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
index 3201a9a75b..6d90259bc1 100644
--- a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
@@ -39,6 +39,8 @@ import static org.msgpack.core.MessagePack.Code;
import io.netty.buffer.ByteBuf;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
@@ -625,6 +627,27 @@ public class ClientMessageUnpacker implements AutoCloseable {
return res;
}
+ /**
+ * Reads a binary value.
+ * NOTE: Exposes internal pooled buffer to avoid copying. The buffer is not valid after current instance is closed.
+ *
+ * @return Payload bytes.
+ */
+ public ByteBuffer readBinaryUnsafe() {
+ assert refCnt > 0 : "Unpacker is closed";
+
+ var length = unpackBinaryHeader();
+ var idx = buf.readerIndex();
+
+ // Note: this may or may not avoid the actual copy.
+ ByteBuffer byteBuffer = buf.internalNioBuffer(idx, length).slice();
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ buf.readerIndex(idx + length);
+
+ return byteBuffer;
+ }
+
/**
* Skips values.
*
diff --git a/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
index 6346a8de1c..b8cbcf0401 100644
--- a/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
+++ b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
@@ -285,7 +285,7 @@ public class ClientMessagePackerUnpackerTest {
@Test
public void testObjectArray() {
try (var packer = new ClientMessagePacker(PooledByteBufAllocator.DEFAULT.directBuffer())) {
- Object[] args = new Object[]{(byte) 4, (short) 8, 15, 16L, 23.0f, 42.0d, "TEST_STRING", null, UUID.randomUUID(), false};
+ Object[] args = new Object[]{(byte) 4, (short) 8, 15, 16L, 23.0f, 42.0d, "TEST_STRING", null, UUID.randomUUID()};
packer.packObjectArray(args);
var buf = packer.getBuffer();
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 59f990f865..1fa1a556ad 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
@@ -17,12 +17,12 @@
package org.apache.ignite.client.handler.requests.table;
-import static org.apache.ignite.internal.client.proto.ClientMessageCommon.NO_VALUE;
import static org.apache.ignite.lang.ErrorGroups.Client.PROTOCOL_ERR;
import static org.apache.ignite.lang.ErrorGroups.Client.TABLE_ID_NOT_FOUND_ERR;
import java.math.BigDecimal;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -32,6 +32,9 @@ import java.util.BitSet;
import java.util.Collection;
import java.util.UUID;
import org.apache.ignite.client.handler.ClientResourceRegistry;
+import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
+import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
+import org.apache.ignite.internal.client.proto.ClientBinaryTupleUtils;
import org.apache.ignite.internal.client.proto.ClientDataType;
import org.apache.ignite.internal.client.proto.ClientMessagePacker;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
@@ -51,7 +54,6 @@ import org.apache.ignite.table.manager.IgniteTables;
import org.apache.ignite.tx.Transaction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.msgpack.core.MessageTypeException;
/**
* Common table functionality.
@@ -87,24 +89,6 @@ public class ClientTableCommon {
}
}
- /**
- * Writes a tuple.
- *
- * @param packer Packer.
- * @param tuple Tuple.
- */
- public static void writeTupleOrNil(ClientMessagePacker packer, Tuple tuple) {
- if (tuple == null) {
- packer.packNil();
-
- return;
- }
-
- var schema = ((SchemaAware) tuple).schema();
-
- writeTuple(packer, tuple, schema);
- }
-
/**
* Writes a tuple.
*
@@ -123,40 +107,6 @@ public class ClientTableCommon {
writeTuple(packer, tuple, schema, false, part);
}
- /**
- * Writes a tuple.
- *
- * @param packer Packer.
- * @param tuple Tuple.
- * @param schema Tuple schema.
- * @throws IgniteException on failed serialization.
- */
- public static void writeTuple(
- ClientMessagePacker packer,
- Tuple tuple,
- SchemaDescriptor schema
- ) {
- writeTuple(packer, tuple, schema, false, TuplePart.KEY_AND_VAL);
- }
-
- /**
- * Writes a tuple.
- *
- * @param packer Packer.
- * @param tuple Tuple.
- * @param schema Tuple schema.
- * @param skipHeader Whether to skip the tuple header.
- * @throws IgniteException on failed serialization.
- */
- public static void writeTuple(
- ClientMessagePacker packer,
- Tuple tuple,
- SchemaDescriptor schema,
- boolean skipHeader
- ) {
- writeTuple(packer, tuple, schema, skipHeader, TuplePart.KEY_AND_VAL);
- }
-
/**
* Writes a tuple.
*
@@ -180,17 +130,21 @@ public class ClientTableCommon {
packer.packInt(schema.version());
}
+ var builder = BinaryTupleBuilder.create(columnCount(schema, part), true);
+
if (part != TuplePart.VAL) {
for (var col : schema.keyColumns().columns()) {
- writeColumnValue(packer, tuple, col);
+ writeColumnValue(builder, tuple, col);
}
}
if (part != TuplePart.KEY) {
for (var col : schema.valueColumns().columns()) {
- writeColumnValue(packer, tuple, col);
+ writeColumnValue(builder, tuple, col);
}
}
+
+ packBinary(packer, builder.build());
}
/**
@@ -316,14 +270,22 @@ public class ClientTableCommon {
) {
var cnt = keyOnly ? schema.keyColumns().length() : schema.length();
+ // NOTE: noValueSet is only present for client -> server communication.
+ // It helps disambiguate two cases: 1 - column value is not set, 2 - column value is set to null explicitly.
+ // If the column has a default value, it should be applied only in case 1.
+ // https://cwiki.apache.org/confluence/display/IGNITE/IEP-76+Thin+Client+Protocol+for+Ignite+3.0#IEP76ThinClientProtocolforIgnite3.0-NullvsNoValue
+ var noValueSet = unpacker.unpackBitSet();
+ var binaryTupleReader = new BinaryTupleReader(cnt, unpacker.readBinaryUnsafe());
var tuple = Tuple.create(cnt);
for (int i = 0; i < cnt; i++) {
- if (unpacker.tryUnpackNoValue()) {
+ if (noValueSet.get(i)) {
continue;
}
- readAndSetColumnValue(unpacker, tuple, schema.column(i));
+ Column column = schema.column(i);
+ ClientBinaryTupleUtils.readAndSetColumnValue(
+ binaryTupleReader, i, tuple, column.name(), getClientDataType(column.type().spec()));
}
return tuple;
@@ -410,16 +372,6 @@ public class ClientTableCommon {
}
}
- private static void readAndSetColumnValue(ClientMessageUnpacker unpacker, Tuple tuple, Column col) {
- try {
- int type = getClientDataType(col.type().spec());
- Object val = unpacker.unpackObject(type);
- tuple.set(col.name(), val);
- } catch (MessageTypeException e) {
- throw new IgniteException(PROTOCOL_ERR, "Incorrect value type for column '" + col.name() + "': " + e.getMessage(), e);
- }
- }
-
private static int getClientDataType(NativeTypeSpec spec) {
switch (spec) {
case INT8:
@@ -475,88 +427,94 @@ public class ClientTableCommon {
}
}
- private static void writeColumnValue(ClientMessagePacker packer, Tuple tuple, Column col) {
- var val = tuple.valueOrDefault(col.name(), NO_VALUE);
+ private static void writeColumnValue(BinaryTupleBuilder builder, Tuple tuple, Column col) {
+ var val = tuple.valueOrDefault(col.name(), null);
if (val == null) {
- packer.packNil();
- return;
- }
-
- if (val == NO_VALUE) {
- packer.packNoValue();
+ builder.appendNull();
return;
}
switch (col.type().spec()) {
case INT8:
- packer.packByte((byte) val);
+ builder.appendByte((byte) val);
break;
case INT16:
- packer.packShort((short) val);
+ builder.appendShort((short) val);
break;
case INT32:
- packer.packInt((int) val);
+ builder.appendInt((int) val);
break;
case INT64:
- packer.packLong((long) val);
+ builder.appendLong((long) val);
break;
case FLOAT:
- packer.packFloat((float) val);
+ builder.appendFloat((float) val);
break;
case DOUBLE:
- packer.packDouble((double) val);
+ builder.appendDouble((double) val);
break;
case DECIMAL:
- packer.packDecimal((BigDecimal) val);
+ builder.appendDecimalNotNull((BigDecimal) val);
break;
case NUMBER:
- packer.packNumber((BigInteger) val);
+ builder.appendNumberNotNull((BigInteger) val);
break;
case UUID:
- packer.packUuid((UUID) val);
+ builder.appendUuidNotNull((UUID) val);
break;
case STRING:
- packer.packString((String) val);
+ builder.appendStringNotNull((String) val);
break;
case BYTES:
- byte[] bytes = (byte[]) val;
- packer.packBinaryHeader(bytes.length);
- packer.writePayload(bytes);
+ builder.appendBytesNotNull((byte[]) val);
break;
case BITMASK:
- packer.packBitSet((BitSet) val);
+ builder.appendBitmaskNotNull((BitSet) val);
break;
case DATE:
- packer.packDate((LocalDate) val);
+ builder.appendDateNotNull((LocalDate) val);
break;
case TIME:
- packer.packTime((LocalTime) val);
+ builder.appendTimeNotNull((LocalTime) val);
break;
case DATETIME:
- packer.packDateTime((LocalDateTime) val);
+ builder.appendDateTimeNotNull((LocalDateTime) val);
break;
case TIMESTAMP:
- packer.packTimestamp((Instant) val);
+ builder.appendTimestampNotNull((Instant) val);
break;
default:
throw new IgniteException(PROTOCOL_ERR, "Data type not supported: " + col.type());
}
}
+
+ private static int columnCount(SchemaDescriptor schema, TuplePart part) {
+ switch (part) {
+ case KEY: return schema.keyColumns().length();
+ case VAL: return schema.valueColumns().length();
+ default: return schema.length();
+ }
+ }
+
+ private static void packBinary(ClientMessagePacker packer, ByteBuffer buf) {
+ packer.packBinaryHeader(buf.limit() - buf.position());
+ packer.writePayload(buf);
+ }
}
diff --git a/modules/client/pom.xml b/modules/client/pom.xml
index 3a90c19538..2208d93d40 100644
--- a/modules/client/pom.xml
+++ b/modules/client/pom.xml
@@ -79,6 +79,11 @@
<artifactId>netty-codec</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-binary-tuple</artifactId>
+ </dependency>
+
<!-- Test dependencies -->
<dependency>
<groupId>org.hamcrest</groupId>
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientKeyValueView.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientKeyValueView.java
index bad6df3be8..30014e3a2c 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientKeyValueView.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientKeyValueView.java
@@ -22,6 +22,7 @@ import static org.apache.ignite.internal.client.table.ClientTable.writeTx;
import static org.apache.ignite.lang.ErrorGroups.Common.UNKNOWN_ERR;
import java.io.Serializable;
+import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -30,11 +31,14 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
+import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
import org.apache.ignite.internal.client.PayloadOutputChannel;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
import org.apache.ignite.internal.client.proto.ClientOp;
import org.apache.ignite.internal.client.proto.TuplePart;
import org.apache.ignite.internal.marshaller.ClientMarshallerReader;
+import org.apache.ignite.internal.marshaller.ClientMarshallerWriter;
import org.apache.ignite.internal.marshaller.Marshaller;
import org.apache.ignite.internal.marshaller.MarshallerException;
import org.apache.ignite.lang.IgniteException;
@@ -192,14 +196,11 @@ public class ClientKeyValueView<K, V> implements KeyValueView<K, V> {
return tbl.doSchemaOutOpAsync(
ClientOp.TUPLE_UPSERT_ALL,
(s, w) -> {
- w.out().packUuid(tbl.tableId());
- writeTx(tx, w);
- w.out().packInt(s.version());
+ writeSchemaAndTx(s, w, tx);
w.out().packInt(pairs.size());
for (Entry<K, V> e : pairs.entrySet()) {
- keySer.writeRecRaw(e.getKey(), s, w.out(), TuplePart.KEY);
- valSer.writeRecRaw(e.getValue(), s, w.out(), TuplePart.VAL);
+ writeKeyValueRaw(s, w, e.getKey(), e.getValue());
}
},
r -> null);
@@ -370,11 +371,9 @@ public class ClientKeyValueView<K, V> implements KeyValueView<K, V> {
return tbl.doSchemaOutOpAsync(
ClientOp.TUPLE_REPLACE_EXACT,
(s, w) -> {
- keySer.writeRec(tx, key, s, w, TuplePart.KEY);
- valSer.writeRecRaw(oldVal, s, w.out(), TuplePart.VAL);
-
- keySer.writeRecRaw(key, s, w.out(), TuplePart.KEY);
- valSer.writeRecRaw(newVal, s, w.out(), TuplePart.VAL);
+ writeSchemaAndTx(s, w, tx);
+ writeKeyValueRaw(s, w, key, oldVal);
+ writeKeyValueRaw(s, w, key, newVal);
},
ClientMessageUnpacker::unpackBoolean);
}
@@ -454,8 +453,29 @@ public class ClientKeyValueView<K, V> implements KeyValueView<K, V> {
}
private void writeKeyValue(ClientSchema s, PayloadOutputChannel w, @Nullable Transaction tx, @NotNull K key, V val) {
- keySer.writeRec(tx, key, s, w, TuplePart.KEY);
- valSer.writeRecRaw(val, s, w.out(), TuplePart.VAL);
+ writeSchemaAndTx(s, w, tx);
+ writeKeyValueRaw(s, w, key, val);
+ }
+
+ private void writeKeyValueRaw(ClientSchema s, PayloadOutputChannel w, @NotNull K key, V val) {
+ var builder = BinaryTupleBuilder.create(s.columns().length, true);
+ var noValueSet = new BitSet();
+ ClientMarshallerWriter writer = new ClientMarshallerWriter(builder, noValueSet);
+
+ try {
+ s.getMarshaller(keySer.mapper(), TuplePart.KEY).writeObject(key, writer);
+ s.getMarshaller(valSer.mapper(), TuplePart.VAL).writeObject(val, writer);
+ } catch (MarshallerException e) {
+ throw new IgniteException(UNKNOWN_ERR, e.getMessage(), e);
+ }
+
+ w.out().packBinaryTuple(builder, noValueSet);
+ }
+
+ private void writeSchemaAndTx(ClientSchema s, PayloadOutputChannel w, @Nullable Transaction tx) {
+ w.out().packUuid(tbl.tableId());
+ writeTx(tx, w);
+ w.out().packInt(s.version());
}
private HashMap<K, V> readGetAllResponse(ClientSchema schema, ClientMessageUnpacker in) {
@@ -466,11 +486,12 @@ public class ClientKeyValueView<K, V> implements KeyValueView<K, V> {
Marshaller keyMarsh = schema.getMarshaller(keySer.mapper(), TuplePart.KEY);
Marshaller valMarsh = schema.getMarshaller(valSer.mapper(), TuplePart.VAL);
- var reader = new ClientMarshallerReader(in);
-
try {
for (int i = 0; i < cnt; i++) {
in.unpackBoolean(); // TODO: Optimize (IGNITE-16022).
+
+ var tupleReader = new BinaryTupleReader(schema.columns().length, in.readBinaryUnsafe());
+ var reader = new ClientMarshallerReader(tupleReader);
res.put((K) keyMarsh.readObject(reader, null), (V) valMarsh.readObject(reader, null));
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientRecordSerializer.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientRecordSerializer.java
index 1395fde606..cf772175b2 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientRecordSerializer.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientRecordSerializer.java
@@ -21,9 +21,12 @@ import static org.apache.ignite.internal.client.table.ClientTable.writeTx;
import static org.apache.ignite.lang.ErrorGroups.Common.UNKNOWN_ERR;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
+import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
+import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
import org.apache.ignite.internal.client.PayloadOutputChannel;
import org.apache.ignite.internal.client.proto.ClientMessagePacker;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
@@ -88,11 +91,27 @@ public class ClientRecordSerializer<R> {
* @param <R> Record type.
*/
public static <R> void writeRecRaw(@Nullable R rec, Mapper<R> mapper, ClientSchema schema, ClientMessagePacker out, TuplePart part) {
- Marshaller marshaller = schema.getMarshaller(mapper, part);
- ClientMarshallerWriter writer = new ClientMarshallerWriter(out);
+ writeRecRaw(rec, out, schema.getMarshaller(mapper, part), columnCount(schema, part));
+ }
+ /**
+ * Writes a record without header.
+ *
+ * @param rec Record.
+ * @param out Writer.
+ * @param marshaller Marshaller.
+ * @param columnCount Column count.
+ * @param <R> Record type.
+ */
+ static <R> void writeRecRaw(@Nullable R rec, ClientMessagePacker out, Marshaller marshaller, int columnCount) {
try {
+ var builder = BinaryTupleBuilder.create(columnCount, true);
+ var noValueSet = new BitSet();
+
+ var writer = new ClientMarshallerWriter(builder, noValueSet);
marshaller.writeObject(rec, writer);
+
+ out.packBinaryTuple(builder, noValueSet);
} catch (MarshallerException e) {
throw new IgniteException(UNKNOWN_ERR, e.getMessage(), e);
}
@@ -123,14 +142,10 @@ public class ClientRecordSerializer<R> {
out.out().packInt(schema.version());
Marshaller marshaller = schema.getMarshaller(mapper, part);
- ClientMarshallerWriter writer = new ClientMarshallerWriter(out.out());
+ int columnCount = columnCount(schema, part);
- try {
- marshaller.writeObject(rec, writer);
- marshaller.writeObject(rec2, writer);
- } catch (MarshallerException e) {
- throw new IgniteException(UNKNOWN_ERR, e.getMessage(), e);
- }
+ writeRecRaw(rec, out.out(), marshaller, columnCount);
+ writeRecRaw(rec2, out.out(), marshaller, columnCount);
}
void writeRecs(
@@ -146,14 +161,10 @@ public class ClientRecordSerializer<R> {
out.out().packInt(recs.size());
Marshaller marshaller = schema.getMarshaller(mapper, part);
- ClientMarshallerWriter writer = new ClientMarshallerWriter(out.out());
+ int columnCount = columnCount(schema, part);
- try {
- for (R rec : recs) {
- marshaller.writeObject(rec, writer);
- }
- } catch (MarshallerException e) {
- throw new IgniteException(UNKNOWN_ERR, e.getMessage(), e);
+ for (R rec : recs) {
+ writeRecRaw(rec, out.out(), marshaller, columnCount);
}
}
@@ -167,13 +178,14 @@ public class ClientRecordSerializer<R> {
var res = new ArrayList<R>(cnt);
Marshaller marshaller = schema.getMarshaller(mapper, part);
- var reader = new ClientMarshallerReader(in);
try {
for (int i = 0; i < cnt; i++) {
if (nullable && !in.unpackBoolean()) {
res.add(null);
} else {
+ var tupleReader = new BinaryTupleReader(columnCount(schema, part), in.readBinaryUnsafe());
+ var reader = new ClientMarshallerReader(tupleReader);
res.add((R) marshaller.readObject(reader, null));
}
}
@@ -186,7 +198,9 @@ public class ClientRecordSerializer<R> {
R readRec(ClientSchema schema, ClientMessageUnpacker in, TuplePart part) {
Marshaller marshaller = schema.getMarshaller(mapper, part);
- ClientMarshallerReader reader = new ClientMarshallerReader(in);
+
+ var tupleReader = new BinaryTupleReader(columnCount(schema, part), in.readBinaryUnsafe());
+ ClientMarshallerReader reader = new ClientMarshallerReader(tupleReader);
try {
return (R) marshaller.readObject(reader, null);
@@ -203,7 +217,8 @@ public class ClientRecordSerializer<R> {
Marshaller keyMarshaller = schema.getMarshaller(mapper, TuplePart.KEY);
Marshaller valMarshaller = schema.getMarshaller(mapper, TuplePart.VAL);
- ClientMarshallerReader reader = new ClientMarshallerReader(in);
+ var tupleReader = new BinaryTupleReader(schema.columns().length - schema.keyColumnCount(), in.readBinaryUnsafe());
+ ClientMarshallerReader reader = new ClientMarshallerReader(tupleReader);
try {
var res = (R) valMarshaller.readObject(reader, null);
@@ -215,4 +230,20 @@ public class ClientRecordSerializer<R> {
throw new IgniteException(UNKNOWN_ERR, e.getMessage(), e);
}
}
+
+ private static int columnCount(ClientSchema schema, TuplePart part) {
+ switch (part) {
+ case KEY:
+ return schema.keyColumnCount();
+
+ case VAL:
+ return schema.columns().length - schema.keyColumnCount();
+
+ case KEY_AND_VAL:
+ return schema.columns().length;
+
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
}
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 88fbbc6989..25614b0bff 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 static org.apache.ignite.internal.client.proto.ClientDataType.BIGINTEGER;
import static org.apache.ignite.internal.client.proto.ClientDataType.BITMASK;
-import static org.apache.ignite.internal.client.proto.ClientDataType.BOOLEAN;
import static org.apache.ignite.internal.client.proto.ClientDataType.BYTES;
import static org.apache.ignite.internal.client.proto.ClientDataType.DATE;
import static org.apache.ignite.internal.client.proto.ClientDataType.DATETIME;
@@ -174,9 +172,6 @@ public class ClientSchema {
private static BinaryMode mode(int dataType) {
switch (dataType) {
- case BOOLEAN:
- throw new IgniteException("TODO: " + dataType);
-
case INT8:
return BinaryMode.BYTE;
@@ -207,8 +202,6 @@ public class ClientSchema {
case DECIMAL:
return BinaryMode.DECIMAL;
- // Falls through.
- case BIGINTEGER:
case NUMBER:
return BinaryMode.NUMBER;
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleSerializer.java b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleSerializer.java
index 97d65ddec8..c358ea5e90 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleSerializer.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientTupleSerializer.java
@@ -19,15 +19,28 @@ package org.apache.ignite.internal.client.table;
import static org.apache.ignite.internal.client.proto.ClientMessageCommon.NO_VALUE;
import static org.apache.ignite.internal.client.table.ClientTable.writeTx;
-
+import static org.apache.ignite.lang.ErrorGroups.Client.PROTOCOL_ERR;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
+import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
import org.apache.ignite.internal.client.PayloadOutputChannel;
+import org.apache.ignite.internal.client.proto.ClientBinaryTupleUtils;
+import org.apache.ignite.internal.client.proto.ClientDataType;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
import org.apache.ignite.lang.IgniteBiTuple;
+import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.table.Tuple;
import org.apache.ignite.tx.Transaction;
import org.jetbrains.annotations.NotNull;
@@ -121,13 +134,17 @@ public class ClientTupleSerializer {
var columns = schema.columns();
var count = keyOnly ? schema.keyColumnCount() : columns.length;
+ var builder = BinaryTupleBuilder.create(count, true);
+ var noValueSet = new BitSet(count);
+
for (var i = 0; i < count; i++) {
var col = columns[i];
-
Object v = tuple.valueOrDefault(col.name(), NO_VALUE);
- out.out().packObject(v);
+ appendValue(builder, noValueSet, col, v);
}
+
+ out.out().packBinaryTuple(builder, noValueSet);
}
/**
@@ -154,6 +171,8 @@ public class ClientTupleSerializer {
}
var columns = schema.columns();
+ var noValueSet = new BitSet(columns.length);
+ var builder = BinaryTupleBuilder.create(columns.length, true);
for (var i = 0; i < columns.length; i++) {
var col = columns[i];
@@ -164,8 +183,10 @@ public class ClientTupleSerializer {
? val.valueOrDefault(col.name(), NO_VALUE)
: NO_VALUE;
- out.out().packObject(v);
+ appendValue(builder, noValueSet, col, v);
}
+
+ out.out().packBinaryTuple(builder, noValueSet);
}
/**
@@ -216,8 +237,11 @@ public class ClientTupleSerializer {
var colCnt = keyOnly ? schema.keyColumnCount() : schema.columns().length;
+ var binTuple = new BinaryTupleReader(colCnt, in.readBinaryUnsafe());
+
for (var i = 0; i < colCnt; i++) {
- tuple.setInternal(i, in.unpackObject(schema.columns()[i].type()));
+ ClientColumn column = schema.columns()[i];
+ ClientBinaryTupleUtils.readAndSetColumnValue(binTuple, i, tuple, column.name(), column.type());
}
return tuple;
@@ -225,15 +249,16 @@ public class ClientTupleSerializer {
static Tuple readValueTuple(ClientSchema schema, ClientMessageUnpacker in, Tuple keyTuple) {
var tuple = new ClientTuple(schema);
+ var binTuple = new BinaryTupleReader(schema.columns().length - schema.keyColumnCount(), in.readBinaryUnsafe());
for (var i = 0; i < schema.columns().length; i++) {
ClientColumn col = schema.columns()[i];
- Object value = i < schema.keyColumnCount()
- ? keyTuple.value(col.name())
- : in.unpackObject(schema.columns()[i].type());
-
- tuple.setInternal(i, value);
+ if (i < schema.keyColumnCount()) {
+ tuple.setInternal(i, keyTuple.value(col.name()));
+ } else {
+ ClientBinaryTupleUtils.readAndSetColumnValue(binTuple, i - schema.keyColumnCount(), tuple, col.name(), col.type());
+ }
}
return tuple;
@@ -244,12 +269,12 @@ public class ClientTupleSerializer {
var colCnt = schema.columns().length;
var valTuple = new ClientTuple(schema, keyColCnt, schema.columns().length - 1);
+ var binTupleReader = new BinaryTupleReader(colCnt - keyColCnt, in.readBinaryUnsafe());
for (var i = keyColCnt; i < colCnt; i++) {
ClientColumn col = schema.columns()[i];
- Object val = in.unpackObject(col.type());
-
- valTuple.setInternal(i - keyColCnt, val);
+ ClientBinaryTupleUtils.readAndSetColumnValue(
+ binTupleReader, i - keyColCnt, valTuple, col.name(), col.type());
}
return valTuple;
@@ -262,14 +287,15 @@ public class ClientTupleSerializer {
var keyTuple = new ClientTuple(schema, 0, keyColCnt - 1);
var valTuple = new ClientTuple(schema, keyColCnt, schema.columns().length - 1);
+ var binTuple = new BinaryTupleReader(colCnt, in.readBinaryUnsafe());
+
for (var i = 0; i < colCnt; i++) {
ClientColumn col = schema.columns()[i];
- Object val = in.unpackObject(col.type());
if (i < keyColCnt) {
- keyTuple.setInternal(i, val);
+ ClientBinaryTupleUtils.readAndSetColumnValue(binTuple, i, keyTuple, col.name(), col.type());
} else {
- valTuple.setInternal(i - keyColCnt, val);
+ ClientBinaryTupleUtils.readAndSetColumnValue(binTuple, i, valTuple, col.name(), col.type());
}
}
@@ -329,4 +355,90 @@ public class ClientTupleSerializer {
return res;
}
+
+ private static void appendValue(BinaryTupleBuilder builder, BitSet noValueSet, ClientColumn col, Object v) {
+ if (v == null) {
+ builder.appendNull();
+ return;
+ }
+
+ if (v == NO_VALUE) {
+ noValueSet.set(col.schemaIndex());
+ builder.appendDefault();
+ return;
+ }
+
+ try {
+ switch (col.type()) {
+ case ClientDataType.INT8:
+ builder.appendByte((byte) v);
+ return;
+
+ case ClientDataType.INT16:
+ builder.appendShort((short) v);
+ return;
+
+ case ClientDataType.INT32:
+ builder.appendInt((int) v);
+ return;
+
+ case ClientDataType.INT64:
+ builder.appendLong((long) v);
+ return;
+
+ case ClientDataType.FLOAT:
+ builder.appendFloat((float) v);
+ return;
+
+ case ClientDataType.DOUBLE:
+ builder.appendDouble((double) v);
+ return;
+
+ case ClientDataType.DECIMAL:
+ builder.appendDecimalNotNull((BigDecimal) v);
+ return;
+
+ case ClientDataType.UUID:
+ builder.appendUuidNotNull((UUID) v);
+ return;
+
+ case ClientDataType.STRING:
+ builder.appendStringNotNull((String) v);
+ return;
+
+ case ClientDataType.BYTES:
+ builder.appendBytesNotNull((byte[]) v);
+ return;
+
+ case ClientDataType.BITMASK:
+ builder.appendBitmaskNotNull((BitSet) v);
+ return;
+
+ case ClientDataType.DATE:
+ builder.appendDateNotNull((LocalDate) v);
+ return;
+
+ case ClientDataType.TIME:
+ builder.appendTimeNotNull((LocalTime) v);
+ return;
+
+ case ClientDataType.DATETIME:
+ builder.appendDateTimeNotNull((LocalDateTime) v);
+ return;
+
+ case ClientDataType.TIMESTAMP:
+ builder.appendTimestampNotNull((Instant) v);
+ return;
+
+ case ClientDataType.NUMBER:
+ builder.appendNumberNotNull((BigInteger) v);
+ return;
+
+ default:
+ throw new IllegalArgumentException("Unsupported type: " + col.type());
+ }
+ } catch (ClassCastException e) {
+ throw new IgniteException(PROTOCOL_ERR, "Incorrect value type for column '" + col.name() + "': " + e.getMessage(), e);
+ }
+ }
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerReader.java b/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerReader.java
index 1e0e7569b2..52af968a47 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerReader.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerReader.java
@@ -25,6 +25,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.BitSet;
import java.util.UUID;
+import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
/**
@@ -32,152 +33,172 @@ import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
*/
public class ClientMarshallerReader implements MarshallerReader {
/** Unpacker. */
- private final ClientMessageUnpacker unpacker;
+ private final BinaryTupleReader unpacker;
+
+ /** Index. */
+ private int index;
/**
* Constructor.
*
* @param unpacker Unpacker.
*/
- public ClientMarshallerReader(ClientMessageUnpacker unpacker) {
+ public ClientMarshallerReader(BinaryTupleReader unpacker) {
this.unpacker = unpacker;
}
/** {@inheritDoc} */
@Override
public void skipValue() {
- unpacker.skipValues(1);
+ index++;
}
/** {@inheritDoc} */
@Override
public byte readByte() {
- return unpacker.unpackByte();
+ return unpacker.byteValue(index++);
}
/** {@inheritDoc} */
@Override
public Byte readByteBoxed() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackByte();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.byteValue(idx);
}
/** {@inheritDoc} */
@Override
public short readShort() {
- return unpacker.unpackShort();
+ return unpacker.shortValue(index++);
}
/** {@inheritDoc} */
@Override
public Short readShortBoxed() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackShort();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.shortValue(idx);
}
/** {@inheritDoc} */
@Override
public int readInt() {
- return unpacker.unpackInt();
+ return unpacker.intValue(index++);
}
/** {@inheritDoc} */
@Override
public Integer readIntBoxed() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackInt();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.intValue(idx);
}
/** {@inheritDoc} */
@Override
public long readLong() {
- return unpacker.unpackLong();
+ return unpacker.longValue(index++);
}
/** {@inheritDoc} */
@Override
public Long readLongBoxed() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackLong();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.longValue(idx);
}
/** {@inheritDoc} */
@Override
public float readFloat() {
- return unpacker.unpackFloat();
+ return unpacker.floatValue(index++);
}
/** {@inheritDoc} */
@Override
public Float readFloatBoxed() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackFloat();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.floatValue(idx);
}
/** {@inheritDoc} */
@Override
public double readDouble() {
- return unpacker.unpackDouble();
+ return unpacker.doubleValue(index++);
}
/** {@inheritDoc} */
@Override
public Double readDoubleBoxed() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackDouble();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.doubleValue(idx);
}
/** {@inheritDoc} */
@Override
public String readString() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackString();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.stringValue(idx);
}
/** {@inheritDoc} */
@Override
public UUID readUuid() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackUuid();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.uuidValue(idx);
}
/** {@inheritDoc} */
@Override
public byte[] readBytes() {
- return unpacker.tryUnpackNil() ? null : unpacker.readPayload(unpacker.unpackBinaryHeader());
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.bytesValue(idx);
}
/** {@inheritDoc} */
@Override
public BitSet readBitSet() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackBitSet();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.bitmaskValue(idx);
}
/** {@inheritDoc} */
@Override
public BigInteger readBigInt() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackNumber();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.numberValue(idx);
}
/** {@inheritDoc} */
@Override
public BigDecimal readBigDecimal() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackDecimal();
+ // TODO IGNITE-17632: Get scale from schema.
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.decimalValue(idx, 100);
}
/** {@inheritDoc} */
@Override
public LocalDate readDate() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackDate();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.dateValue(idx);
}
/** {@inheritDoc} */
@Override
public LocalTime readTime() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackTime();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.timeValue(idx);
}
/** {@inheritDoc} */
@Override
public Instant readTimestamp() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackTimestamp();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.timestampValue(idx);
}
/** {@inheritDoc} */
@Override
public LocalDateTime readDateTime() {
- return unpacker.tryUnpackNil() ? null : unpacker.unpackDateTime();
+ var idx = index++;
+ return unpacker.hasNullValue(idx) ? null : unpacker.dateTimeValue(idx);
}
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerWriter.java b/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerWriter.java
index 1bec432172..bcaa375af6 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerWriter.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/marshaller/ClientMarshallerWriter.java
@@ -25,6 +25,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.BitSet;
import java.util.UUID;
+import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
import org.apache.ignite.internal.client.proto.ClientMessagePacker;
/**
@@ -32,123 +33,127 @@ import org.apache.ignite.internal.client.proto.ClientMessagePacker;
*/
public class ClientMarshallerWriter implements MarshallerWriter {
/** Packer. */
- private final ClientMessagePacker packer;
+ private final BinaryTupleBuilder packer;
+
+ /** No-value bit set. */
+ private final BitSet noValueSet;
/**
* Constructor.
*
* @param packer Packer.
*/
- public ClientMarshallerWriter(ClientMessagePacker packer) {
+ public ClientMarshallerWriter(BinaryTupleBuilder packer, BitSet noValueSet) {
this.packer = packer;
+ this.noValueSet = noValueSet;
}
/** {@inheritDoc} */
@Override
public void writeNull() {
- packer.packNil();
+ packer.appendNull();
}
/** {@inheritDoc} */
@Override
public void writeAbsentValue() {
- packer.packNoValue();
+ noValueSet.set(packer.elementIndex());
+ packer.appendDefault();
}
/** {@inheritDoc} */
@Override
public void writeByte(byte val) {
- packer.packByte(val);
+ packer.appendByte(val);
}
/** {@inheritDoc} */
@Override
public void writeShort(short val) {
- packer.packShort(val);
+ packer.appendShort(val);
}
/** {@inheritDoc} */
@Override
public void writeInt(int val) {
- packer.packInt(val);
+ packer.appendInt(val);
}
/** {@inheritDoc} */
@Override
public void writeLong(long val) {
- packer.packLong(val);
+ packer.appendLong(val);
}
/** {@inheritDoc} */
@Override
public void writeFloat(float val) {
- packer.packFloat(val);
+ packer.appendFloat(val);
}
/** {@inheritDoc} */
@Override
public void writeDouble(double val) {
- packer.packDouble(val);
+ packer.appendDouble(val);
}
/** {@inheritDoc} */
@Override
public void writeString(String val) {
- packer.packString(val);
+ packer.appendString(val);
}
/** {@inheritDoc} */
@Override
public void writeUuid(UUID val) {
- packer.packUuid(val);
+ packer.appendUuid(val);
}
/** {@inheritDoc} */
@Override
public void writeBytes(byte[] val) {
- packer.packBinaryHeader(val.length);
- packer.writePayload(val);
+ packer.appendBytes(val);
}
/** {@inheritDoc} */
@Override
public void writeBitSet(BitSet val) {
- packer.packBitSet(val);
+ packer.appendBitmask(val);
}
/** {@inheritDoc} */
@Override
public void writeBigInt(BigInteger val) {
- packer.packNumber(val);
+ packer.appendNumber(val);
}
/** {@inheritDoc} */
@Override
public void writeBigDecimal(BigDecimal val) {
- packer.packDecimal(val);
+ packer.appendDecimal(val);
}
/** {@inheritDoc} */
@Override
public void writeDate(LocalDate val) {
- packer.packDate(val);
+ packer.appendDate(val);
}
/** {@inheritDoc} */
@Override
public void writeTime(LocalTime val) {
- packer.packTime(val);
+ packer.appendTime(val);
}
/** {@inheritDoc} */
@Override
public void writeTimestamp(Instant val) {
- packer.packTimestamp(val);
+ packer.appendTimestamp(val);
}
/** {@inheritDoc} */
@Override
public void writeDateTime(LocalDateTime val) {
- packer.packDateTime(val);
+ packer.appendDateTime(val);
}
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java
index 05308556ae..31c7a07015 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java
@@ -102,7 +102,7 @@ public class AbstractClientTableTest extends AbstractClientTest {
return client.tables().table(TABLE_WITH_DEFAULT_VALUES);
}
- protected static Tuple allClumnsTableKey(long id) {
+ protected static Tuple allColumnsTableKey(long id) {
return Tuple.create().set("gid", id).set("id", String.valueOf(id));
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java
index 36e3578484..e3aac3ea21 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java
@@ -94,7 +94,7 @@ public class ClientKeyValueViewTest extends AbstractClientTableTest {
KeyValueView<Tuple, Tuple> kvView = table.keyValueView();
KeyValueView<IncompletePojo, IncompletePojo> pojoView = table.keyValueView(IncompletePojo.class, IncompletePojo.class);
- kvView.put(null, allClumnsTableKey(1), allColumnsTableVal("x"));
+ kvView.put(null, allColumnsTableKey(1), allColumnsTableVal("x"));
var key = new IncompletePojo();
key.id = "1";
@@ -136,7 +136,7 @@ public class ClientKeyValueViewTest extends AbstractClientTableTest {
assertEquals("foo", res.zstring);
assertArrayEquals(new byte[]{1, 2}, res.zbytes);
assertEquals(BitSet.valueOf(new byte[]{32}), res.zbitmask);
- assertEquals(21, res.zdecimal.longValue());
+ assertEquals(21, res.zdecimal.unscaledValue().longValue()); // TODO: IGNITE-17632 check correct round-trip
assertEquals(22, res.znumber.longValue());
assertEquals(uuid, res.zuuid);
}
@@ -174,10 +174,10 @@ public class ClientKeyValueViewTest extends AbstractClientTableTest {
pojoView.put(null, val, val);
- Tuple res = table.recordView().get(null, Tuple.create().set("id", "112").set("gid", 111));
+ Tuple res = table.recordView().get(null, Tuple.create().set("id", "112").set("gid", 111L));
assertNotNull(res);
- assertEquals(111, res.intValue("gid"));
+ assertEquals(111, res.longValue("gid"));
assertEquals("112", res.stringValue("id"));
assertEquals(113, res.byteValue("zbyte"));
assertEquals(114, res.shortValue("zshort"));
@@ -191,7 +191,7 @@ public class ClientKeyValueViewTest extends AbstractClientTableTest {
assertEquals("119", res.stringValue("zstring"));
assertEquals(120, ((byte[]) res.value("zbytes"))[0]);
assertEquals(BitSet.valueOf(new byte[]{121}), res.bitmaskValue("zbitmask"));
- assertEquals(122, ((Number) res.value("zdecimal")).longValue());
+ assertEquals(122, ((BigDecimal) res.value("zdecimal")).unscaledValue().longValue()); // TODO: IGNITE-17632 check correct round-trip
assertEquals(BigInteger.valueOf(123), res.value("znumber"));
assertEquals(uuid, res.uuidValue("zuuid"));
}
@@ -467,7 +467,7 @@ public class ClientKeyValueViewTest extends AbstractClientTableTest {
pojoView.put(null, 1, new NamePojo());
- var res = recordView.get(null, tupleKey(1));
+ var res = recordView.get(null, Tuple.create().set("id", 1));
assertEquals("def_str", res.stringValue("str"));
assertEquals("def_str2", res.stringValue("strNonNull"));
@@ -485,7 +485,7 @@ public class ClientKeyValueViewTest extends AbstractClientTableTest {
pojoView.put(null, 1, pojo);
- var res = recordView.get(null, tupleKey(1));
+ var res = recordView.get(null, Tuple.create().set("id", 1));
assertNull(res.stringValue("str"));
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java
index 8db9e210da..ff2fffa711 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java
@@ -96,7 +96,7 @@ public class ClientRecordViewTest extends AbstractClientTableTest {
KeyValueView<Tuple, Tuple> kvView = table.keyValueView();
RecordView<IncompletePojo> pojoView = table.recordView(IncompletePojo.class);
- kvView.put(null, allClumnsTableKey(1), allColumnsTableVal("x"));
+ kvView.put(null, allColumnsTableKey(1), allColumnsTableVal("x"));
var key = new IncompletePojo();
key.id = "1";
@@ -136,7 +136,7 @@ public class ClientRecordViewTest extends AbstractClientTableTest {
assertEquals("foo", res.zstring);
assertArrayEquals(new byte[]{1, 2}, res.zbytes);
assertEquals(BitSet.valueOf(new byte[]{32}), res.zbitmask);
- assertEquals(21, res.zdecimal.longValue());
+ assertEquals(21, res.zdecimal.unscaledValue().longValue()); // TODO: IGNITE-17632 check correct round-trip
assertEquals(22, res.znumber.longValue());
assertEquals(uuid, res.zuuid);
}
@@ -172,10 +172,10 @@ public class ClientRecordViewTest extends AbstractClientTableTest {
pojoView.upsert(null, val);
- Tuple res = table.recordView().get(null, Tuple.create().set("id", "112").set("gid", 111));
+ Tuple res = table.recordView().get(null, Tuple.create().set("id", "112").set("gid", 111L));
assertNotNull(res);
- assertEquals(111, res.intValue("gid"));
+ assertEquals(111, res.longValue("gid"));
assertEquals("112", res.stringValue("id"));
assertEquals(113, res.byteValue("zbyte"));
assertEquals(114, res.shortValue("zshort"));
@@ -189,7 +189,7 @@ public class ClientRecordViewTest extends AbstractClientTableTest {
assertEquals("119", res.stringValue("zstring"));
assertEquals(120, ((byte[]) res.value("zbytes"))[0]);
assertEquals(BitSet.valueOf(new byte[]{121}), res.bitmaskValue("zbitmask"));
- assertEquals(122, ((Number) res.value("zdecimal")).longValue());
+ assertEquals(122, ((BigDecimal) res.value("zdecimal")).unscaledValue().longValue());
assertEquals(BigInteger.valueOf(123), res.value("znumber"));
assertEquals(uuid, res.uuidValue("zuuid"));
}
@@ -492,7 +492,7 @@ public class ClientRecordViewTest extends AbstractClientTableTest {
pojoView.upsert(null, new PersonPojo());
- var res = recordView.get(null, tupleKey(0));
+ var res = recordView.get(null, Tuple.create().set("id", 0));
assertEquals("def_str", res.stringValue("str"));
assertEquals("def_str2", res.stringValue("strNonNull"));
@@ -511,7 +511,7 @@ public class ClientRecordViewTest extends AbstractClientTableTest {
pojoView.upsert(null, pojo);
- var res = recordView.get(null, tupleKey(1));
+ var res = recordView.get(null, Tuple.create().set("id", 1));
assertNull(res.stringValue("str"));
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
index 2b2904e4ba..e79a264157 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
@@ -383,7 +383,8 @@ public class ClientTableTest extends AbstractClientTableTest {
var ex = assertThrows(IgniteException.class, () -> defaultTable().recordView().upsert(null, tuple));
- assertTrue(ex.getMessage().contains("Incorrect value type for column 'ID': Expected Integer, but got String"), ex.getMessage());
+ String expectedErr = "Incorrect value type for column 'ID': class java.lang.String cannot be cast to class java.lang.Long";
+ assertThat(ex.getMessage(), containsString(expectedErr));
}
@Test
diff --git a/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java b/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
index a37c1bb7fc..9776aa26d5 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/RetryPolicyTest.java
@@ -143,8 +143,8 @@ public class RetryPolicyTest {
try (var client = getClient(plc)) {
RecordView<Tuple> recView = client.tables().table("t").recordView();
- recView.get(null, Tuple.create().set("id", 1));
- recView.get(null, Tuple.create().set("id", 1));
+ recView.get(null, Tuple.create().set("id", 1L));
+ recView.get(null, Tuple.create().set("id", 1L));
assertEquals(1, plc.invocations.size());
}
@@ -172,8 +172,8 @@ public class RetryPolicyTest {
try (var client = getClient(new RetryReadPolicy())) {
RecordView<Tuple> recView = client.tables().table("t").recordView();
- recView.get(null, Tuple.create().set("id", 1));
- recView.get(null, Tuple.create().set("id", 1));
+ recView.get(null, Tuple.create().set("id", 1L));
+ recView.get(null, Tuple.create().set("id", 1L));
}
}
@@ -183,8 +183,8 @@ public class RetryPolicyTest {
try (var client = getClient(new RetryReadPolicy())) {
RecordView<Tuple> recView = client.tables().table("t").recordView();
- recView.upsert(null, Tuple.create().set("id", 1));
- assertThrows(IgniteClientConnectionException.class, () -> recView.upsert(null, Tuple.create().set("id", 1)));
+ recView.upsert(null, Tuple.create().set("id", 1L));
+ assertThrows(IgniteClientConnectionException.class, () -> recView.upsert(null, Tuple.create().set("id", 1L)));
}
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java
index eb8532b45a..82787d6dc0 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeIgniteTables.java
@@ -224,7 +224,7 @@ public class FakeIgniteTables implements IgniteTables, IgniteTablesInternal {
return new SchemaDescriptor(
v,
new Column[]{
- new Column("gid".toUpperCase(), NativeTypes.INT32, false),
+ new Column("gid".toUpperCase(), NativeTypes.INT64, false),
new Column("id".toUpperCase(), NativeTypes.STRING, false)
},
new Column[]{
@@ -241,7 +241,7 @@ public class FakeIgniteTables implements IgniteTables, IgniteTablesInternal {
new Column("zbytes".toUpperCase(), NativeTypes.BYTES, true),
new Column("zuuid".toUpperCase(), NativeTypes.UUID, true),
new Column("zbitmask".toUpperCase(), NativeTypes.bitmaskOf(16), true),
- new Column("zdecimal".toUpperCase(), NativeTypes.decimalOf(20, 3), true),
+ new Column("zdecimal".toUpperCase(), NativeTypes.decimalOf(20, 100), true),
new Column("znumber".toUpperCase(), NativeTypes.numberOf(24), true),
});
}
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 13c3a04abe..a675657f61 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerBenchmarksBase.cs
@@ -63,7 +63,7 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
{
var bytesWritten = pooledWriter.GetWrittenMemory().Length;
- if (bytesWritten != 29)
+ if (bytesWritten != 31)
{
throw new Exception("Unexpected number of bytes written: " + bytesWritten);
}
@@ -77,7 +77,7 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
TupleSerializerHandler.Instance.Write(ref writer, Schema, Tuple);
writer.Flush();
- return pooledWriter.GetWrittenMemory().ToArray();
+ return pooledWriter.GetWrittenMemory().Slice(3).ToArray();
}
protected internal class Car
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs
index 9a14a671d3..1e3dddd427 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerReadBenchmarks.cs
@@ -20,11 +20,13 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
using System.Diagnostics.CodeAnalysis;
using BenchmarkDotNet.Attributes;
using Internal.Proto;
+ using Internal.Proto.BinaryTuple;
using Internal.Table.Serialization;
using MessagePack;
/// <summary>
/// Benchmarks for <see cref="IRecordSerializerHandler{T}.Read"/> implementations.
+ ///
/// Results on Intel Core i7-9700K, .NET SDK 3.1.416, Ubuntu 20.04:
/// | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
/// |----------------- |-----------:|--------:|--------:|------:|--------:|-------:|----------:|
@@ -32,6 +34,21 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
/// | ReadObject | 257.5 ns | 1.41 ns | 1.25 ns | 1.22 | 0.01 | 0.0124 | 80 B |
/// | ReadTuple | 561.0 ns | 3.09 ns | 2.89 ns | 2.66 | 0.01 | 0.0849 | 536 B |
/// | ReadObjectOld | 1,020.9 ns | 9.05 ns | 8.47 ns | 4.84 | 0.05 | 0.0744 | 472 B |.
+ ///
+ /// Results on i7-7700HQ, .NET SDK 6.0.400, Ubuntu 20.04:
+ /// MsgPack (old)
+ /// | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
+ /// |----------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|
+ /// | ReadObjectManual | 289.4 ns | 2.92 ns | 2.59 ns | 1.00 | 0.00 | 0.0024 | 80 B |
+ /// | ReadObject | 364.3 ns | 3.28 ns | 3.07 ns | 1.26 | 0.02 | 0.0024 | 80 B |
+ /// | ReadTuple | 755.4 ns | 2.82 ns | 2.35 ns | 2.61 | 0.03 | 0.0181 | 536 B |
+ ///
+ /// BinaryTuple (new)
+ /// | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Allocated |
+ /// |----------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|
+ /// | ReadObjectManual | 299.3 ns | 3.42 ns | 3.20 ns | 1.00 | 0.00 | 0.0024 | 80 B |
+ /// | ReadObject | 382.9 ns | 2.49 ns | 2.21 ns | 1.28 | 0.02 | 0.0024 | 80 B |
+ /// | ReadTuple | 769.0 ns | 6.06 ns | 5.37 ns | 2.57 | 0.04 | 0.0181 | 536 B |.
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Benchmarks.")]
[MemoryDiagnoser]
@@ -41,12 +58,13 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
public void ReadObjectManual()
{
var reader = new MessagePackReader(SerializedData);
+ var tupleReader = new BinaryTupleReader(reader.ReadBytesAsMemory(), 3);
var res = new Car
{
- Id = reader.TryReadNoValue() ? default : reader.ReadGuid(),
- BodyType = reader.TryReadNoValue() ? default! : reader.ReadString(),
- Seats = reader.TryReadNoValue() ? default : reader.ReadInt32()
+ Id = tupleReader.GetGuid(0),
+ BodyType = tupleReader.GetString(1),
+ Seats = tupleReader.GetInt(2)
};
Consumer.Consume(res);
@@ -70,13 +88,13 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
Consumer.Consume(res);
}
- [Benchmark]
- public void ReadObjectOld()
- {
- var reader = new MessagePackReader(SerializedData);
- var res = ObjectSerializerHandlerOld.Read(ref reader, Schema);
-
- Consumer.Consume(res);
- }
+ // [Benchmark]
+ // public void ReadObjectOld()
+ // {
+ // var reader = new MessagePackReader(SerializedData);
+ // var res = ObjectSerializerHandlerOld.Read(ref reader, Schema);
+ //
+ // Consumer.Consume(res);
+ // }
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
index 3eb504d0ac..57f0698ec8 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Table/Serialization/SerializerHandlerWriteBenchmarks.cs
@@ -21,6 +21,7 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
using BenchmarkDotNet.Attributes;
using Internal.Buffers;
using Internal.Proto;
+ using Internal.Proto.BinaryTuple;
using Internal.Table.Serialization;
/// <summary>
@@ -43,9 +44,13 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
using var pooledWriter = new PooledArrayBufferWriter();
var writer = pooledWriter.GetMessageWriter();
- writer.Write(Object.Id);
- writer.Write(Object.BodyType);
- writer.Write(Object.Seats);
+ using var tupleBuilder = new BinaryTupleBuilder(3);
+ tupleBuilder.AppendGuid(Object.Id);
+ tupleBuilder.AppendString(Object.BodyType);
+ tupleBuilder.AppendInt(Object.Seats);
+
+ writer.WriteBitSet(3);
+ writer.Write(tupleBuilder.Build().Span);
writer.Flush();
VerifyWritten(pooledWriter);
@@ -75,7 +80,7 @@ namespace Apache.Ignite.Benchmarks.Table.Serialization
VerifyWritten(pooledWriter);
}
- [Benchmark]
+ // [Benchmark]
public void WriteObjectOld()
{
using var pooledWriter = new PooledArrayBufferWriter();
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
index 94dd005217..795cbccde1 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/ComputeTests.cs
@@ -190,7 +190,7 @@ namespace Apache.Ignite.Tests.Compute
[TestCase(2, "_2")]
[TestCase(3, "")]
[TestCase(5, "_2")]
- public async Task TestExecuteColocated(int key, string nodeName)
+ public async Task TestExecuteColocated(long key, string nodeName)
{
var keyTuple = new IgniteTuple { [KeyCol] = key };
var resNodeName = await Client.Compute.ExecuteColocatedAsync<string>(TableName, keyTuple, NodeNameJob);
@@ -230,7 +230,7 @@ namespace Apache.Ignite.Tests.Compute
try
{
- var keyTuple = new IgniteTuple { [KeyCol] = 1 };
+ var keyTuple = new IgniteTuple { [KeyCol] = 1L };
var resNodeName = await Client.Compute.ExecuteColocatedAsync<string>(tableName, keyTuple, NodeNameJob);
// Drop table and create a new one with a different ID, then execute a computation again.
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
index 4e60f61b27..ba96516c73 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Proto/BinaryTuple/BinaryTupleTests.cs
@@ -18,7 +18,6 @@
namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
using System;
- using System.Linq;
using Internal.Proto.BinaryTuple;
using NUnit.Framework;
@@ -27,6 +26,8 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
/// </summary>
public class BinaryTupleTests
{
+ private delegate void BinaryTupleBuilderAction(ref BinaryTupleBuilder builder);
+
[Test]
public void TestNullValue()
{
@@ -43,23 +44,21 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
[Test]
public void TestGetValueThrowsOnNull()
{
- var reader = BuildAndRead(b => b.AppendNull());
-
- var getters = new Action<BinaryTupleReader>[]
+ var getters = new Action[]
{
- x => x.GetString(0),
- x => x.GetByte(0),
- x => x.GetShort(0),
- x => x.GetInt(0),
- x => x.GetLong(0),
- x => x.GetFloat(0),
- x => x.GetDouble(0),
- x => x.GetGuid(0)
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetString(0),
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetByte(0),
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetShort(0),
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetInt(0),
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetLong(0),
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetFloat(0),
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetDouble(0),
+ () => BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull()).GetGuid(0)
};
foreach (var getter in getters)
{
- var ex = Assert.Throws<InvalidOperationException>(() => getter(reader));
+ var ex = Assert.Throws<InvalidOperationException>(() => getter());
Assert.AreEqual("Binary tuple element with index 0 is null.", ex!.Message);
}
}
@@ -67,7 +66,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
[Test]
public void TestAppendNull()
{
- var reader = BuildAndRead(b => b.AppendNull());
+ var reader = BuildAndRead((ref BinaryTupleBuilder b) => b.AppendNull());
Assert.IsTrue(reader.HasNullMap);
Assert.IsTrue(reader.IsNull(0));
@@ -113,7 +112,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
[Test]
public void TestByte([Values(0, 1, sbyte.MaxValue, sbyte.MinValue)] sbyte value)
{
- var res = Build(b => b.AppendByte(value));
+ var res = Build((ref BinaryTupleBuilder b) => b.AppendByte(value));
Assert.AreEqual(value != 0 ? 1 : 0, res[1]);
Assert.AreEqual(value != 0 ? 3 : 2, res.Length);
@@ -129,7 +128,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
foreach (var value in values)
{
- var bytes = Build(b => b.AppendShort(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendShort(value));
Assert.AreEqual(value != 0 ? 1 : 0, bytes[1]);
Assert.AreEqual(value != 0 ? 3 : 2, bytes.Length);
@@ -142,7 +141,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
foreach (var value in values)
{
- var bytes = Build(b => b.AppendShort(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendShort(value));
Assert.AreEqual(2, bytes[1]);
Assert.AreEqual(4, bytes.Length);
@@ -158,7 +157,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
int[] values = { sbyte.MinValue, -1, 0, 1, sbyte.MaxValue };
foreach (var value in values)
{
- var bytes = Build(b => b.AppendInt(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendInt(value));
Assert.AreEqual(value != 0 ? 1 : 0, bytes[1]);
Assert.AreEqual(value != 0 ? 3 : 2, bytes.Length);
@@ -170,7 +169,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
values = new[] { short.MinValue, sbyte.MinValue - 1, sbyte.MaxValue + 1, short.MaxValue };
foreach (var value in values)
{
- var bytes = Build(b => b.AppendInt(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendInt(value));
Assert.AreEqual(2, bytes[1]);
Assert.AreEqual(4, bytes.Length);
@@ -182,7 +181,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
values = new[] { int.MinValue, short.MinValue - 1, short.MaxValue + 1, int.MaxValue };
foreach (var value in values)
{
- var bytes = Build(b => b.AppendInt(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendInt(value));
Assert.AreEqual(4, bytes[1]);
Assert.AreEqual(6, bytes.Length);
@@ -198,7 +197,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
long[] values = { sbyte.MinValue, -1, 0, 1, sbyte.MaxValue };
foreach (var value in values)
{
- var bytes = Build(b => b.AppendLong(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendLong(value));
Assert.AreEqual(value != 0 ? 1 : 0, bytes[1]);
Assert.AreEqual(value != 0 ? 3 : 2, bytes.Length);
@@ -210,7 +209,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
values = new long[] { short.MinValue, sbyte.MinValue - 1, sbyte.MaxValue + 1, short.MaxValue };
foreach (var value in values)
{
- var bytes = Build(b => b.AppendLong(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendLong(value));
Assert.AreEqual(2, bytes[1]);
Assert.AreEqual(4, bytes.Length);
@@ -222,7 +221,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
values = new long[] { int.MinValue, short.MinValue - 1, short.MaxValue + 1, int.MaxValue };
foreach (var value in values)
{
- var bytes = Build(b => b.AppendLong(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendLong(value));
Assert.AreEqual(4, bytes[1]);
Assert.AreEqual(6, bytes.Length);
@@ -234,7 +233,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
values = new[] { long.MinValue, int.MinValue - 1L, int.MaxValue + 1L, long.MaxValue };
foreach (var value in values)
{
- var bytes = Build(b => b.AppendLong(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendLong(value));
Assert.AreEqual(8, bytes[1]);
Assert.AreEqual(10, bytes.Length);
@@ -249,7 +248,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
{
float value = 0.0F;
- var bytes = Build(b => b.AppendFloat(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendFloat(value));
Assert.AreEqual(0, bytes[1]);
Assert.AreEqual(2, bytes.Length);
@@ -260,7 +259,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
float value = 0.5F;
- var bytes = Build(b => b.AppendFloat(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendFloat(value));
Assert.AreEqual(4, bytes[1]);
Assert.AreEqual(6, bytes.Length);
@@ -275,7 +274,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
{
double value = 0.0;
- var bytes = Build(b => b.AppendDouble(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendDouble(value));
Assert.AreEqual(0, bytes[1]);
Assert.AreEqual(2, bytes.Length);
@@ -286,7 +285,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
double value = 0.5;
- var bytes = Build(b => b.AppendDouble(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendDouble(value));
Assert.AreEqual(4, bytes[1]);
Assert.AreEqual(6, bytes.Length);
@@ -297,7 +296,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
double value = 0.1;
- var bytes = Build(b => b.AppendDouble(value));
+ var bytes = Build((ref BinaryTupleBuilder b) => b.AppendDouble(value));
Assert.AreEqual(8, bytes[1]);
Assert.AreEqual(10, bytes.Length);
@@ -312,7 +311,15 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
{
var values = new[] {"ascii", "我愛Java", string.Empty, "a string with a bit more characters"};
- var reader = BuildAndRead(b => values.ToList().ForEach(b.AppendString), numElements: values.Length);
+ var reader = BuildAndRead(
+ (ref BinaryTupleBuilder b) =>
+ {
+ foreach (var value in values)
+ {
+ b.AppendString(value);
+ }
+ },
+ numElements: values.Length);
for (var i = 0; i < values.Length; i++)
{
@@ -326,7 +333,7 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
var guid = Guid.NewGuid();
var reader = BuildAndRead(
- b =>
+ (ref BinaryTupleBuilder b) =>
{
b.AppendGuid(Guid.Empty);
b.AppendGuid(guid);
@@ -337,20 +344,27 @@ namespace Apache.Ignite.Tests.Proto.BinaryTuple
Assert.AreEqual(guid, reader.GetGuid(1));
}
- private static BinaryTupleReader BuildAndRead(Action<BinaryTupleBuilder> build, int numElements = 1)
+ private static BinaryTupleReader BuildAndRead(BinaryTupleBuilderAction build, int numElements = 1)
{
var bytes = Build(build, numElements);
return new BinaryTupleReader(bytes, numElements);
}
- private static byte[] Build(Action<BinaryTupleBuilder> build, int numElements = 1)
+ private static byte[] Build(BinaryTupleBuilderAction build, int numElements = 1)
{
- using var builder = new BinaryTupleBuilder(numElements);
+ var builder = new BinaryTupleBuilder(numElements);
- build(builder);
+ try
+ {
+ build.Invoke(ref builder);
- return builder.Build().ToArray();
+ return builder.Build().ToArray();
+ }
+ finally
+ {
+ builder.Dispose();
+ }
}
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
index 42ee0bfcd1..a46302d65b 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
@@ -24,7 +24,7 @@ namespace Apache.Ignite.Tests.Table
/// </summary>
public class CustomTestIgniteTuple : IIgniteTuple
{
- public const int Key = 42;
+ public const long Key = 42;
public const string Value = "Val1";
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/IgniteTupleTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/IgniteTupleTests.cs
index b49a5250a0..50756b94c1 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/IgniteTupleTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/IgniteTupleTests.cs
@@ -147,7 +147,7 @@ namespace Apache.Ignite.Tests.Table
[Test]
public void TestCustomTupleEquality()
{
- var tuple = new IgniteTuple { ["key"] = 42, ["val"] = "Val1" };
+ var tuple = new IgniteTuple { ["key"] = 42L, ["val"] = "Val1" };
var customTuple = new CustomTestIgniteTuple();
Assert.IsTrue(IIgniteTuple.Equals(tuple, customTuple));
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Poco2.cs
similarity index 51%
copy from modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
copy to modules/platforms/dotnet/Apache.Ignite.Tests/Table/Poco2.cs
index 42ee0bfcd1..716859fbaa 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Poco2.cs
@@ -17,33 +17,28 @@
namespace Apache.Ignite.Tests.Table
{
- using Ignite.Table;
-
- /// <summary>
- /// Custom tuple implementation for tests.
- /// </summary>
- public class CustomTestIgniteTuple : IIgniteTuple
+ public class Poco2
{
- public const int Key = 42;
+ public int Id { get; set; }
+
+ public sbyte Prop1 { get; set; }
+
+ public short Prop2 { get; set; }
+
+ public int Prop3 { get; set; }
+
+ public long Prop4 { get; set; }
- public const string Value = "Val1";
+ public float Prop5 { get; set; }
- public int FieldCount => 2;
+ public double Prop6 { get; set; }
- public object? this[int ordinal]
- {
- get => ordinal switch { 0 => Key, _ => Value };
- set => throw new System.NotImplementedException();
- }
+ public long Prop7 { get; set; }
- public object? this[string name]
- {
- get => name switch { "KEY" => Key, _ => Value };
- set => throw new System.NotImplementedException();
- }
+ public string? Prop8 { get; set; }
- public string GetName(int ordinal) => ordinal switch { 0 => "KEY", _ => "VAL" };
+ public int Prop9 { get; set; }
- public int GetOrdinal(string name) => name switch { "KEY" => 0, _ => 1 };
+ public int Prop10 { get; set; }
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
index e9fbea0db8..a11b046f4e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/RecordViewPocoTests.cs
@@ -524,5 +524,50 @@ namespace Apache.Ignite.Tests.Table
Assert.AreEqual(key, resTuple.Key);
Assert.AreEqual(val, resTuple.Val);
}
+
+ [Test]
+ public async Task TestBigPoco()
+ {
+ var sql = "CREATE TABLE IF NOT EXISTS TestBigPoco(ID INT PRIMARY KEY, PROP1 TINYINT, PROP2 SMALLINT, PROP3 INT, " +
+ "PROP4 BIGINT, PROP5 FLOAT, PROP6 DOUBLE, PROP7 BIGINT, PROP8 VARCHAR, PROP9 INT, PROP10 INT)";
+
+ await Client.Sql.ExecuteAsync(null, sql);
+
+ using var deferDropTable = new DisposeAction(
+ () => Client.Sql.ExecuteAsync(null, "DROP TABLE TestBigPoco").GetAwaiter().GetResult());
+
+ var table = await Client.Tables.GetTableAsync("PUBLIC.TestBigPoco");
+ var pocoView = table!.GetRecordView<Poco2>();
+
+ var poco = new Poco2
+ {
+ Id = -1,
+ Prop1 = 1,
+ Prop2 = 2,
+ Prop3 = 3,
+ Prop4 = 4,
+ Prop5 = 5,
+ Prop6 = 6,
+ Prop7 = 7,
+ Prop8 = "8",
+ Prop9 = 9,
+ Prop10 = 10
+ };
+
+ await pocoView.UpsertAsync(null, poco);
+
+ var res = await pocoView.GetAsync(null, new Poco2 { Id = -1 });
+
+ Assert.AreEqual(poco.Prop1, res!.Prop1);
+ Assert.AreEqual(poco.Prop2, res.Prop2);
+ Assert.AreEqual(poco.Prop3, res.Prop3);
+ Assert.AreEqual(poco.Prop4, res.Prop4);
+ Assert.AreEqual(poco.Prop5, res.Prop5);
+ Assert.AreEqual(poco.Prop6, res.Prop6);
+ Assert.AreEqual(poco.Prop7, res.Prop7);
+ Assert.AreEqual(poco.Prop8, res.Prop8);
+ Assert.AreEqual(poco.Prop9, res.Prop9);
+ Assert.AreEqual(poco.Prop10, res.Prop10);
+ }
}
}
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 705f277d3e..638d192be4 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/Serialization/ObjectSerializerHandlerTests.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Tests.Table.Serialization
using System;
using Internal.Buffers;
using Internal.Proto;
+ using Internal.Proto.BinaryTuple;
using Internal.Table;
using Internal.Table.Serialization;
using MessagePack;
@@ -40,31 +41,18 @@ namespace Apache.Ignite.Tests.Table.Serialization
[Test]
public void TestWrite()
{
- var reader = WriteAndGetReader();
-
- Assert.AreEqual(1234, reader.ReadInt32());
- Assert.AreEqual("foo", reader.ReadString());
- Assert.IsTrue(reader.End);
- }
-
- [Test]
- public void TestWriteUnsigned()
- {
- var bytes = Write(new UnsignedPoco(ulong.MaxValue, "foo"));
- var reader = new MessagePackReader(bytes);
+ var reader = WriteAndGetTupleReader();
- Assert.AreEqual(ulong.MaxValue, reader.ReadUInt64());
- Assert.AreEqual("foo", reader.ReadString());
- Assert.IsTrue(reader.End);
+ Assert.AreEqual(1234, reader.GetInt(0));
+ Assert.AreEqual("foo", reader.GetString(1));
}
[Test]
public void TestWriteKeyOnly()
{
- var reader = WriteAndGetReader(keyOnly: true);
+ var reader = WriteAndGetTupleReader(keyOnly: true);
- Assert.AreEqual(1234, reader.ReadInt32());
- Assert.IsTrue(reader.End);
+ Assert.AreEqual(1234, reader.GetInt(0));
}
[Test]
@@ -80,24 +68,13 @@ namespace Apache.Ignite.Tests.Table.Serialization
[Test]
public void TestReadKeyOnly()
{
- var reader = WriteAndGetReader();
+ var reader = WriteAndGetReader(true);
var resPoco = new ObjectSerializerHandler<Poco>().Read(ref reader, Schema, keyOnly: true);
Assert.AreEqual(1234, resPoco.Key);
Assert.IsNull(resPoco.Val);
}
- [Test]
- public void TestReadValuePart()
- {
- var reader = WriteAndGetReader();
- reader.Skip(); // Skip key.
- var resPoco = new ObjectSerializerHandler<Poco>().ReadValuePart(ref reader, Schema, new Poco{Key = 4321});
-
- Assert.AreEqual(4321, resPoco.Key);
- Assert.AreEqual("foo", resPoco.Val);
- }
-
[Test]
public void TestReadUnsupportedFieldTypeThrowsException()
{
@@ -131,6 +108,14 @@ namespace Apache.Ignite.Tests.Table.Serialization
return new MessagePackReader(bytes);
}
+ private static BinaryTupleReader WriteAndGetTupleReader(bool keyOnly = false)
+ {
+ var msgPackReader = WriteAndGetReader(keyOnly);
+ var bytes = msgPackReader.ReadBytesAsMemory();
+
+ return new BinaryTupleReader(bytes, keyOnly ? 1 : 2);
+ }
+
private static byte[] Write<T>(T obj, bool keyOnly = false)
where T : class
{
@@ -141,11 +126,11 @@ namespace Apache.Ignite.Tests.Table.Serialization
handler.Write(ref writer, Schema, obj, keyOnly);
writer.Flush();
- return pooledWriter.GetWrittenMemory().ToArray();
+
+ // Slice NoValueSet.
+ return pooledWriter.GetWrittenMemory().Slice(3).ToArray();
}
private record BadPoco(Guid Key, DateTimeOffset Val);
-
- private record UnsignedPoco(ulong Key, string Val);
}
}
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 1381d05f32..781e51d58b 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleBuilder.cs
@@ -19,13 +19,12 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
using System;
using System.Diagnostics;
- using System.Text;
using Buffers;
/// <summary>
/// Binary tuple builder.
/// </summary>
- internal sealed class BinaryTupleBuilder : IDisposable // TODO: Support all types (IGNITE-15431).
+ internal ref struct BinaryTupleBuilder // TODO: Support all types (IGNITE-15431).
{
/** Number of elements in the tuple. */
private readonly int _numElements;
@@ -40,7 +39,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
private readonly int _valueBase;
/** Buffer for tuple content. */
- private readonly PooledArrayBufferWriter _buffer = new();
+ private readonly PooledArrayBufferWriter _buffer;
/** Flag indicating if any NULL values were really put here. */
private bool _hasNullValues;
@@ -49,16 +48,19 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
private int _elementIndex;
/// <summary>
- /// Initializes a new instance of the <see cref="BinaryTupleBuilder"/> class.
+ /// Initializes a new instance of the <see cref="BinaryTupleBuilder"/> struct.
/// </summary>
/// <param name="numElements">Capacity.</param>
/// <param name="allowNulls">Whether nulls are allowed.</param>
/// <param name="totalValueSize">Total value size, -1 when unknown.</param>
public BinaryTupleBuilder(int numElements, bool allowNulls = true, int totalValueSize = -1)
{
- Debug.Assert(numElements > 0, "numElements > 0");
+ Debug.Assert(numElements >= 0, "numElements >= 0");
_numElements = numElements;
+ _buffer = new();
+ _elementIndex = 0;
+ _hasNullValues = false;
int baseOffset = BinaryTupleCommon.HeaderSize;
if (allowNulls)
@@ -108,6 +110,14 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
OnWrite();
}
+ /// <summary>
+ /// Appends a default value.
+ /// </summary>
+ public void AppendDefault()
+ {
+ OnWrite();
+ }
+
/// <summary>
/// Appends a byte.
/// </summary>
@@ -229,6 +239,23 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
OnWrite();
}
+ /// <summary>
+ /// Appends a string.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ public void AppendStringNullable(string? value)
+ {
+ if (value == null)
+ {
+ AppendNull();
+ return;
+ }
+
+ PutString(value);
+
+ OnWrite();
+ }
+
/// <summary>
/// Appends bytes.
/// </summary>
@@ -253,6 +280,65 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
OnWrite();
}
+ /// <summary>
+ /// Appends an object.
+ /// </summary>
+ /// <param name="value">Value.</param>
+ /// <param name="colType">Column type.</param>
+ public void AppendObject(object? value, ClientDataType colType)
+ {
+ if (value == null)
+ {
+ AppendNull();
+ return;
+ }
+
+ switch (colType)
+ {
+ case ClientDataType.Int8:
+ AppendByte((sbyte)value);
+ break;
+
+ case ClientDataType.Int16:
+ AppendShort((short)value);
+ break;
+
+ case ClientDataType.Int32:
+ AppendInt((int)value);
+ break;
+
+ case ClientDataType.Int64:
+ AppendLong((long)value);
+ break;
+
+ case ClientDataType.Float:
+ AppendFloat((float)value);
+ break;
+
+ case ClientDataType.Double:
+ AppendDouble((double)value);
+ break;
+
+ case ClientDataType.Uuid:
+ AppendGuid((Guid)value);
+ break;
+
+ case ClientDataType.String:
+ AppendString((string)value);
+ break;
+
+ case ClientDataType.Bytes:
+ AppendBytes((byte[])value);
+ break;
+
+ case ClientDataType.BitMask:
+ case ClientDataType.Decimal:
+ default:
+ // TODO: Support all types (IGNITE-15431).
+ throw new IgniteClientException("Unsupported type: " + colType);
+ }
+ }
+
/// <summary>
/// Builds the tuple.
/// <para />
@@ -328,7 +414,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
return _buffer.GetWrittenMemory().Slice(offset);
}
- /// <inheritdoc/>
+ /// <summary>
+ /// Disposes this instance.
+ /// </summary>
public void Dispose()
{
_buffer.Dispose();
@@ -355,10 +443,10 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
return;
}
- var maxByteCount = Encoding.UTF8.GetMaxByteCount(value.Length);
+ var maxByteCount = BinaryTupleCommon.StringEncoding.GetMaxByteCount(value.Length);
var span = _buffer.GetSpan(maxByteCount);
- var actualBytes = Encoding.UTF8.GetBytes(value, span);
+ var actualBytes = BinaryTupleCommon.StringEncoding.GetBytes(value, span);
_buffer.Advance(actualBytes);
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleCommon.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleCommon.cs
index 6bfb494e13..ed5704f343 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleCommon.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleCommon.cs
@@ -17,6 +17,9 @@
namespace Apache.Ignite.Internal.Proto.BinaryTuple
{
+ using System.Diagnostics;
+ using System.Text;
+
/// <summary>
/// Common binary tuple constants and utils.
/// </summary>
@@ -37,6 +40,11 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
/// </summary>
public const int NullmapFlag = 0b100;
+ /// <summary>
+ /// UTF8 encoding without preamble (as opposed to <see cref="Encoding.UTF8"/>).
+ /// </summary>
+ public static readonly Encoding StringEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
+
/// <summary>
/// Calculates flags for a given size of variable-length area.
/// </summary>
@@ -54,12 +62,9 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
return 0b01;
}
- if (size <= int.MaxValue)
- {
- return 0b10;
- }
+ Debug.Assert(size <= int.MaxValue, "size <= int.MaxValue");
- throw new IgniteClientException("Too big binary tuple size");
+ return 0b10;
}
/// <summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
index 36cabae670..7ea6189474 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/BinaryTuple/BinaryTupleReader.cs
@@ -20,12 +20,11 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
using System;
using System.Buffers.Binary;
using System.Diagnostics;
- using System.Text;
/// <summary>
/// Binary tuple reader.
/// </summary>
- internal sealed class BinaryTupleReader // TODO: Support all types (IGNITE-15431).
+ internal readonly ref struct BinaryTupleReader // TODO: Support all types (IGNITE-15431).
{
/** Buffer. */
private readonly ReadOnlyMemory<byte> _buffer;
@@ -43,7 +42,7 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
private readonly int _valueBase;
/// <summary>
- /// Initializes a new instance of the <see cref="BinaryTupleReader"/> class.
+ /// Initializes a new instance of the <see cref="BinaryTupleReader"/> struct.
/// </summary>
/// <param name="buffer">Buffer.</param>
/// <param name="numElements">Number of elements in the tuple.</param>
@@ -158,9 +157,16 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
public string GetString(int index) => Seek(index) switch
{
{ IsEmpty: true } => string.Empty,
- var s => Encoding.UTF8.GetString(s)
+ var s => BinaryTupleCommon.StringEncoding.GetString(s)
};
+ /// <summary>
+ /// Gets a string value.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <returns>Value.</returns>
+ public string? GetStringNullable(int index) => IsNull(index) ? null : GetString(index);
+
/// <summary>
/// Gets a float value.
/// </summary>
@@ -184,6 +190,35 @@ namespace Apache.Ignite.Internal.Proto.BinaryTuple
var s => BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(s))
};
+ /// <summary>
+ /// Gets an object value according to the specified type.
+ /// </summary>
+ /// <param name="index">Index.</param>
+ /// <param name="columnType">Column type.</param>
+ /// <returns>Value.</returns>
+ public object? GetObject(int index, ClientDataType columnType)
+ {
+ if (IsNull(index))
+ {
+ return null;
+ }
+
+ return columnType switch
+ {
+ ClientDataType.Int8 => GetByte(index),
+ ClientDataType.Int16 => GetShort(index),
+ ClientDataType.Int32 => GetInt(index),
+ ClientDataType.Int64 => GetLong(index),
+ ClientDataType.Float => GetFloat(index),
+ ClientDataType.Double => GetDouble(index),
+ ClientDataType.Uuid => GetGuid(index),
+ ClientDataType.String => GetString(index),
+
+ // TODO: Support all types (IGNITE-15431).
+ _ => throw new IgniteClientException("Unsupported type: " + columnType)
+ };
+ }
+
private int GetOffset(int position)
{
var span = _buffer.Span[position..];
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs
index d694abe61b..7f51ebad72 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackReaderExtensions.cs
@@ -170,6 +170,20 @@ namespace Apache.Ignite.Internal.Proto
return false;
}
+ /// <summary>
+ /// Reads binary value as <see cref="ReadOnlyMemory{T}"/>.
+ /// </summary>
+ /// <param name="reader">Reader.</param>
+ /// <returns>Binary value.</returns>
+ public static ReadOnlyMemory<byte> ReadBytesAsMemory(this ref MessagePackReader reader)
+ {
+ ReadOnlySequence<byte> tupleSeq = reader.ReadBytes()!.Value;
+
+ Debug.Assert(tupleSeq.IsSingleSegment, "tupleSeq.IsSingleSegment");
+
+ return tupleSeq.First;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ValidateExtensionType(
ref MessagePackReader reader,
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
index 9428590ec7..b4f7792636 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Proto/MessagePackWriterExtensions.cs
@@ -57,6 +57,24 @@ namespace Apache.Ignite.Internal.Proto
writer.Advance(1);
}
+ /// <summary>
+ /// Writes BitSet header and reserves space for bits, returns a span to write bits to.
+ /// </summary>
+ /// <param name="writer">Writer.</param>
+ /// <param name="bitCount">Bit count.</param>
+ /// <returns>Span to write bits to.</returns>
+ public static Span<byte> WriteBitSet(this ref MessagePackWriter writer, int bitCount)
+ {
+ var byteCount = bitCount / 8 + 1;
+ writer.WriteExtensionFormatHeader(new ExtensionHeader((sbyte)ClientMessagePackType.Bitmask, byteCount));
+
+ var span = writer.GetSpan(byteCount)[..byteCount];
+ span.Clear();
+ writer.Advance(byteCount);
+
+ return span;
+ }
+
/// <summary>
/// Writes an object.
/// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleBuilderExtensions.cs
similarity index 52%
copy from modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
copy to modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleBuilderExtensions.cs
index 42ee0bfcd1..a93f02297e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleBuilderExtensions.cs
@@ -15,35 +15,25 @@
* limitations under the License.
*/
-namespace Apache.Ignite.Tests.Table
+namespace Apache.Ignite.Internal.Table.Serialization
{
- using Ignite.Table;
+ using System;
+ using Proto.BinaryTuple;
/// <summary>
- /// Custom tuple implementation for tests.
+ /// Extensions for <see cref="BinaryTupleBuilder"/>.
/// </summary>
- public class CustomTestIgniteTuple : IIgniteTuple
+ internal static class BinaryTupleBuilderExtensions
{
- public const int Key = 42;
-
- public const string Value = "Val1";
-
- public int FieldCount => 2;
-
- public object? this[int ordinal]
- {
- get => ordinal switch { 0 => Key, _ => Value };
- set => throw new System.NotImplementedException();
- }
-
- public object? this[string name]
+ /// <summary>
+ /// Appends a no-value marker.
+ /// </summary>
+ /// <param name="builder">Builder.</param>
+ /// <param name="noValueSet">No-value bit set.</param>
+ public static void AppendNoValue(this ref BinaryTupleBuilder builder, Span<byte> noValueSet)
{
- get => name switch { "KEY" => Key, _ => Value };
- set => throw new System.NotImplementedException();
+ noValueSet.SetBit(builder.ElementIndex);
+ builder.AppendDefault();
}
-
- public string GetName(int ordinal) => ordinal switch { 0 => "KEY", _ => "VAL" };
-
- public int GetOrdinal(string name) => name switch { "KEY" => 0, _ => 1 };
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleMethods.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleMethods.cs
new file mode 100644
index 0000000000..b461a71ab9
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/BinaryTupleMethods.cs
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Internal.Table.Serialization
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Reflection;
+ using Proto.BinaryTuple;
+
+ /// <summary>
+ /// MethodInfos for <see cref="BinaryTupleBuilder"/> and <see cref="BinaryTupleReader"/>.
+ /// </summary>
+ internal static class BinaryTupleMethods
+ {
+ /// <summary>
+ /// No-value writer.
+ /// </summary>
+ public static readonly MethodInfo WriteNoValue =
+ typeof(BinaryTupleBuilderExtensions).GetMethod(nameof(BinaryTupleBuilderExtensions.AppendNoValue))!;
+
+ private static readonly MethodInfo AppendByte = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendByte))!;
+ private static readonly MethodInfo AppendShort = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendShort))!;
+ private static readonly MethodInfo AppendInt = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendInt))!;
+ private static readonly MethodInfo AppendLong = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendLong))!;
+ private static readonly MethodInfo AppendFloat = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendFloat))!;
+ private static readonly MethodInfo AppendDouble = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendDouble))!;
+ private static readonly MethodInfo AppendGuid = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendGuid))!;
+ private static readonly MethodInfo AppendString = typeof(BinaryTupleBuilder).GetMethod(nameof(BinaryTupleBuilder.AppendStringNullable))!;
+
+ private static readonly MethodInfo GetByte = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetByte))!;
+ private static readonly MethodInfo GetShort = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetShort))!;
+ private static readonly MethodInfo GetInt = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetInt))!;
+ private static readonly MethodInfo GetLong = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetLong))!;
+ private static readonly MethodInfo GetFloat = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetFloat))!;
+ private static readonly MethodInfo GetDouble = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetDouble))!;
+ private static readonly MethodInfo GetGuid = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetGuid))!;
+ private static readonly MethodInfo GetString = typeof(BinaryTupleReader).GetMethod(nameof(BinaryTupleReader.GetStringNullable))!;
+
+ // TODO: Support all types (IGNITE-15431).
+ private static readonly IReadOnlyDictionary<Type, MethodInfo> WriteMethods = new Dictionary<Type, MethodInfo>
+ {
+ { typeof(string), AppendString },
+ { typeof(sbyte), AppendByte },
+ { typeof(short), AppendShort },
+ { typeof(int), AppendInt },
+ { typeof(long), AppendLong },
+ { typeof(float), AppendFloat },
+ { typeof(double), AppendDouble },
+ { typeof(Guid), AppendGuid }
+ };
+
+ private static readonly IReadOnlyDictionary<Type, MethodInfo> ReadMethods = new Dictionary<Type, MethodInfo>
+ {
+ { typeof(string), GetString },
+ { typeof(sbyte), GetByte },
+ { typeof(short), GetShort },
+ { typeof(int), GetInt },
+ { typeof(long), GetLong },
+ { typeof(float), GetFloat },
+ { typeof(double), GetDouble },
+ { typeof(Guid), GetGuid }
+ };
+
+ /// <summary>
+ /// Gets the write method.
+ /// </summary>
+ /// <param name="valueType">Type of the value to write.</param>
+ /// <returns>Write method for the specified value type.</returns>
+ public static MethodInfo GetWriteMethod(Type valueType) =>
+ WriteMethods.TryGetValue(valueType, out var method)
+ ? method
+ : throw new IgniteClientException("Unsupported type: " + valueType);
+
+ /// <summary>
+ /// Gets the read method.
+ /// </summary>
+ /// <param name="valueType">Type of the value to read.</param>
+ /// <returns>Read method for the specified value type.</returns>
+ public static MethodInfo GetReadMethod(Type valueType) =>
+ ReadMethods.TryGetValue(valueType, out var method)
+ ? method
+ : throw new IgniteClientException("Unsupported type: " + valueType);
+ }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ByteSpanExtensions.cs
similarity index 51%
copy from modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
copy to modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ByteSpanExtensions.cs
index 42ee0bfcd1..4edd6f82ae 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/CustomTestIgniteTuple.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ByteSpanExtensions.cs
@@ -15,35 +15,20 @@
* limitations under the License.
*/
-namespace Apache.Ignite.Tests.Table
+namespace Apache.Ignite.Internal.Table.Serialization
{
- using Ignite.Table;
+ using System;
/// <summary>
- /// Custom tuple implementation for tests.
+ /// Extensions for <see cref="Span{T}"/>.
/// </summary>
- public class CustomTestIgniteTuple : IIgniteTuple
+ internal static class ByteSpanExtensions
{
- public const int Key = 42;
-
- public const string Value = "Val1";
-
- public int FieldCount => 2;
-
- public object? this[int ordinal]
- {
- get => ordinal switch { 0 => Key, _ => Value };
- set => throw new System.NotImplementedException();
- }
-
- public object? this[string name]
- {
- get => name switch { "KEY" => Key, _ => Value };
- set => throw new System.NotImplementedException();
- }
-
- public string GetName(int ordinal) => ordinal switch { 0 => "KEY", _ => "VAL" };
-
- public int GetOrdinal(string name) => name switch { "KEY" => 0, _ => 1 };
+ /// <summary>
+ /// Sets the bit at the specified index.
+ /// </summary>
+ /// <param name="span">Span.</param>
+ /// <param name="index">Bit index.</param>
+ public static void SetBit(this Span<byte> span, int index) => span[index / 8] |= (byte)(1 << (index % 8));
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/MessagePackMethods.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/MessagePackMethods.cs
deleted file mode 100644
index 4ca4de7d24..0000000000
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/MessagePackMethods.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-namespace Apache.Ignite.Internal.Table.Serialization
-{
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using MessagePack;
- using Proto;
-
- /// <summary>
- /// MethodInfos for <see cref="MessagePackWriter"/> and <see cref="MessagePackReader"/>.
- /// </summary>
- internal static class MessagePackMethods
- {
- /// <summary>
- /// No-value writer.
- /// </summary>
- public static readonly MethodInfo WriteNoValue =
- typeof(MessagePackWriterExtensions).GetMethod(nameof(MessagePackWriterExtensions.WriteNoValue))!;
-
- /// <summary>
- /// No-value reader.
- /// </summary>
- public static readonly MethodInfo TryReadNoValue =
- typeof(MessagePackReaderExtensions).GetMethod(nameof(MessagePackReaderExtensions.TryReadNoValue))!;
-
- /// <summary>
- /// Skip reader.
- /// </summary>
- public static readonly MethodInfo Skip =
- typeof(MessagePackReaderExtensions).GetMethod(nameof(MessagePackReaderExtensions.Skip))!;
-
- // TODO: Support all types (IGNITE-15431).
- private static readonly IReadOnlyDictionary<Type, MethodInfo> WriteMethods = new Dictionary<Type, MethodInfo>
- {
- { typeof(string), GetWriteMethod<string>() },
- { typeof(byte), GetWriteMethod<byte>() },
- { typeof(sbyte), GetWriteMethod<sbyte>() },
- { typeof(short), GetWriteMethod<short>() },
- { typeof(ushort), GetWriteMethod<ushort>() },
- { typeof(int), GetWriteMethod<int>() },
- { typeof(uint), GetWriteMethod<uint>() },
- { typeof(long), GetWriteMethod<long>() },
- { typeof(ulong), GetWriteMethod<ulong>() },
- { typeof(float), GetWriteMethod<float>() },
- { typeof(Guid), GetWriteMethod<Guid>() },
- };
-
- private static readonly IReadOnlyDictionary<Type, MethodInfo> ReadMethods = new Dictionary<Type, MethodInfo>
- {
- { typeof(string), GetReadMethod<string>() },
- { typeof(byte), GetReadMethod<byte>() },
- { typeof(sbyte), GetReadMethod<sbyte>() },
- { typeof(short), GetReadMethod<short>() },
- { typeof(ushort), GetReadMethod<ushort>() },
- { typeof(int), GetReadMethod<int>() },
- { typeof(uint), GetReadMethod<uint>() },
- { typeof(long), GetReadMethod<long>() },
- { typeof(ulong), GetReadMethod<ulong>() },
- { typeof(float), GetReadMethod<float>() },
- { typeof(Guid), GetReadMethod<Guid>() },
- };
-
- /// <summary>
- /// Gets the write method.
- /// </summary>
- /// <param name="valueType">Type of the value to write.</param>
- /// <returns>Write method for the specified value type.</returns>
- public static MethodInfo GetWriteMethod(Type valueType) =>
- WriteMethods.TryGetValue(valueType, out var method)
- ? method
- : throw new IgniteClientException("Unsupported type: " + valueType);
-
- /// <summary>
- /// Gets the read method.
- /// </summary>
- /// <param name="valueType">Type of the value to read.</param>
- /// <returns>Read method for the specified value type.</returns>
- public static MethodInfo GetReadMethod(Type valueType) =>
- ReadMethods.TryGetValue(valueType, out var method)
- ? method
- : throw new IgniteClientException("Unsupported type: " + valueType);
-
- private static MethodInfo GetWriteMethod<TArg>()
- {
- const string methodName = nameof(MessagePackWriter.Write);
-
- var methodInfo = typeof(MessagePackWriter).GetMethod(methodName, new[] { typeof(TArg) }) ??
- typeof(MessagePackWriterExtensions).GetMethod(
- methodName, new[] { typeof(MessagePackWriter).MakeByRefType(), typeof(TArg) });
-
- if (methodInfo == null)
- {
- throw new InvalidOperationException($"Method not found: Write({typeof(TArg).Name})");
- }
-
- return methodInfo;
- }
-
- private static MethodInfo GetReadMethod<TRes>()
- {
- var methodName = "Read" + typeof(TRes).Name;
-
- var methodInfo = typeof(MessagePackReader).GetMethod(methodName) ??
- typeof(MessagePackReaderExtensions).GetMethod(methodName);
-
- if (methodInfo == null)
- {
- throw new InvalidOperationException($"Method not found: {methodName}");
- }
-
- return methodInfo;
- }
- }
-}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
index 9cd7f33005..8fc4f257bc 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/ObjectSerializerHandler.cs
@@ -17,11 +17,13 @@
namespace Apache.Ignite.Internal.Table.Serialization
{
+ using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Reflection.Emit;
using MessagePack;
using Proto;
+ using Proto.BinaryTuple;
/// <summary>
/// Object serializer handler.
@@ -36,11 +38,11 @@ namespace Apache.Ignite.Internal.Table.Serialization
private readonly ConcurrentDictionary<int, ReadValuePartDelegate<T>> _valuePartReaders = new();
- private delegate void WriteDelegate<in TV>(ref MessagePackWriter writer, TV value);
+ private delegate void WriteDelegate<in TV>(ref BinaryTupleBuilder writer, Span<byte> noValueSet, TV value);
- private delegate TV ReadDelegate<out TV>(ref MessagePackReader reader);
+ private delegate TV ReadDelegate<out TV>(ref BinaryTupleReader reader);
- private delegate TV ReadValuePartDelegate<TV>(ref MessagePackReader reader, TV key);
+ private delegate TV ReadValuePartDelegate<TV>(ref BinaryTupleReader reader, TV key);
/// <inheritdoc/>
public T Read(ref MessagePackReader reader, Schema schema, bool keyOnly = false)
@@ -51,7 +53,11 @@ namespace Apache.Ignite.Internal.Table.Serialization
? w
: _readers.GetOrAdd(cacheKey, EmitReader(schema, keyOnly));
- return readDelegate(ref reader);
+ var columnCount = keyOnly ? schema.KeyColumnCount : schema.Columns.Count;
+
+ var binaryTupleReader = new BinaryTupleReader(reader.ReadBytesAsMemory(), columnCount);
+
+ return readDelegate(ref binaryTupleReader);
}
/// <inheritdoc/>
@@ -61,7 +67,9 @@ namespace Apache.Ignite.Internal.Table.Serialization
? w
: _valuePartReaders.GetOrAdd(schema.Version, EmitValuePartReader(schema));
- return readDelegate(ref reader, key);
+ var binaryTupleReader = new BinaryTupleReader(reader.ReadBytesAsMemory(), schema.Columns.Count - schema.KeyColumnCount);
+
+ return readDelegate(ref binaryTupleReader, key);
}
/// <inheritdoc/>
@@ -73,7 +81,21 @@ namespace Apache.Ignite.Internal.Table.Serialization
? w
: _writers.GetOrAdd(cacheKey, EmitWriter(schema, keyOnly));
- writeDelegate(ref writer, record);
+ var count = keyOnly ? schema.KeyColumnCount : schema.Columns.Count;
+ var noValueSet = writer.WriteBitSet(count);
+ var tupleBuilder = new BinaryTupleBuilder(count);
+
+ try
+ {
+ writeDelegate(ref tupleBuilder, noValueSet, record);
+
+ var binaryTupleMemory = tupleBuilder.Build();
+ writer.Write(binaryTupleMemory.Span);
+ }
+ finally
+ {
+ tupleBuilder.Dispose();
+ }
}
private static WriteDelegate<T> EmitWriter(Schema schema, bool keyOnly)
@@ -83,7 +105,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
var method = new DynamicMethod(
name: "Write" + type.Name,
returnType: typeof(void),
- parameterTypes: new[] { typeof(MessagePackWriter).MakeByRefType(), type },
+ parameterTypes: new[] { typeof(BinaryTupleBuilder).MakeByRefType(), typeof(Span<byte>), type },
m: typeof(IIgnite).Module,
skipVisibility: true);
@@ -100,16 +122,17 @@ namespace Apache.Ignite.Internal.Table.Serialization
if (fieldInfo == null)
{
il.Emit(OpCodes.Ldarg_0); // writer
- il.Emit(OpCodes.Call, MessagePackMethods.WriteNoValue);
+ il.Emit(OpCodes.Ldarg_1); // noValueSet
+ il.Emit(OpCodes.Call, BinaryTupleMethods.WriteNoValue);
}
else
{
ValidateFieldType(fieldInfo, col);
il.Emit(OpCodes.Ldarg_0); // writer
- il.Emit(OpCodes.Ldarg_1); // record
+ il.Emit(OpCodes.Ldarg_2); // record
il.Emit(OpCodes.Ldfld, fieldInfo);
- var writeMethod = MessagePackMethods.GetWriteMethod(fieldInfo.FieldType);
+ var writeMethod = BinaryTupleMethods.GetWriteMethod(fieldInfo.FieldType);
il.Emit(OpCodes.Call, writeMethod);
}
}
@@ -126,7 +149,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
var method = new DynamicMethod(
name: "Read" + type.Name,
returnType: type,
- parameterTypes: new[] { typeof(MessagePackReader).MakeByRefType() },
+ parameterTypes: new[] { typeof(BinaryTupleReader).MakeByRefType() },
m: typeof(IIgnite).Module,
skipVisibility: true);
@@ -147,7 +170,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
var col = columns[i];
var fieldInfo = type.GetFieldIgnoreCase(col.Name);
- EmitFieldRead(fieldInfo, il, col);
+ EmitFieldRead(fieldInfo, il, col, i);
}
il.Emit(OpCodes.Ldloc_0); // res
@@ -163,7 +186,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
var method = new DynamicMethod(
name: "ReadValuePart" + type.Name,
returnType: type,
- parameterTypes: new[] { typeof(MessagePackReader).MakeByRefType(), type },
+ parameterTypes: new[] { typeof(BinaryTupleReader).MakeByRefType(), type },
m: typeof(IIgnite).Module,
skipVisibility: true);
@@ -196,7 +219,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
continue;
}
- EmitFieldRead(fieldInfo, il, col);
+ EmitFieldRead(fieldInfo, il, col, i - schema.KeyColumnCount);
}
il.Emit(OpCodes.Ldloc_0); // res
@@ -205,31 +228,68 @@ namespace Apache.Ignite.Internal.Table.Serialization
return (ReadValuePartDelegate<T>)method.CreateDelegate(typeof(ReadValuePartDelegate<T>));
}
- private static void EmitFieldRead(FieldInfo? fieldInfo, ILGenerator il, Column col)
+ private static void EmitFieldRead(FieldInfo? fieldInfo, ILGenerator il, Column col, int elemIdx)
{
if (fieldInfo == null)
{
- il.Emit(OpCodes.Ldarg_0); // reader
- il.Emit(OpCodes.Call, MessagePackMethods.Skip);
+ return;
}
- else
+
+ ValidateFieldType(fieldInfo, col);
+
+ var readMethod = BinaryTupleMethods.GetReadMethod(fieldInfo.FieldType);
+
+ il.Emit(OpCodes.Ldloc_0); // res
+ il.Emit(OpCodes.Ldarg_0); // reader
+ EmitLdcI4(il, elemIdx); // index
+
+ il.Emit(OpCodes.Call, readMethod);
+ il.Emit(OpCodes.Stfld, fieldInfo); // res.field = value
+ }
+
+ private static void EmitLdcI4(ILGenerator il, int elemIdx)
+ {
+ switch (elemIdx)
{
- ValidateFieldType(fieldInfo, col);
- il.Emit(OpCodes.Ldarg_0); // reader
- il.Emit(OpCodes.Call, MessagePackMethods.TryReadNoValue);
+ case 0:
+ il.Emit(OpCodes.Ldc_I4_0);
+ break;
+
+ case 1:
+ il.Emit(OpCodes.Ldc_I4_1);
+ break;
+
+ case 2:
+ il.Emit(OpCodes.Ldc_I4_2);
+ break;
+
+ case 3:
+ il.Emit(OpCodes.Ldc_I4_3);
+ break;
+
+ case 4:
+ il.Emit(OpCodes.Ldc_I4_4);
+ break;
- Label noValueLabel = il.DefineLabel();
- il.Emit(OpCodes.Brtrue_S, noValueLabel);
+ case 5:
+ il.Emit(OpCodes.Ldc_I4_5);
+ break;
- var readMethod = MessagePackMethods.GetReadMethod(fieldInfo.FieldType);
+ case 6:
+ il.Emit(OpCodes.Ldc_I4_6);
+ break;
- il.Emit(OpCodes.Ldloc_0); // res
- il.Emit(OpCodes.Ldarg_0); // reader
+ case 7:
+ il.Emit(OpCodes.Ldc_I4_7);
+ break;
- il.Emit(OpCodes.Call, readMethod);
- il.Emit(OpCodes.Stfld, fieldInfo); // res.field = value
+ case 8:
+ il.Emit(OpCodes.Ldc_I4_8);
+ break;
- il.MarkLabel(noValueLabel);
+ default:
+ il.Emit(OpCodes.Ldc_I4, elemIdx);
+ break;
}
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
index e2f29523d5..747a5a53a1 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Table/Serialization/TupleSerializerHandler.cs
@@ -20,6 +20,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
using Ignite.Table;
using MessagePack;
using Proto;
+ using Proto.BinaryTuple;
/// <summary>
/// Serializer handler for <see cref="IIgniteTuple"/>.
@@ -45,16 +46,12 @@ namespace Apache.Ignite.Internal.Table.Serialization
var columns = schema.Columns;
var count = keyOnly ? schema.KeyColumnCount : columns.Count;
var tuple = new IgniteTuple(count);
+ var tupleReader = new BinaryTupleReader(reader.ReadBytesAsMemory(), count);
for (var index = 0; index < count; index++)
{
- if (reader.TryReadNoValue())
- {
- continue;
- }
-
var column = columns[index];
- tuple[column.Name] = reader.ReadObject(column.Type);
+ tuple[column.Name] = tupleReader.GetObject(index, column.Type);
}
return tuple;
@@ -65,6 +62,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
{
var columns = schema.Columns;
var tuple = new IgniteTuple(columns.Count);
+ var tupleReader = new BinaryTupleReader(reader.ReadBytesAsMemory(), schema.Columns.Count - schema.KeyColumnCount);
for (var i = 0; i < columns.Count; i++)
{
@@ -76,12 +74,7 @@ namespace Apache.Ignite.Internal.Table.Serialization
}
else
{
- if (reader.TryReadNoValue())
- {
- continue;
- }
-
- tuple[column.Name] = reader.ReadObject(column.Type);
+ tuple[column.Name] = tupleReader.GetObject(i - schema.KeyColumnCount, column.Type);
}
}
@@ -93,20 +86,33 @@ namespace Apache.Ignite.Internal.Table.Serialization
{
var columns = schema.Columns;
var count = keyOnly ? schema.KeyColumnCount : columns.Count;
+ var noValueSet = writer.WriteBitSet(count);
- for (var index = 0; index < count; index++)
- {
- var col = columns[index];
- var colIdx = record.GetOrdinal(col.Name);
+ var tupleBuilder = new BinaryTupleBuilder(count);
- if (colIdx < 0)
- {
- writer.WriteNoValue();
- }
- else
+ try
+ {
+ for (var index = 0; index < count; index++)
{
- writer.WriteObject(record[colIdx]);
+ var col = columns[index];
+ var colIdx = record.GetOrdinal(col.Name);
+
+ if (colIdx >= 0)
+ {
+ tupleBuilder.AppendObject(record[colIdx], col.Type);
+ }
+ else
+ {
+ tupleBuilder.AppendNoValue(noValueSet);
+ }
}
+
+ var binaryTupleMemory = tupleBuilder.Build();
+ writer.Write(binaryTupleMemory.Span);
+ }
+ finally
+ {
+ tupleBuilder.Dispose();
}
}
}