You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2021/05/24 07:53:38 UTC

[ignite-3] branch main updated: IGNITE-14388: Add affinity key support. (#127)

This is an automated email from the ASF dual-hosted git repository.

amashenkov 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 dd35b12  IGNITE-14388: Add affinity key support. (#127)
dd35b12 is described below

commit dd35b12eaa02c830f4f2906d07e919edabad4efa
Author: Andrew V. Mashenkov <AM...@users.noreply.github.com>
AuthorDate: Mon May 24 10:53:33 2021 +0300

    IGNITE-14388: Add affinity key support. (#127)
---
 .../apache/ignite/internal/util/ArrayUtils.java    |  10 +-
 .../ignite/internal/schema/RowAssembler.java       |  58 ++++++-
 .../ignite/internal/schema/SchemaDescriptor.java   |  22 ++-
 .../ignite/internal/schema/SchemaManager.java      |   1 +
 .../ignite/internal/schema/RowAssemblerTest.java   | 185 ++++++++++++++++-----
 5 files changed, 227 insertions(+), 49 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java
index 0457241..f7efe7f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/ArrayUtils.java
@@ -180,10 +180,18 @@ public final class ArrayUtils {
     };
 
     /**
+     * @param arr Array to check.
+     * @param <T> Array element type.
+     * @return {@code true} if {@code null} or an empty array is provided, {@code false} otherwise.
+     */
+    public static <T> boolean nullOrEmpty(T[] arr) {
+        return arr == null || arr.length == 0;
+    }
+
+    /**
      * Stub.
      */
     private ArrayUtils() {
         // No op.
     }
-
 }
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/RowAssembler.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/RowAssembler.java
index 367d8d0..770ab6d 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/RowAssembler.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/RowAssembler.java
@@ -20,10 +20,13 @@ package org.apache.ignite.internal.schema;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.UUID;
 import org.apache.ignite.internal.schema.BinaryRow.RowFlags;
 
+import static org.apache.ignite.internal.schema.BinaryRow.RowFlags.OMIT_KEY_VARTBL_FLAG;
+import static org.apache.ignite.internal.schema.BinaryRow.RowFlags.OMIT_VAL_VARTBL_FLAG;
 import static org.apache.ignite.internal.schema.BinaryRow.VARLEN_COLUMN_OFFSET_FIELD_SIZE;
 import static org.apache.ignite.internal.schema.BinaryRow.VARLEN_TABLE_SIZE_FIELD_SIZE;
 
@@ -69,6 +72,9 @@ public class RowAssembler {
     /** Offset of the varlen table for current chunk. */
     private int varlenTblChunkOff;
 
+    /** Row hashcode. */
+    private int keyHash;
+
     /** Flags. */
     private short flags;
 
@@ -168,6 +174,7 @@ public class RowAssembler {
 
         curCols = schema.keyColumns();
         flags = 0;
+        keyHash = 0;
         strEncoder = null;
 
         initOffsets(BinaryRow.KEY_CHUNK_OFFSET, nonNullVarlenKeyCols);
@@ -183,7 +190,7 @@ public class RowAssembler {
         buf.putShort(0, (short)schema.version());
 
         if (nonNullVarlenKeyCols == 0)
-            flags |= RowFlags.OMIT_KEY_VARTBL_FLAG;
+            flags |= OMIT_KEY_VARTBL_FLAG;
         else
             buf.putShort(varlenTblChunkOff, (short)nonNullVarlenKeyCols);
     }
@@ -200,6 +207,9 @@ public class RowAssembler {
 
         setNull(curCol);
 
+        if (isKeyColumn())
+            keyHash *= 31;
+
         shiftColumn(0, false);
     }
 
@@ -213,6 +223,9 @@ public class RowAssembler {
 
         buf.put(curOff, val);
 
+        if (isKeyColumn())
+            keyHash = 31 * keyHash + Byte.hashCode(val);
+
         shiftColumn(NativeTypes.BYTE);
     }
 
@@ -226,6 +239,9 @@ public class RowAssembler {
 
         buf.putShort(curOff, val);
 
+        if (isKeyColumn())
+            keyHash = 31 * keyHash + Short.hashCode(val);
+
         shiftColumn(NativeTypes.SHORT);
     }
 
@@ -239,6 +255,9 @@ public class RowAssembler {
 
         buf.putInt(curOff, val);
 
+        if (isKeyColumn())
+            keyHash = 31 * keyHash + Integer.hashCode(val);
+
         shiftColumn(NativeTypes.INTEGER);
     }
 
@@ -252,6 +271,9 @@ public class RowAssembler {
 
         buf.putLong(curOff, val);
 
+        if (isKeyColumn())
+            keyHash += 31 * keyHash + Long.hashCode(val);
+
         shiftColumn(NativeTypes.LONG);
     }
 
@@ -265,6 +287,9 @@ public class RowAssembler {
 
         buf.putFloat(curOff, val);
 
+        if (isKeyColumn())
+            keyHash += 31 * keyHash + Float.hashCode(val);
+
         shiftColumn(NativeTypes.FLOAT);
     }
 
@@ -278,6 +303,9 @@ public class RowAssembler {
 
         buf.putDouble(curOff, val);
 
+        if (isKeyColumn())
+            keyHash += 31 * keyHash + Double.hashCode(val);
+
         shiftColumn(NativeTypes.DOUBLE);
     }
 
@@ -292,6 +320,9 @@ public class RowAssembler {
         buf.putLong(curOff, uuid.getLeastSignificantBits());
         buf.putLong(curOff + 8, uuid.getMostSignificantBits());
 
+        if (isKeyColumn())
+            keyHash += 31 * keyHash + uuid.hashCode();
+
         shiftColumn(NativeTypes.UUID);
     }
 
@@ -308,6 +339,9 @@ public class RowAssembler {
 
             writeOffset(curVarlenTblEntry, curOff - baseOff);
 
+            if (isKeyColumn())
+                keyHash += 31 * keyHash + val.hashCode();
+
             shiftColumn(written, true);
         }
         catch (CharacterCodingException e) {
@@ -325,6 +359,9 @@ public class RowAssembler {
 
         buf.putBytes(curOff, val);
 
+        if (isKeyColumn())
+            keyHash += 31 * keyHash + Arrays.hashCode(val);
+
         writeOffset(curVarlenTblEntry, curOff - baseOff);
 
         shiftColumn(val.length, true);
@@ -353,6 +390,9 @@ public class RowAssembler {
         for (int i = 0; i < maskType.sizeInBytes() - arr.length; i++)
             buf.put(curOff + arr.length + i, (byte)0);
 
+        if (isKeyColumn())
+            keyHash += 31 * keyHash + Arrays.hashCode(arr);
+
         shiftColumn(maskType);
     }
 
@@ -370,6 +410,7 @@ public class RowAssembler {
         }
 
         buf.putShort(BinaryRow.FLAGS_FIELD_OFFSET, flags);
+        buf.putInt(BinaryRow.KEY_HASH_FIELD_OFFSET, keyHash);
 
         return buf.toArray();
     }
@@ -391,7 +432,8 @@ public class RowAssembler {
      * @param off Offset to write.
      */
     private void writeOffset(int tblEntryIdx, int off) {
-        assert (flags & (baseOff == BinaryRow.KEY_CHUNK_OFFSET ? RowFlags.OMIT_KEY_VARTBL_FLAG : RowFlags.OMIT_VAL_VARTBL_FLAG)) == 0;
+        assert (flags & (baseOff == BinaryRow.KEY_CHUNK_OFFSET ? OMIT_KEY_VARTBL_FLAG : OMIT_VAL_VARTBL_FLAG)) == 0 :
+            "Illegal writing of varlen when 'omit vartable' flag is set for a chunk.";
 
         buf.putShort(varlenTblChunkOff + Row.varlenItemOffset(tblEntryIdx), (short)off);
     }
@@ -424,7 +466,8 @@ public class RowAssembler {
      * @param colIdx Column index.
      */
     private void setNull(int colIdx) {
-        assert (flags & (baseOff == BinaryRow.KEY_CHUNK_OFFSET ? RowFlags.OMIT_KEY_NULL_MAP_FLAG : RowFlags.OMIT_VAL_NULL_MAP_FLAG)) == 0;
+        assert (flags & (baseOff == BinaryRow.KEY_CHUNK_OFFSET ? RowFlags.OMIT_KEY_NULL_MAP_FLAG : RowFlags.OMIT_VAL_NULL_MAP_FLAG)) == 0 :
+            "Illegal writing 'null' value when 'omit null-map' flag is set for a chunk.";
 
         int byteInMap = colIdx / 8;
         int bitInByte = colIdx % 8;
@@ -472,7 +515,7 @@ public class RowAssembler {
             initOffsets(baseOff + chunkLen, nonNullVarlenValCols);
 
             if (nonNullVarlenValCols == 0)
-                flags |= RowFlags.OMIT_VAL_VARTBL_FLAG;
+                flags |= OMIT_VAL_VARTBL_FLAG;
             else
                 buf.putShort(varlenTblChunkOff, (short)nonNullVarlenValCols);
         }
@@ -493,4 +536,11 @@ public class RowAssembler {
 
         curOff = varlenTblChunkOff + varlenTableChunkSize(nonNullVarlenCols);
     }
+
+    /**
+     * @return {@code true} if current column is a key column, {@code false} otherwise.
+     */
+    private boolean isKeyColumn() {
+        return schema.keyColumns() == curCols;
+    }
 }
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
index 0476f88..0b14363 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaDescriptor.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.internal.util.ArrayUtils;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -43,7 +44,10 @@ public class SchemaDescriptor implements Serializable {
     /** Value columns in serialization order. */
     private final Columns valCols;
 
-    /** Mapping 'Column name' to Column. */
+    /** Affinity columns. */
+    private final Column[] affCols;
+
+    /** Mapping 'Column name' -&gt; Column. */
     private final Map<String, Column> colMap;
 
     /**
@@ -77,7 +81,10 @@ public class SchemaDescriptor implements Serializable {
         Arrays.stream(this.keyCols.columns()).forEach(c -> colMap.put(c.name(), c));
         Arrays.stream(this.valCols.columns()).forEach(c -> colMap.put(c.name(), c));
 
-        //TODO: https://issues.apache.org/jira/browse/IGNITE-14388 Add affinity columns support.
+        // Preserving key chunk column order is not actually required.
+        // It is sufficient to has same column order for all nodes.
+        this.affCols = (ArrayUtils.nullOrEmpty(affCols)) ? keyCols :
+            Arrays.stream(affCols).map(colMap::get).toArray(Column[]::new);
     }
 
     /**
@@ -95,8 +102,8 @@ public class SchemaDescriptor implements Serializable {
     }
 
     /**
-     * @param idx Index to check.
-     * @return {@code true} if the column belongs to the key chunk.
+     * @param idx Column index to check.
+     * @return {@code true} if the column belongs to the key chunk, {@code false} otherwise.
      */
     public boolean isKeyColumn(int idx) {
         return idx < keyCols.length();
@@ -127,6 +134,13 @@ public class SchemaDescriptor implements Serializable {
     }
 
     /**
+     * @return Key affinity columns chunk.
+     */
+    public Column[] affinityColumns() {
+        return affCols;
+    }
+
+    /**
      * @return Value columns chunk.
      */
     public Columns valueColumns() {
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java
index b1ae96a..24994c3 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/SchemaManager.java
@@ -185,6 +185,7 @@ public class SchemaManager extends Producer<SchemaEvent, SchemaEventParameters>
                 return metaStorageMgr.invoke(Conditions.notExists(schemaKey),
                     Operations.put(schemaKey, ByteUtils.toBytes(desc)),
                     Operations.noop())
+                    //TODO: IGNITE-14679 Serialize schema.
                     .thenCompose(res -> metaStorageMgr.invoke(Conditions.notExists(lastVerKey),
                         Operations.put(lastVerKey, ByteUtils.longToBytes(schemaVer)),
                         Operations.noop()));
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/RowAssemblerTest.java b/modules/schema/src/test/java/org/apache/ignite/internal/schema/RowAssemblerTest.java
index 5f042d4..86edaef 100644
--- a/modules/schema/src/test/java/org/apache/ignite/internal/schema/RowAssemblerTest.java
+++ b/modules/schema/src/test/java/org/apache/ignite/internal/schema/RowAssemblerTest.java
@@ -39,6 +39,9 @@ public class RowAssemblerTest {
     /** Table ID test value. */
     public final java.util.UUID tableId = java.util.UUID.randomUUID();
 
+    /**
+     * Validate row layout for schema of fix-len non-null key and fix-len nullable value.
+     */
     @Test
     public void fixedKeyFixedNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyIntCol", INTEGER, false)};
@@ -52,7 +55,7 @@ public class RowAssemblerTest {
             asm.appendInt(33);
             asm.appendInt(-71);
 
-            assertRowBytesEquals(new byte[] {42, 0, 26, 0, 0, 0, 0, 0, 8, 0, 0, 0, 33, 0, 0, 0, 9, 0, 0, 0, 0, -71, -1, -1, -1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 26, 0, 33, 0, 0, 0, 8, 0, 0, 0, 33, 0, 0, 0, 9, 0, 0, 0, 0, -71, -1, -1, -1}, asm.build());
         }
 
         { // Null value.
@@ -61,7 +64,7 @@ public class RowAssemblerTest {
             asm.appendInt(-33);
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 26, 0, 0, 0, 0, 0, 8, 0, 0, 0, -33, -1, -1, -1, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 26, 0, -33, -1, -1, -1, 8, 0, 0, 0, -33, -1, -1, -1, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // No value.
@@ -69,10 +72,13 @@ public class RowAssemblerTest {
 
             asm.appendInt(-33);
 
-            assertRowBytesEquals(new byte[] {42, 0, 27, 0, 0, 0, 0, 0, 8, 0, 0, 0, -33, -1, -1, -1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 27, 0, -33, -1, -1, -1, 8, 0, 0, 0, -33, -1, -1, -1}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of fix-len non-null key and fix-len non-null value.
+     */
     @Test
     public void fixedKeyFixedValue() {
         Column[] keyCols = new Column[] {new Column("keyShortCol", SHORT, false)};
@@ -86,7 +92,7 @@ public class RowAssemblerTest {
             asm.appendShort((short)33);
             asm.appendShort((short)71L);
 
-            assertRowBytesEquals(new byte[] {42, 0, 30, 0, 0, 0, 0, 0, 6, 0, 0, 0, 33, 0, 6, 0, 0, 0, 71, 0}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 30, 0, 33, 0, 0, 0, 6, 0, 0, 0, 33, 0, 6, 0, 0, 0, 71, 0}, asm.build());
         }
 
         { // No value.
@@ -94,10 +100,13 @@ public class RowAssemblerTest {
 
             asm.appendShort((short)-33);
 
-            assertRowBytesEquals(new byte[] {42, 0, 31, 0, 0, 0, 0, 0, 6, 0, 0, 0, -33, -1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 31, 0, -33, -1, -1, -1, 6, 0, 0, 0, -33, -1}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of fix-len non-null key and var-len nullable value.
+     */
     @Test
     public void fixedKeyVarlenNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyShortCol", SHORT, false)};
@@ -111,7 +120,7 @@ public class RowAssemblerTest {
             asm.appendShort((short)-33);
             asm.appendString("val");
 
-            assertRowBytesEquals(new byte[] {42, 0, 10, 0, 0, 0, 0, 0, 6, 0, 0, 0, -33, -1, 12, 0, 0, 0, 0, 1, 0, 9, 0, 118, 97, 108}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 10, 0, -33, -1, -1, -1, 6, 0, 0, 0, -33, -1, 12, 0, 0, 0, 0, 1, 0, 9, 0, 118, 97, 108}, asm.build());
         }
 
         { // Null value.
@@ -120,7 +129,7 @@ public class RowAssemblerTest {
             asm.appendShort((short)33);
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 26, 0, 0, 0, 0, 0, 6, 0, 0, 0, 33, 0, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 26, 0, 33, 0, 0, 0, 6, 0, 0, 0, 33, 0, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // No value.
@@ -128,10 +137,13 @@ public class RowAssemblerTest {
 
             asm.appendShort((short)33);
 
-            assertRowBytesEquals(new byte[] {42, 0, 27, 0, 0, 0, 0, 0, 6, 0, 0, 0, 33, 0}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 27, 0, 33, 0, 0, 0, 6, 0, 0, 0, 33, 0}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of fix-len non-null key and var-len non-null value.
+     */
     @Test
     public void fixedKeyVarlenValue() {
         Column[] keyCols = new Column[] {new Column("keyShortCol", SHORT, false)};
@@ -145,7 +157,7 @@ public class RowAssemblerTest {
             asm.appendShort((short)-33);
             asm.appendString("val");
 
-            assertRowBytesEquals(new byte[] {42, 0, 14, 0, 0, 0, 0, 0, 6, 0, 0, 0, -33, -1, 11, 0, 0, 0, 1, 0, 8, 0, 118, 97, 108}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 14, 0, -33, -1, -1, -1, 6, 0, 0, 0, -33, -1, 11, 0, 0, 0, 1, 0, 8, 0, 118, 97, 108}, asm.build());
         }
 
         { // No value.
@@ -153,10 +165,13 @@ public class RowAssemblerTest {
 
             asm.appendShort((short)33);
 
-            assertRowBytesEquals(new byte[] {42, 0, 31, 0, 0, 0, 0, 0, 6, 0, 0, 0, 33, 0}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 31, 0, 33, 0, 0, 0, 6, 0, 0, 0, 33, 0}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of fix-len nullable key and fix-len non-null value.
+     */
     @Test
     public void fixedNullableKeyFixedValue() {
         Column[] keyCols = new Column[] {new Column("keyShortCol", SHORT, true)};
@@ -170,7 +185,7 @@ public class RowAssemblerTest {
             asm.appendShort((short)-33);
             asm.appendByte((byte)71);
 
-            assertRowBytesEquals(new byte[] {42, 0, 28, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, -33, -1, 5, 0, 0, 0, 71}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 28, 0, -33, -1, -1, -1, 7, 0, 0, 0, 0, -33, -1, 5, 0, 0, 0, 71}, asm.build());
         }
 
         { // Null key.
@@ -187,10 +202,13 @@ public class RowAssemblerTest {
 
             asm.appendShort((short)33);
 
-            assertRowBytesEquals(new byte[] {42, 0, 29, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 33, 0}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 29, 0, 33, 0, 0, 0, 7, 0, 0, 0, 0, 33, 0}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of fix-len nullable key and fix-len nullable value.
+     */
     @Test
     public void fixedNullableKeyFixedNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyShortCol", SHORT, true)};
@@ -204,7 +222,7 @@ public class RowAssemblerTest {
             asm.appendShort((short)-1133);
             asm.appendShort((short)-1071);
 
-            assertRowBytesEquals(new byte[] {42, 0, 24, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, -109, -5, 7, 0, 0, 0, 0, -47, -5}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 24, 0, -109, -5, -1, -1, 7, 0, 0, 0, 0, -109, -5, 7, 0, 0, 0, 0, -47, -5}, asm.build());
         }
 
         { // Null key.
@@ -222,7 +240,7 @@ public class RowAssemblerTest {
             asm.appendShort((short)1133);
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 24, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 109, 4, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 24, 0, 109, 4, 0, 0, 7, 0, 0, 0, 0, 109, 4, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // Null both.
@@ -239,10 +257,13 @@ public class RowAssemblerTest {
 
             asm.appendShort((short)1133);
 
-            assertRowBytesEquals(new byte[] {42, 0, 25, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 109, 4}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 25, 0, 109, 4, 0, 0, 7, 0, 0, 0, 0, 109, 4}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of fix-len nullable key and var-len nullable value.
+     */
     @Test
     public void fixedNullableKeyVarlenNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyIntCol", INTEGER, true)};
@@ -256,7 +277,7 @@ public class RowAssemblerTest {
             asm.appendInt(-33);
             asm.appendString("val");
 
-            assertRowBytesEquals(new byte[] {42, 0, 8, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, -33, -1, -1, -1, 12, 0, 0, 0, 0, 1, 0, 9, 0, 118, 97, 108}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 8, 0, -33, -1, -1, -1, 9, 0, 0, 0, 0, -33, -1, -1, -1, 12, 0, 0, 0, 0, 1, 0, 9, 0, 118, 97, 108}, asm.build());
         }
 
         { // Null key.
@@ -274,7 +295,7 @@ public class RowAssemblerTest {
             asm.appendInt(33);
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 24, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 33, 0, 0, 0, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 24, 0, 33, 0, 0, 0, 9, 0, 0, 0, 0, 33, 0, 0, 0, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // Null both.
@@ -291,10 +312,13 @@ public class RowAssemblerTest {
 
             asm.appendInt(33);
 
-            assertRowBytesEquals(new byte[] {42, 0, 25, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 33, 0, 0, 0}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 25, 0, 33, 0, 0, 0, 9, 0, 0, 0, 0, 33, 0, 0, 0}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of fix-len nullable key and var-len non-null value.
+     */
     @Test
     public void fixedNullableKeyVarlenValue() {
         Column[] keyCols = new Column[] {new Column("keyByteCol", BYTE, true)};
@@ -308,7 +332,7 @@ public class RowAssemblerTest {
             asm.appendByte((byte)-33);
             asm.appendString("val");
 
-            assertRowBytesEquals(new byte[] {42, 0, 12, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, -33, 11, 0, 0, 0, 1, 0, 8, 0, 118, 97, 108}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 12, 0, -33, -1, -1, -1, 6, 0, 0, 0, 0, -33, 11, 0, 0, 0, 1, 0, 8, 0, 118, 97, 108}, asm.build());
         }
 
         { // Null key.
@@ -325,10 +349,13 @@ public class RowAssemblerTest {
 
             asm.appendByte((byte)33);
 
-            assertRowBytesEquals(new byte[] {42, 0, 29, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 33}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 29, 0, 33, 0, 0, 0, 6, 0, 0, 0, 0, 33}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len non-null key and fix-len nullable value.
+     */
     @Test
     public void varlenKeyFixedNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, false)};
@@ -343,7 +370,7 @@ public class RowAssemblerTest {
             asm.appendUuid(uuidVal);
 
             assertRowBytesEquals(new byte[] {
-                42, 0, 18, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121,
+                42, 0, 18, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121,
                 21, 0, 0, 0, 0, -117, -61, -31, 85, 61, -32, 57, 68, 111, 67, 56, -3, -99, -37, -58, -73}, asm.build());
         }
 
@@ -353,7 +380,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 18, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 18, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // No value.
@@ -361,10 +388,13 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 19, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 19, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len non-null key and fix-len non-null value.
+     */
     @Test
     public void varlenKeyFixedValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, false)};
@@ -379,7 +409,7 @@ public class RowAssemblerTest {
             asm.appendUuid(uuidVal);
 
             assertRowBytesEquals(new byte[] {
-                42, 0, 22, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121,
+                42, 0, 22, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121,
                 20, 0, 0, 0, -117, -61, -31, 85, 61, -32, 57, 68, 111, 67, 56, -3, -99, -37, -58, -73}, asm.build());
         }
 
@@ -388,10 +418,13 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 23, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 23, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len non-null key and var-len nullable value.
+     */
     @Test
     public void varlenKeyVarlenNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, false)};
@@ -405,7 +438,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendBytes(new byte[] {-1, 1, 0, 120});
 
-            assertRowBytesEquals(new byte[] {42, 0, 2, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 13, 0, 0, 0, 0, 1, 0, 9, 0, -1, 1, 0, 120}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 2, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 13, 0, 0, 0, 0, 1, 0, 9, 0, -1, 1, 0, 120}, asm.build());
         }
 
         { // Null value.
@@ -414,7 +447,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 18, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 18, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // No value.
@@ -422,10 +455,13 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 19, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 19, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len non-null key and var-len non-null value.
+     */
     @Test
     public void varlenKeyVarlenValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, false)};
@@ -439,7 +475,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendBytes(new byte[] {-1, 1, 0, 120});
 
-            assertRowBytesEquals(new byte[] {42, 0, 6, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 12, 0, 0, 0, 1, 0, 8, 0, -1, 1, 0, 120}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 6, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121, 12, 0, 0, 0, 1, 0, 8, 0, -1, 1, 0, 120}, asm.build());
         }
 
         { // No value.
@@ -447,10 +483,13 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 23, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 23, 0, 95, -98, 1, 0, 11, 0, 0, 0, 1, 0, 8, 0, 107, 101, 121}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len nullable key and fix-len nullable value.
+     */
     @Test
     public void varlenNullableKeyFixedNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, true)};
@@ -464,7 +503,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendShort((short)-71);
 
-            assertRowBytesEquals(new byte[] {42, 0, 16, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 7, 0, 0, 0, 0, -71, -1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 16, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 7, 0, 0, 0, 0, -71, -1}, asm.build());
         }
 
         { // Null key.
@@ -482,7 +521,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 16, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 16, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // Null both.
@@ -499,10 +538,13 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 17, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 17, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len nullable key and fix-len non-null value.
+     */
     @Test
     public void varlenNullableKeyFixedValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, true)};
@@ -516,7 +558,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendShort((short)-71L);
 
-            assertRowBytesEquals(new byte[] {42, 0, 20, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 6, 0, 0, 0, -71, -1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 20, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 6, 0, 0, 0, -71, -1}, asm.build());
         }
 
         { // Null key.
@@ -533,10 +575,13 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 21, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 21, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len nullable key and var-len nullable value.
+     */
     @Test
     public void varlenNullableKeyVarlenNullableValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, true)};
@@ -550,7 +595,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendBytes(new byte[] {-1, 1, 0, 120});
 
-            assertRowBytesEquals(new byte[] {42, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 13, 0, 0, 0, 0, 1, 0, 9, 0, -1, 1, 0, 120}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 0, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 13, 0, 0, 0, 0, 1, 0, 9, 0, -1, 1, 0, 120}, asm.build());
         }
 
         { // Null key.
@@ -568,7 +613,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendNull();
 
-            assertRowBytesEquals(new byte[] {42, 0, 16, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 16, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 5, 0, 0, 0, 1}, asm.build());
         }
 
         { // Null both.
@@ -585,10 +630,13 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 17, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 17, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
         }
     }
 
+    /**
+     * Validate row layout for schema of var-len nullable key and var-len non-null value.
+     */
     @Test
     public void varlenNullableKeyVarlenValue() {
         Column[] keyCols = new Column[] {new Column("keyStrCol", STRING, true)};
@@ -602,7 +650,7 @@ public class RowAssemblerTest {
             asm.appendString("key");
             asm.appendBytes(new byte[] {-1, 1, 0, 120});
 
-            assertRowBytesEquals(new byte[] {42, 0, 4, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 12, 0, 0, 0, 1, 0, 8, 0, -1, 1, 0, 120}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 4, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121, 12, 0, 0, 0, 1, 0, 8, 0, -1, 1, 0, 120}, asm.build());
         }
 
         { // Null key.
@@ -619,7 +667,64 @@ public class RowAssemblerTest {
 
             asm.appendString("key");
 
-            assertRowBytesEquals(new byte[] {42, 0, 21, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
+            assertRowBytesEquals(new byte[] {42, 0, 21, 0, 95, -98, 1, 0, 12, 0, 0, 0, 0, 1, 0, 9, 0, 107, 101, 121}, asm.build());
+        }
+    }
+
+    /**
+     * Validate row layout for key\value columns of different types.
+     */
+    @Test
+    public void mixedTypes() {
+        Column[] keyCols = new Column[] {
+            new Column("keyShortCol", SHORT, false),
+            new Column("keyStrCol", STRING, false)
+        };
+        Column[] valCols = new Column[] {
+            new Column("valIntCol", INTEGER, true),
+            new Column("valStrCol", STRING, true)
+        };
+
+        SchemaDescriptor schema = new SchemaDescriptor(tableId,42, keyCols, valCols);
+
+        {
+            RowAssembler asm = new RowAssembler(schema, 0, 1, 1);
+
+            asm.appendShort((short)33);
+            asm.appendString("keystr");
+            asm.appendInt(73);
+            asm.appendString("valstr");
+
+            assertRowBytesEquals(new byte[] {
+                42, 0, 2, 0, -110, -109, 94, -68,
+                16, 0, 0, 0, 1, 0, 10, 0, 33, 0, 107, 101, 121, 115, 116, 114,
+                19, 0, 0, 0, 0, 1, 0, 13, 0, 73, 0, 0, 0, 118, 97, 108, 115, 116, 114}, asm.build());
+        }
+
+        { // Null value.
+            RowAssembler asm = new RowAssembler(schema, 0, 1, 0);
+
+            asm.appendShort((short)33);
+            asm.appendString("keystr2");
+            asm.appendNull();
+            asm.appendNull();
+
+            assertRowBytesEquals(new byte[] {
+                42, 0, 18, 0, 32, 99, 115, -49,
+                17, 0, 0, 0, 1, 0, 10, 0, 33, 0, 107, 101, 121, 115, 116, 114, 50,
+                5, 0, 0, 0, 3}, asm.build());
+        }
+
+        { // No value.
+            RowAssembler asm = new RowAssembler(schema, 0, 1, 0);
+
+            asm.appendShort((short)33);
+            asm.appendString("keystr");
+
+            assertRowBytesEquals(new byte[] {
+                42, 0, 19, 0, -110, -109, 94, -68,
+                16, 0, 0, 0, 1, 0, 10, 0, 33, 0, 107, 101, 121, 115, 116, 114}, asm.build());
+
         }
     }