You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by jo...@apache.org on 2025/03/19 12:23:52 UTC

(ignite-3) branch main updated: IGNITE-24698 Add new catalog serializers that conform with the new serialization protocol (#5373)

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

jooger 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 688662c9d8f IGNITE-24698 Add new catalog serializers that conform with the new serialization protocol (#5373)
688662c9d8f is described below

commit 688662c9d8f2477d55f18843409fd73f1962fd67
Author: Max Zhuravkov <sh...@gmail.com>
AuthorDate: Wed Mar 19 14:23:45 2025 +0200

    IGNITE-24698 Add new catalog serializers that conform with the new serialization protocol (#5373)
---
 .../CatalogHashIndexDescriptorSerializers.java     |  39 ++-
 .../CatalogSchemaDescriptorSerializers.java        |  43 ++-
 .../CatalogSortedIndexDescriptorSerializers.java   |  54 +++-
 ...CatalogStorageProfileDescriptorSerializers.java |  26 +-
 ...atalogStorageProfilesDescriptorSerializers.java |  26 +-
 .../CatalogSystemViewDescriptorSerializers.java    |  40 ++-
 .../CatalogTableColumnDescriptorSerializers.java   |  41 ++-
 .../CatalogTableDescriptorSerializers.java         | 141 +++++++++-
 .../CatalogTableSchemaVersionsSerializers.java     |  31 ++-
 .../CatalogTableVersionSerializers.java            |  27 +-
 .../CatalogZoneDescriptorSerializers.java          |  62 ++++-
 .../storage/AlterColumnEntrySerializers.java       |  28 +-
 .../catalog/storage/AlterZoneEntrySerializers.java |  26 +-
 .../storage/DropColumnsEntrySerializers.java       |  30 ++-
 .../catalog/storage/DropIndexEntrySerializers.java |  26 +-
 .../catalog/storage/DropSchemaSerializers.java     |  26 +-
 .../catalog/storage/DropTableEntrySerializers.java |  26 +-
 .../catalog/storage/DropZoneEntrySerializers.java  |  26 +-
 .../MakeIndexAvailableEntrySerializers.java        |  26 +-
 .../storage/NewColumnsEntrySerializers.java        |  28 +-
 .../catalog/storage/NewIndexEntrySerializers.java  |  26 +-
 .../catalog/storage/NewSchemaEntrySerializers.java |  25 +-
 .../storage/NewSystemViewEntrySerializers.java     |  26 +-
 .../catalog/storage/NewTableEntrySerializers.java  |  26 +-
 .../catalog/storage/NewZoneEntrySerializers.java   |  26 +-
 .../storage/ObjectIdGenUpdateEntrySerializers.java |  26 +-
 .../storage/RemoveIndexEntrySerializers.java       |  26 +-
 .../storage/RenameIndexEntrySerializers.java       |  29 +-
 .../storage/RenameTableEntrySerializers.java       |  28 +-
 .../storage/SetDefaultZoneEntrySerializers.java    |  26 +-
 .../catalog/storage/SnapshotEntrySerializers.java  |  56 +++-
 .../StartBuildingIndexEntrySerializers.java        |  26 +-
 .../internal/catalog/storage/UpdateLogImpl.java    |   2 +-
 .../storage/VersionedUpdateSerializers.java        |  31 ++-
 .../CatalogEntrySerializerProviderImpl.java        |   3 +
 .../serialization/CatalogMarshallerException.java  |   4 +
 .../serialization/CatalogObjectDataInput.java      | 146 ++++++++++
 .../serialization/CatalogObjectDataOutput.java     | 134 ++++++++++
 .../serialization/CatalogObjectSerializer.java     |   6 +-
 .../serialization/CatalogSerializationUtils.java   |  18 +-
 .../CatalogVersionAwareSerializer.java             |   6 +-
 .../serialization/UpdateLogMarshallerImpl.java     |  47 ++--
 .../storage/CatalogEntrySerializationTest.java     |  23 +-
 .../CatalogSerializationCompatibilityTest.java     | 297 ++++++++-------------
 ...ogSerializationCompatibilityV1ReadsV1Test.java} |  27 +-
 ...ogSerializationCompatibilityV2ReadsV1Test.java} |  32 ++-
 ...ogSerializationCompatibilityV2ReadsV2Test.java} |  32 ++-
 .../catalog/storage/UpdateLogImplTest.java         |  14 +-
 .../serialization/CatalogDataInputOutputTest.java  | 213 +++++++++++++++
 .../CatalogEntrySerializerProviderImplTest.java    |   6 +-
 .../serialization_v2/AlterColumnsEntry_2.bin       | Bin 0 -> 104 bytes
 .../serialization_v2/AlterZoneEntry_2.bin          | Bin 0 -> 114 bytes
 .../serialization_v2/DropColumnsEntry_2.bin        | Bin 0 -> 35 bytes
 .../serialization_v2/DropIndexEntry_2.bin          | Bin 0 -> 24 bytes
 .../serialization_v2/DropSchemaEntry_2.bin         | Bin 0 -> 24 bytes
 .../serialization_v2/DropTableEntry_2.bin          | Bin 0 -> 24 bytes
 .../resources/serialization_v2/DropZoneEntry_2.bin | Bin 0 -> 24 bytes
 .../serialization_v2/MakeIndexAvailableEntry_2.bin | Bin 0 -> 24 bytes
 .../serialization_v2/NewColumnsEntry_2.bin         | Bin 0 -> 7608 bytes
 .../resources/serialization_v2/NewIndexEntry_2.bin | Bin 0 -> 1302 bytes
 .../serialization_v2/NewSchemaEntry_2.bin          | Bin 0 -> 165269 bytes
 .../serialization_v2/NewSystemViewEntry_2.bin      | Bin 0 -> 7652 bytes
 .../resources/serialization_v2/NewTableEntry_2.bin | Bin 0 -> 51763 bytes
 .../resources/serialization_v2/NewZoneEntry_2.bin  | Bin 0 -> 153 bytes
 .../serialization_v2/ObjectIdGenUpdateEntry_2.bin  | Bin 0 -> 19 bytes
 .../serialization_v2/RemoveIndexEntry_2.bin        | Bin 0 -> 24 bytes
 .../serialization_v2/RenameIndexEntry_2.bin        | Bin 0 -> 25 bytes
 .../serialization_v2/RenameTableEntry_2.bin        | Bin 0 -> 44 bytes
 .../serialization_v2/SetDefaultZoneEntry_2.bin     | Bin 0 -> 24 bytes
 .../SnapshotEntryNoDefaultZone_2.bin               | Bin 0 -> 165391 bytes
 .../resources/serialization_v2/SnapshotEntry_2.bin | Bin 0 -> 165394 bytes
 .../serialization_v2/StartBuildingIndexEntry_2.bin | Bin 0 -> 24 bytes
 72 files changed, 1752 insertions(+), 407 deletions(-)

diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogHashIndexDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogHashIndexDescriptorSerializers.java
index 44f617db8c5..61da2295a3a 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogHashIndexDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogHashIndexDescriptorSerializers.java
@@ -23,10 +23,12 @@ import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSe
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataInput;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataOutput;
 
 /**
  * Serializers for {@link CatalogHashIndexDescriptor}.
@@ -38,7 +40,7 @@ public class CatalogHashIndexDescriptorSerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class HashIndexDescriptorSerializerV1 implements CatalogObjectSerializer<CatalogHashIndexDescriptor> {
         @Override
-        public CatalogHashIndexDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogHashIndexDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             int id = input.readVarIntAsInt();
             String name = input.readUTF();
             long updateToken = input.readVarInt();
@@ -52,7 +54,7 @@ public class CatalogHashIndexDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogHashIndexDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogHashIndexDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(descriptor.id());
             output.writeUTF(descriptor.name());
             output.writeVarInt(descriptor.updateToken());
@@ -63,4 +65,33 @@ public class CatalogHashIndexDescriptorSerializers {
             writeStringCollection(descriptor.columns(), output);
         }
     }
+
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class HashIndexDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogHashIndexDescriptor> {
+        @Override
+        public CatalogHashIndexDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            int id = input.readVarIntAsInt();
+            String name = input.readUTF();
+            long updateToken = input.readVarInt();
+            int tableId = input.readVarIntAsInt();
+            boolean unique = input.readBoolean();
+            CatalogIndexStatus status = CatalogIndexStatus.forId(input.readByte());
+            boolean isCreatedWithTable = input.readBoolean();
+            List<String> columns = input.readObjectCollection(IgniteUnsafeDataInput::readUTF, ArrayList::new);
+
+            return new CatalogHashIndexDescriptor(id, name, tableId, unique, status, columns, updateToken, isCreatedWithTable);
+        }
+
+        @Override
+        public void writeTo(CatalogHashIndexDescriptor value, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(value.id());
+            output.writeUTF(value.name());
+            output.writeVarInt(value.updateToken());
+            output.writeVarInt(value.tableId());
+            output.writeBoolean(value.unique());
+            output.writeByte(value.status().id());
+            output.writeBoolean(value.isCreatedWithTable());
+            output.writeObjectCollection(IgniteUnsafeDataOutput::writeUTF, value.columns());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptorSerializers.java
index ac7ae31c21d..8aea4480d34 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptorSerializers.java
@@ -21,13 +21,15 @@ import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSe
 import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.writeArray;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.IndexDescriptorSerializerHelper;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogSchemaDescriptor}.
@@ -47,7 +49,7 @@ public class CatalogSchemaDescriptorSerializers {
         }
 
         @Override
-        public CatalogSchemaDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogSchemaDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             CatalogObjectSerializer<CatalogTableDescriptor> tableDescriptorSerializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE.id());
             CatalogObjectSerializer<CatalogSystemViewDescriptor> viewDescriptorSerializer =
@@ -65,7 +67,7 @@ public class CatalogSchemaDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogSchemaDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogSchemaDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             CatalogObjectSerializer<CatalogTableDescriptor> tableDescriptorSerializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE.id());
             CatalogObjectSerializer<CatalogSystemViewDescriptor> viewDescriptorSerializer =
@@ -80,4 +82,37 @@ public class CatalogSchemaDescriptorSerializers {
             writeArray(descriptor.systemViews(), viewDescriptorSerializer, output);
         }
     }
+
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class SchemaDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogSchemaDescriptor> {
+
+        @Override
+        public CatalogSchemaDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            int id = input.readVarIntAsInt();
+            String name = input.readUTF();
+            long updateToken = input.readVarInt();
+
+            List<CatalogTableDescriptor> tables = input.readCompactEntryList(CatalogTableDescriptor.class);
+            List<CatalogIndexDescriptor> indexes = input.readEntryList(CatalogIndexDescriptor.class);
+            List<CatalogSystemViewDescriptor> systemViews = input.readCompactEntryList(CatalogSystemViewDescriptor.class);
+
+            return new CatalogSchemaDescriptor(id, name,
+                    tables.toArray(new CatalogTableDescriptor[0]),
+                    indexes.toArray(new CatalogIndexDescriptor[0]),
+                    systemViews.toArray(new CatalogSystemViewDescriptor[0]),
+                    updateToken
+            );
+        }
+
+        @Override
+        public void writeTo(CatalogSchemaDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(descriptor.id());
+            output.writeUTF(descriptor.name());
+            output.writeVarInt(descriptor.updateToken());
+
+            output.writeCompactEntryList(Arrays.asList(descriptor.tables()));
+            output.writeEntryList(Arrays.asList(descriptor.indexes()));
+            output.writeCompactEntryList(Arrays.asList(descriptor.systemViews()));
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSortedIndexDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSortedIndexDescriptorSerializers.java
index 7380a9e7893..c2db4ef63a0 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSortedIndexDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSortedIndexDescriptorSerializers.java
@@ -21,12 +21,13 @@ import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSe
 import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.writeList;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogSortedIndexDescriptor}.
@@ -46,7 +47,7 @@ public class CatalogSortedIndexDescriptorSerializers {
         }
 
         @Override
-        public CatalogSortedIndexDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogSortedIndexDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             int id = input.readVarIntAsInt();
             String name = input.readUTF();
             long updateToken = input.readVarInt();
@@ -60,7 +61,7 @@ public class CatalogSortedIndexDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogSortedIndexDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogSortedIndexDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(descriptor.id());
             output.writeUTF(descriptor.name());
             output.writeVarInt(descriptor.updateToken());
@@ -73,7 +74,7 @@ public class CatalogSortedIndexDescriptorSerializers {
 
         private static class IndexColumnDescriptorSerializerV1 implements CatalogObjectSerializer<CatalogIndexColumnDescriptor> {
             @Override
-            public CatalogIndexColumnDescriptor readFrom(IgniteDataInput input) throws IOException {
+            public CatalogIndexColumnDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
                 String name = input.readUTF();
                 CatalogColumnCollation collation = CatalogColumnCollation.unpack(input.readByte());
 
@@ -81,10 +82,51 @@ public class CatalogSortedIndexDescriptorSerializers {
             }
 
             @Override
-            public void writeTo(CatalogIndexColumnDescriptor descriptor, IgniteDataOutput output) throws IOException {
+            public void writeTo(CatalogIndexColumnDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
                 output.writeUTF(descriptor.name());
                 output.writeByte(CatalogColumnCollation.pack(descriptor.collation()));
             }
         }
     }
+
+    /**
+     * Serializer for {@link CatalogSortedIndexDescriptor}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class SortedIndexDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogSortedIndexDescriptor> {
+        @Override
+        public CatalogSortedIndexDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            int id = input.readVarIntAsInt();
+            String name = input.readUTF();
+            long updateToken = input.readVarInt();
+            int tableId = input.readVarIntAsInt();
+            boolean unique = input.readBoolean();
+            CatalogIndexStatus status = CatalogIndexStatus.forId(input.readByte());
+            boolean isCreatedWithTable = input.readBoolean();
+
+            List<CatalogIndexColumnDescriptor> columns = input.readObjectCollection(in -> {
+                String columnName = input.readUTF();
+                CatalogColumnCollation collation = CatalogColumnCollation.unpack(input.readByte());
+                return new CatalogIndexColumnDescriptor(columnName, collation);
+            }, ArrayList::new);
+
+            return new CatalogSortedIndexDescriptor(id, name, tableId, unique, status, columns, updateToken, isCreatedWithTable);
+        }
+
+        @Override
+        public void writeTo(CatalogSortedIndexDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(descriptor.id());
+            output.writeUTF(descriptor.name());
+            output.writeVarInt(descriptor.updateToken());
+            output.writeVarInt(descriptor.tableId());
+            output.writeBoolean(descriptor.unique());
+            output.writeByte(descriptor.status().id());
+            output.writeBoolean(descriptor.isCreatedWithTable());
+
+            output.writeObjectCollection((out, elem) -> {
+                output.writeUTF(elem.name());
+                output.writeByte(CatalogColumnCollation.pack(elem.collation()));
+            }, descriptor.columns());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfileDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfileDescriptorSerializers.java
index a1b4dda8ffa..4ac33ec0f0f 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfileDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfileDescriptorSerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.descriptors;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogStorageProfilesDescriptor}.
@@ -33,14 +33,32 @@ public class CatalogStorageProfileDescriptorSerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class StorageProfileDescriptorSerializerV1 implements CatalogObjectSerializer<CatalogStorageProfileDescriptor> {
         @Override
-        public CatalogStorageProfileDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogStorageProfileDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             String storageProfileDescriptor = input.readUTF();
 
             return new CatalogStorageProfileDescriptor(storageProfileDescriptor);
         }
 
         @Override
-        public void writeTo(CatalogStorageProfileDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogStorageProfileDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeUTF(descriptor.storageProfile());
+        }
+    }
+
+    /**
+     * Serializer for {@link CatalogStorageProfilesDescriptor}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class StorageProfileDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogStorageProfileDescriptor> {
+        @Override
+        public CatalogStorageProfileDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            String storageProfileDescriptor = input.readUTF();
+
+            return new CatalogStorageProfileDescriptor(storageProfileDescriptor);
+        }
+
+        @Override
+        public void writeTo(CatalogStorageProfileDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             output.writeUTF(descriptor.storageProfile());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfilesDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfilesDescriptorSerializers.java
index c6fb1ae3330..c69fa3c6eab 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfilesDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogStorageProfilesDescriptorSerializers.java
@@ -23,11 +23,11 @@ import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSe
 import java.io.IOException;
 import java.util.List;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogStorageProfilesDescriptor}.
@@ -45,7 +45,7 @@ public class CatalogStorageProfilesDescriptorSerializers {
         }
 
         @Override
-        public CatalogStorageProfilesDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogStorageProfilesDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             CatalogObjectSerializer<CatalogStorageProfileDescriptor> profileSerializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_STORAGE_PROFILE.id());
 
@@ -55,11 +55,29 @@ public class CatalogStorageProfilesDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogStorageProfilesDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogStorageProfilesDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             CatalogObjectSerializer<CatalogStorageProfileDescriptor> profileSerializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_STORAGE_PROFILE.id());
 
             writeList(descriptor.profiles(), profileSerializer, output);
         }
     }
+
+    /**
+     * Serializer for {@link CatalogStorageProfilesDescriptor}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class StorageProfilesDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogStorageProfilesDescriptor> {
+        @Override
+        public CatalogStorageProfilesDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            List<CatalogStorageProfileDescriptor> storageProfileDescriptors = input.readEntryList(CatalogStorageProfileDescriptor.class);
+
+            return new CatalogStorageProfilesDescriptor(storageProfileDescriptors);
+        }
+
+        @Override
+        public void writeTo(CatalogStorageProfilesDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntryList(descriptor.profiles());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSystemViewDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSystemViewDescriptorSerializers.java
index fc72d7d6fcc..8b3c40a97f7 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSystemViewDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSystemViewDescriptorSerializers.java
@@ -24,11 +24,11 @@ import java.io.IOException;
 import java.util.List;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSystemViewDescriptor.SystemViewType;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogSystemViewDescriptor}.
@@ -46,7 +46,7 @@ public class CatalogSystemViewDescriptorSerializers {
         }
 
         @Override
-        public CatalogSystemViewDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogSystemViewDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             CatalogObjectSerializer<CatalogTableColumnDescriptor> columnSerializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_COLUMN.id());
 
@@ -64,7 +64,7 @@ public class CatalogSystemViewDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogSystemViewDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogSystemViewDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             CatalogObjectSerializer<CatalogTableColumnDescriptor> columnSerializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_COLUMN.id());
 
@@ -76,4 +76,36 @@ public class CatalogSystemViewDescriptorSerializers {
             output.writeByte(descriptor.systemViewType().id());
         }
     }
+
+    /**
+     * Serializer for {@link CatalogSystemViewDescriptor}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class SystemViewDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogSystemViewDescriptor> {
+
+        @Override
+        public CatalogSystemViewDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            int id = input.readVarIntAsInt();
+            int schemaId = input.readVarIntAsInt();
+            String name = input.readUTF();
+            long updateToken = input.readVarInt();
+
+            List<CatalogTableColumnDescriptor> columns = input.readEntryList(CatalogTableColumnDescriptor.class);
+
+            byte sysViewTypeId = input.readByte();
+            SystemViewType sysViewType = SystemViewType.forId(sysViewTypeId);
+
+            return new CatalogSystemViewDescriptor(id, schemaId, name, columns, sysViewType, updateToken);
+        }
+
+        @Override
+        public void writeTo(CatalogSystemViewDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(descriptor.id());
+            output.writeVarInt(descriptor.schemaId());
+            output.writeUTF(descriptor.name());
+            output.writeVarInt(descriptor.updateToken());
+            output.writeEntryList(descriptor.columns());
+            output.writeByte(descriptor.systemViewType().id());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptorSerializers.java
index 6807a3f7b64..ebc0765cbf4 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableColumnDescriptorSerializers.java
@@ -19,10 +19,10 @@ package org.apache.ignite.internal.catalog.descriptors;
 
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.commands.DefaultValue;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.apache.ignite.sql.ColumnType;
 
 /**
@@ -35,7 +35,7 @@ public class CatalogTableColumnDescriptorSerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class TableColumnDescriptorSerializerV1 implements CatalogObjectSerializer<CatalogTableColumnDescriptor> {
         @Override
-        public CatalogTableColumnDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogTableColumnDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             String name = input.readUTF();
             int typeId = input.readVarIntAsInt();
             ColumnType type = ColumnType.getById(typeId);
@@ -53,7 +53,40 @@ public class CatalogTableColumnDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogTableColumnDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogTableColumnDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeUTF(descriptor.name());
+            output.writeVarInt(descriptor.type().id());
+            output.writeBoolean(descriptor.nullable());
+            output.writeVarInt(descriptor.precision());
+            output.writeVarInt(descriptor.scale());
+            output.writeVarInt(descriptor.length());
+
+            DefaultValue.writeTo(descriptor.defaultValue(), output);
+        }
+    }
+
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class TableColumnDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogTableColumnDescriptor> {
+        @Override
+        public CatalogTableColumnDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            String name = input.readUTF();
+            int typeId = input.readVarIntAsInt();
+            ColumnType type = ColumnType.getById(typeId);
+
+            assert type != null : "Unknown column type: " + typeId;
+
+            boolean nullable = input.readBoolean();
+            int precision = input.readVarIntAsInt();
+            int scale = input.readVarIntAsInt();
+            int length = input.readVarIntAsInt();
+
+            DefaultValue defaultValue = DefaultValue.readFrom(input);
+
+            return new CatalogTableColumnDescriptor(name, type, nullable, precision, scale, length, defaultValue);
+        }
+
+        @Override
+        public void writeTo(CatalogTableColumnDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             output.writeUTF(descriptor.name());
             output.writeVarInt(descriptor.type().id());
             output.writeBoolean(descriptor.nullable());
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
index 50a059cda13..a8988fc69d9 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableDescriptorSerializers.java
@@ -24,11 +24,11 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogTableDescriptor}.
@@ -46,7 +46,7 @@ public class CatalogTableDescriptorSerializers {
         }
 
         @Override
-        public CatalogTableDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogTableDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             int id = input.readVarIntAsInt();
             String name = input.readUTF();
             long updateToken = input.readVarInt();
@@ -104,7 +104,7 @@ public class CatalogTableDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogTableDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogTableDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(descriptor.id());
             output.writeUTF(descriptor.name());
             output.writeVarInt(descriptor.updateToken());
@@ -181,4 +181,137 @@ public class CatalogTableDescriptorSerializers {
             return pkColumnIndexes;
         }
     }
+
+    /**
+     * Serializer for {@link CatalogTableDescriptor}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class TableDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogTableDescriptor> {
+        @Override
+        public CatalogTableDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            int id = input.readVarIntAsInt();
+            String name = input.readUTF();
+            long updateToken = input.readVarInt();
+
+            CatalogTableSchemaVersions schemaVersions =  input.readEntry(CatalogTableSchemaVersions.class);
+            List<CatalogTableColumnDescriptor> columns = input.readEntryList(CatalogTableColumnDescriptor.class);
+            String storageProfile = input.readUTF();
+
+            int schemaId = input.readVarIntAsInt();
+            int pkIndexId = input.readVarIntAsInt();
+            int zoneId = input.readVarIntAsInt();
+
+            int pkKeysLen = input.readVarIntAsInt();
+            int[] pkColumnIndexes = input.readIntArray(pkKeysLen);
+            List<String> primaryKeyColumns = new ArrayList<>(pkColumnIndexes.length);
+
+            for (int idx : pkColumnIndexes) {
+                primaryKeyColumns.add(columns.get(idx).name());
+            }
+
+            int colocationColumnsLen = input.readVarIntAsInt();
+
+            List<String> colocationColumns;
+
+            if (colocationColumnsLen == -1) {
+                colocationColumns = primaryKeyColumns;
+            } else {
+                colocationColumns = new ArrayList<>(colocationColumnsLen);
+
+                int[] colocationColumnIdxs = input.readIntArray(colocationColumnsLen);
+
+                for (int idx : colocationColumnIdxs) {
+                    colocationColumns.add(columns.get(idx).name());
+                }
+            }
+
+            return new CatalogTableDescriptor(
+                    id,
+                    schemaId,
+                    pkIndexId,
+                    name,
+                    zoneId,
+                    columns,
+                    primaryKeyColumns,
+                    colocationColumns,
+                    schemaVersions,
+                    storageProfile,
+                    updateToken
+            );
+        }
+
+        @Override
+        public void writeTo(CatalogTableDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(descriptor.id());
+            output.writeUTF(descriptor.name());
+            output.writeVarInt(descriptor.updateToken());
+
+            output.writeEntry(descriptor.schemaVersions());
+            output.writeEntryList(descriptor.columns());
+            output.writeUTF(descriptor.storageProfile());
+
+            output.writeVarInt(descriptor.schemaId());
+            output.writeVarInt(descriptor.primaryKeyIndexId());
+            output.writeVarInt(descriptor.zoneId());
+
+            int[] pkIndexes = resolvePkColumnIndexes(descriptor);
+
+            output.writeVarInt(pkIndexes.length);
+            output.writeIntArray(pkIndexes);
+
+            if (descriptor.colocationColumns() == descriptor.primaryKeyColumns()) {
+                output.writeVarInt(-1);
+            } else {
+                int[] colocationIndexes = resolveColocationColumnIndexes(pkIndexes, descriptor);
+
+                output.writeVarInt(colocationIndexes.length);
+                output.writeIntArray(colocationIndexes);
+            }
+        }
+
+        private static int[] resolveColocationColumnIndexes(int[] pkColumnIndexes, CatalogTableDescriptor descriptor) {
+            int[] colocationColumnIndexes = new int[descriptor.colocationColumns().size()];
+
+            for (int idx : pkColumnIndexes) {
+                String columnName = descriptor.columns().get(idx).name();
+
+                for (int j = 0; j < descriptor.colocationColumns().size(); j++) {
+                    if (descriptor.colocationColumns().get(j).equals(columnName)) {
+                        colocationColumnIndexes[j] = idx;
+
+                        break;
+                    }
+                }
+            }
+
+            return colocationColumnIndexes;
+        }
+
+        private static int[] resolvePkColumnIndexes(CatalogTableDescriptor descriptor) {
+            List<CatalogTableColumnDescriptor> columns = descriptor.columns();
+            List<String> pkColumns = descriptor.primaryKeyColumns();
+
+            assert columns.size() >= pkColumns.size();
+
+            int[] pkColumnIndexes = new int[pkColumns.size()];
+            int foundCount = 0;
+
+            for (int i = 0; i < columns.size() && foundCount < pkColumnIndexes.length; i++) {
+                for (int j = 0; j < pkColumns.size(); j++) {
+                    String pkColumn = pkColumns.get(j);
+
+                    if (pkColumn.equals(columns.get(i).name())) {
+                        pkColumnIndexes[j] = i;
+                        foundCount++;
+
+                        break;
+                    }
+                }
+            }
+
+            assert foundCount == pkColumnIndexes.length;
+
+            return pkColumnIndexes;
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableSchemaVersionsSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableSchemaVersionsSerializers.java
index 0754a227180..4812d7ae0be 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableSchemaVersionsSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableSchemaVersionsSerializers.java
@@ -21,13 +21,15 @@ import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSe
 import static org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.writeArray;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableSchemaVersions.TableVersion;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogTableSchemaVersions}.
@@ -45,7 +47,7 @@ public class CatalogTableSchemaVersionsSerializers {
         }
 
         @Override
-        public CatalogTableSchemaVersions readFrom(IgniteDataInput input) throws IOException {
+        public CatalogTableSchemaVersions readFrom(CatalogObjectDataInput input) throws IOException {
             CatalogObjectSerializer<TableVersion> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_VERSION.id());
 
@@ -56,7 +58,7 @@ public class CatalogTableSchemaVersionsSerializers {
         }
 
         @Override
-        public void writeTo(CatalogTableSchemaVersions tabVersions, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogTableSchemaVersions tabVersions, CatalogObjectDataOutput output) throws IOException {
             CatalogObjectSerializer<TableVersion> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_VERSION.id());
 
@@ -64,4 +66,25 @@ public class CatalogTableSchemaVersionsSerializers {
             output.writeVarInt(tabVersions.earliestVersion());
         }
     }
+
+    /**
+     * Serializer for {@link CatalogTableSchemaVersions}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class TableSchemaVersionsSerializerV2 implements CatalogObjectSerializer<CatalogTableSchemaVersions> {
+
+        @Override
+        public CatalogTableSchemaVersions readFrom(CatalogObjectDataInput input) throws IOException {
+            List<TableVersion> versions = input.readCompactEntryList(TableVersion.class);
+            int base = input.readVarIntAsInt();
+
+            return new CatalogTableSchemaVersions(base, versions.toArray(new TableVersion[0]));
+        }
+
+        @Override
+        public void writeTo(CatalogTableSchemaVersions tabVersions, CatalogObjectDataOutput output) throws IOException {
+            output.writeCompactEntryList(Arrays.asList(tabVersions.versions()));
+            output.writeVarInt(tabVersions.earliestVersion());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableVersionSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableVersionSerializers.java
index de24bbeea7d..fd8661b38e2 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableVersionSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogTableVersionSerializers.java
@@ -24,11 +24,11 @@ import java.io.IOException;
 import java.util.List;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableSchemaVersions.TableVersion;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link TableVersion}.
@@ -46,7 +46,7 @@ public class CatalogTableVersionSerializers {
         }
 
         @Override
-        public TableVersion readFrom(IgniteDataInput input) throws IOException {
+        public TableVersion readFrom(CatalogObjectDataInput input) throws IOException {
             CatalogObjectSerializer<CatalogTableColumnDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_COLUMN.id());
 
@@ -56,11 +56,30 @@ public class CatalogTableVersionSerializers {
         }
 
         @Override
-        public void writeTo(TableVersion tableVersion, IgniteDataOutput output) throws IOException {
+        public void writeTo(TableVersion tableVersion, CatalogObjectDataOutput output) throws IOException {
             CatalogObjectSerializer<CatalogTableColumnDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_COLUMN.id());
 
             writeList(tableVersion.columns(), serializer, output);
         }
     }
+
+    /**
+     * Serializer for {@link TableVersion}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class TableVersionSerializerV2 implements CatalogObjectSerializer<TableVersion> {
+
+        @Override
+        public TableVersion readFrom(CatalogObjectDataInput input) throws IOException {
+            List<CatalogTableColumnDescriptor> columns = input.readEntryList(CatalogTableColumnDescriptor.class);
+
+            return new TableVersion(columns);
+        }
+
+        @Override
+        public void writeTo(TableVersion tableVersion, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntryList(tableVersion.columns());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogZoneDescriptorSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogZoneDescriptorSerializers.java
index 4b2890951da..85e30426189 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogZoneDescriptorSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogZoneDescriptorSerializers.java
@@ -19,11 +19,11 @@ package org.apache.ignite.internal.catalog.descriptors;
 
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link CatalogZoneDescriptor}.
@@ -41,7 +41,7 @@ public class CatalogZoneDescriptorSerializers {
         }
 
         @Override
-        public CatalogZoneDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogZoneDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             int id = input.readVarIntAsInt();
             String name = input.readUTF();
             long updateToken = input.readVarInt();
@@ -75,7 +75,7 @@ public class CatalogZoneDescriptorSerializers {
         }
 
         @Override
-        public void writeTo(CatalogZoneDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogZoneDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(descriptor.id());
             output.writeUTF(descriptor.name());
             output.writeVarInt(descriptor.updateToken());
@@ -93,4 +93,58 @@ public class CatalogZoneDescriptorSerializers {
             output.writeByte(descriptor.consistencyMode().id());
         }
     }
+
+    /**
+     * Serializer for {@link CatalogZoneDescriptor}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class ZoneDescriptorSerializerV2 implements CatalogObjectSerializer<CatalogZoneDescriptor> {
+        @Override
+        public CatalogZoneDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
+            int id = input.readVarIntAsInt();
+            String name = input.readUTF();
+            long updateToken = input.readVarInt();
+
+            CatalogStorageProfilesDescriptor catalogStorageProfilesDescriptor = input.readEntry(CatalogStorageProfilesDescriptor.class);
+
+            int partitions = input.readVarIntAsInt();
+            int replicas = input.readVarIntAsInt();
+            int dataNodesAutoAdjust = input.readVarIntAsInt();
+            int dataNodesAutoAdjustScaleUp = input.readVarIntAsInt();
+            int dataNodesAutoAdjustScaleDown = input.readVarIntAsInt();
+            String filter = input.readUTF();
+            ConsistencyMode consistencyMode = ConsistencyMode.forId(input.readByte());
+
+            return new CatalogZoneDescriptor(
+                    id,
+                    name,
+                    partitions,
+                    replicas,
+                    dataNodesAutoAdjust,
+                    dataNodesAutoAdjustScaleUp,
+                    dataNodesAutoAdjustScaleDown,
+                    filter,
+                    catalogStorageProfilesDescriptor,
+                    updateToken,
+                    consistencyMode
+            );
+        }
+
+        @Override
+        public void writeTo(CatalogZoneDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(descriptor.id());
+            output.writeUTF(descriptor.name());
+            output.writeVarInt(descriptor.updateToken());
+
+            output.writeEntry(descriptor.storageProfiles());
+
+            output.writeVarInt(descriptor.partitions());
+            output.writeVarInt(descriptor.replicas());
+            output.writeVarInt(descriptor.dataNodesAutoAdjust());
+            output.writeVarInt(descriptor.dataNodesAutoAdjustScaleUp());
+            output.writeVarInt(descriptor.dataNodesAutoAdjustScaleDown());
+            output.writeUTF(descriptor.filter());
+            output.writeByte(descriptor.consistencyMode().id());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntrySerializers.java
index 42b49239120..5337f641e4d 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterColumnEntrySerializers.java
@@ -20,11 +20,11 @@ package org.apache.ignite.internal.catalog.storage;
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link AlterColumnEntry}.
@@ -42,7 +42,7 @@ public class AlterColumnEntrySerializers {
         }
 
         @Override
-        public AlterColumnEntry readFrom(IgniteDataInput input) throws IOException {
+        public AlterColumnEntry readFrom(CatalogObjectDataInput input) throws IOException {
             CatalogObjectSerializer<CatalogTableColumnDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_COLUMN.id());
 
@@ -53,10 +53,30 @@ public class AlterColumnEntrySerializers {
         }
 
         @Override
-        public void writeTo(AlterColumnEntry value, IgniteDataOutput output) throws IOException {
+        public void writeTo(AlterColumnEntry value, CatalogObjectDataOutput output) throws IOException {
             serializers.get(1, value.descriptor().typeId()).writeTo(value.descriptor(), output);
 
             output.writeVarInt(value.tableId());
         }
     }
+
+    /**
+     * Serializer for {@link AlterColumnEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class AlterColumnEntrySerializerV2 implements CatalogObjectSerializer<AlterColumnEntry> {
+        @Override
+        public AlterColumnEntry readFrom(CatalogObjectDataInput input) throws IOException {
+            CatalogTableColumnDescriptor descriptor = input.readEntry(CatalogTableColumnDescriptor.class);
+            int tableId = input.readVarIntAsInt();
+
+            return new AlterColumnEntry(tableId, descriptor);
+        }
+
+        @Override
+        public void writeTo(AlterColumnEntry value, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntry(value.descriptor());
+            output.writeVarInt(value.tableId());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterZoneEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterZoneEntrySerializers.java
index eb4ab1ddcb5..47ecd9ac88e 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterZoneEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/AlterZoneEntrySerializers.java
@@ -20,12 +20,12 @@ package org.apache.ignite.internal.catalog.storage;
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntry;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link AlterZoneEntry}.
@@ -43,7 +43,7 @@ public class AlterZoneEntrySerializers {
         }
 
         @Override
-        public AlterZoneEntry readFrom(IgniteDataInput input) throws IOException {
+        public AlterZoneEntry readFrom(CatalogObjectDataInput input)throws IOException {
             CatalogObjectSerializer<MarshallableEntry> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_ZONE.id());
 
@@ -53,8 +53,26 @@ public class AlterZoneEntrySerializers {
         }
 
         @Override
-        public void writeTo(AlterZoneEntry object, IgniteDataOutput output) throws IOException {
+        public void writeTo(AlterZoneEntry object, CatalogObjectDataOutput output) throws IOException {
             serializers.get(1, object.descriptor().typeId()).writeTo(object.descriptor(), output);
         }
     }
+
+    /**
+     * Serializer for {@link AlterZoneEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class AlterZoneEntrySerializerV2 implements CatalogObjectSerializer<AlterZoneEntry> {
+        @Override
+        public AlterZoneEntry readFrom(CatalogObjectDataInput input) throws IOException {
+            CatalogZoneDescriptor descriptor = input.readEntry(CatalogZoneDescriptor.class);
+
+            return new AlterZoneEntry(descriptor);
+        }
+
+        @Override
+        public void writeTo(AlterZoneEntry object, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntry(object.descriptor());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntrySerializers.java
index 75b0bc58538..9c376e95f71 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropColumnsEntrySerializers.java
@@ -23,11 +23,13 @@ import static org.apache.ignite.internal.util.IgniteUtils.capacity;
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataInput;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataOutput;
 
 /**
  * Serializers for {@link DropColumnsEntry}.
@@ -39,7 +41,7 @@ public class DropColumnsEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class DropColumnEntrySerializerV1 implements CatalogObjectSerializer<DropColumnsEntry> {
         @Override
-        public DropColumnsEntry readFrom(IgniteDataInput input) throws IOException {
+        public DropColumnsEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int tableId = input.readVarIntAsInt();
             Set<String> columns = CatalogSerializationUtils.readStringCollection(input, size -> new HashSet<>(capacity(size)));
 
@@ -47,9 +49,29 @@ public class DropColumnsEntrySerializers {
         }
 
         @Override
-        public void writeTo(DropColumnsEntry object, IgniteDataOutput output) throws IOException {
+        public void writeTo(DropColumnsEntry object, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(object.tableId());
             writeStringCollection(object.columns(), output);
         }
     }
+
+    /**
+     * Serializer for {@link DropColumnsEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class DropColumnEntrySerializerV2 implements CatalogObjectSerializer<DropColumnsEntry> {
+        @Override
+        public DropColumnsEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int tableId = input.readVarIntAsInt();
+            Set<String> columns = input.readObjectCollection(IgniteUnsafeDataInput::readUTF, size -> new HashSet<>(capacity(size)));
+
+            return new DropColumnsEntry(tableId, columns);
+        }
+
+        @Override
+        public void writeTo(DropColumnsEntry object, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(object.tableId());
+            output.writeObjectCollection(IgniteUnsafeDataOutput::writeUTF, object.columns());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropIndexEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropIndexEntrySerializers.java
index b8c963a722f..911d5466a58 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropIndexEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropIndexEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link DropIndexEntry}.
@@ -33,14 +33,32 @@ public class DropIndexEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class DropIndexEntrySerializerV1 implements CatalogObjectSerializer<DropIndexEntry> {
         @Override
-        public DropIndexEntry readFrom(IgniteDataInput input) throws IOException {
+        public DropIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int indexId = input.readVarIntAsInt();
 
             return new DropIndexEntry(indexId);
         }
 
         @Override
-        public void writeTo(DropIndexEntry entry, IgniteDataOutput out) throws IOException {
+        public void writeTo(DropIndexEntry entry, CatalogObjectDataOutput out) throws IOException {
+            out.writeVarInt(entry.indexId());
+        }
+    }
+
+    /**
+     * Serializer for {@link DropIndexEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class DropIndexEntrySerializerV2 implements CatalogObjectSerializer<DropIndexEntry> {
+        @Override
+        public DropIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int indexId = input.readVarIntAsInt();
+
+            return new DropIndexEntry(indexId);
+        }
+
+        @Override
+        public void writeTo(DropIndexEntry entry, CatalogObjectDataOutput out) throws IOException {
             out.writeVarInt(entry.indexId());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropSchemaSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropSchemaSerializers.java
index 54a76e95041..9bcebb46cbd 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropSchemaSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropSchemaSerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link DropSchemaEntry}.
@@ -33,14 +33,32 @@ public class DropSchemaSerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class DropSchemaSerializerV1 implements CatalogObjectSerializer<DropSchemaEntry> {
         @Override
-        public DropSchemaEntry readFrom(IgniteDataInput input) throws IOException {
+        public DropSchemaEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int schemaId = input.readVarIntAsInt();
 
             return new DropSchemaEntry(schemaId);
         }
 
         @Override
-        public void writeTo(DropSchemaEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(DropSchemaEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(entry.schemaId());
+        }
+    }
+
+    /**
+     * Serializer for {@link DropSchemaEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class DropSchemaSerializerV2 implements CatalogObjectSerializer<DropSchemaEntry> {
+        @Override
+        public DropSchemaEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int schemaId = input.readVarIntAsInt();
+
+            return new DropSchemaEntry(schemaId);
+        }
+
+        @Override
+        public void writeTo(DropSchemaEntry entry, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(entry.schemaId());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropTableEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropTableEntrySerializers.java
index a31255ee02e..9f878d1605f 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropTableEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropTableEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link DropTableEntry}.
@@ -33,14 +33,32 @@ public class DropTableEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class DropTableEntrySerializerV1 implements CatalogObjectSerializer<DropTableEntry> {
         @Override
-        public DropTableEntry readFrom(IgniteDataInput input) throws IOException {
+        public DropTableEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int tableId = input.readVarIntAsInt();
 
             return new DropTableEntry(tableId);
         }
 
         @Override
-        public void writeTo(DropTableEntry entry, IgniteDataOutput out) throws IOException {
+        public void writeTo(DropTableEntry entry, CatalogObjectDataOutput out) throws IOException {
+            out.writeVarInt(entry.tableId());
+        }
+    }
+
+    /**
+     * Serializer for {@link DropTableEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class DropTableEntrySerializerV2 implements CatalogObjectSerializer<DropTableEntry> {
+        @Override
+        public DropTableEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int tableId = input.readVarIntAsInt();
+
+            return new DropTableEntry(tableId);
+        }
+
+        @Override
+        public void writeTo(DropTableEntry entry, CatalogObjectDataOutput out) throws IOException {
             out.writeVarInt(entry.tableId());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropZoneEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropZoneEntrySerializers.java
index 16c00829a5f..9d587a7221c 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropZoneEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropZoneEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link DropZoneEntry}.
@@ -33,14 +33,32 @@ public class DropZoneEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class DropZoneEntrySerializerV1 implements CatalogObjectSerializer<DropZoneEntry> {
         @Override
-        public DropZoneEntry readFrom(IgniteDataInput input) throws IOException {
+        public DropZoneEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int zoneId = input.readVarIntAsInt();
 
             return new DropZoneEntry(zoneId);
         }
 
         @Override
-        public void writeTo(DropZoneEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(DropZoneEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(entry.zoneId());
+        }
+    }
+
+    /**
+     * Serializer for {@link DropZoneEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class DropZoneEntrySerializerV2 implements CatalogObjectSerializer<DropZoneEntry> {
+        @Override
+        public DropZoneEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int zoneId = input.readVarIntAsInt();
+
+            return new DropZoneEntry(zoneId);
+        }
+
+        @Override
+        public void writeTo(DropZoneEntry entry, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(entry.zoneId());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/MakeIndexAvailableEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/MakeIndexAvailableEntrySerializers.java
index 66ad92c7162..98e3e9eac71 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/MakeIndexAvailableEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/MakeIndexAvailableEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link MakeIndexAvailableEntry}.
@@ -33,14 +33,32 @@ public class MakeIndexAvailableEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class MakeIndexAvailableEntrySerializerV1 implements CatalogObjectSerializer<MakeIndexAvailableEntry> {
         @Override
-        public MakeIndexAvailableEntry readFrom(IgniteDataInput input) throws IOException {
+        public MakeIndexAvailableEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int indexId = input.readVarIntAsInt();
 
             return new MakeIndexAvailableEntry(indexId);
         }
 
         @Override
-        public void writeTo(MakeIndexAvailableEntry object, IgniteDataOutput output) throws IOException {
+        public void writeTo(MakeIndexAvailableEntry object, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(object.indexId);
+        }
+    }
+
+    /**
+     * Serializer for {@link MakeIndexAvailableEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class MakeIndexAvailableEntrySerializerV2 implements CatalogObjectSerializer<MakeIndexAvailableEntry> {
+        @Override
+        public MakeIndexAvailableEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int indexId = input.readVarIntAsInt();
+
+            return new MakeIndexAvailableEntry(indexId);
+        }
+
+        @Override
+        public void writeTo(MakeIndexAvailableEntry object, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(object.indexId);
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntrySerializers.java
index 469a7d1bdad..b6ba24b7d74 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewColumnsEntrySerializers.java
@@ -24,11 +24,11 @@ import java.io.IOException;
 import java.util.List;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link NewColumnsEntry}.
@@ -46,7 +46,7 @@ public class NewColumnsEntrySerializers {
         }
 
         @Override
-        public NewColumnsEntry readFrom(IgniteDataInput in) throws IOException {
+        public NewColumnsEntry readFrom(CatalogObjectDataInput in) throws IOException {
             CatalogObjectSerializer<CatalogTableColumnDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_COLUMN.id());
 
@@ -57,7 +57,7 @@ public class NewColumnsEntrySerializers {
         }
 
         @Override
-        public void writeTo(NewColumnsEntry entry, IgniteDataOutput out) throws IOException {
+        public void writeTo(NewColumnsEntry entry, CatalogObjectDataOutput out) throws IOException {
             CatalogObjectSerializer<CatalogTableColumnDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE_COLUMN.id());
 
@@ -65,4 +65,24 @@ public class NewColumnsEntrySerializers {
             out.writeVarInt(entry.tableId());
         }
     }
+
+    /**
+     * Serializer for {@link NewColumnsEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class NewColumnsEntrySerializerV2 implements CatalogObjectSerializer<NewColumnsEntry> {
+        @Override
+        public NewColumnsEntry readFrom(CatalogObjectDataInput in) throws IOException {
+            List<CatalogTableColumnDescriptor> columns = in.readEntryList(CatalogTableColumnDescriptor.class);
+            int tableId = in.readVarIntAsInt();
+
+            return new NewColumnsEntry(tableId, columns);
+        }
+
+        @Override
+        public void writeTo(NewColumnsEntry entry, CatalogObjectDataOutput out) throws IOException {
+            out.writeEntryList(entry.descriptors());
+            out.writeVarInt(entry.tableId());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewIndexEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewIndexEntrySerializers.java
index f2308b48f8e..9823544e2e1 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewIndexEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewIndexEntrySerializers.java
@@ -20,11 +20,11 @@ package org.apache.ignite.internal.catalog.storage;
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils.IndexDescriptorSerializerHelper;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link NewIndexEntry}.
@@ -42,15 +42,33 @@ public class NewIndexEntrySerializers {
         }
 
         @Override
-        public NewIndexEntry readFrom(IgniteDataInput input) throws IOException {
+        public NewIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
             CatalogIndexDescriptor descriptor = serializerHelper.readFrom(input);
 
             return new NewIndexEntry(descriptor);
         }
 
         @Override
-        public void writeTo(NewIndexEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(NewIndexEntry entry, CatalogObjectDataOutput output) throws IOException {
             serializerHelper.writeTo(entry.descriptor(), output);
         }
     }
+
+    /**
+     * Serializer for {@link NewIndexEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class NewIndexEntrySerializerV2 implements CatalogObjectSerializer<NewIndexEntry> {
+        @Override
+        public NewIndexEntry readFrom(CatalogObjectDataInput input) throws IOException {
+            CatalogIndexDescriptor descriptor = input.readEntry(CatalogIndexDescriptor.class);
+
+            return new NewIndexEntry(descriptor);
+        }
+
+        @Override
+        public void writeTo(NewIndexEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntry(entry.descriptor());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSchemaEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSchemaEntrySerializers.java
index af3dabd0bbe..2832c94af89 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSchemaEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSchemaEntrySerializers.java
@@ -20,11 +20,11 @@ package org.apache.ignite.internal.catalog.storage;
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link NewSchemaEntry}.
@@ -42,7 +42,7 @@ public class NewSchemaEntrySerializers {
         }
 
         @Override
-        public NewSchemaEntry readFrom(IgniteDataInput input) throws IOException {
+        public NewSchemaEntry readFrom(CatalogObjectDataInput input) throws IOException {
             CatalogObjectSerializer<CatalogSchemaDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_SCHEMA.id());
 
@@ -51,8 +51,25 @@ public class NewSchemaEntrySerializers {
         }
 
         @Override
-        public void writeTo(NewSchemaEntry value, IgniteDataOutput output) throws IOException {
+        public void writeTo(NewSchemaEntry value, CatalogObjectDataOutput output) throws IOException {
             serializers.get(1, MarshallableEntryType.DESCRIPTOR_SCHEMA.id()).writeTo(value.descriptor(), output);
         }
     }
+
+    /**
+     * Serializer for {@link NewSchemaEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class SerializerV2 implements CatalogObjectSerializer<NewSchemaEntry> {
+        @Override
+        public NewSchemaEntry readFrom(CatalogObjectDataInput input) throws IOException {
+            CatalogSchemaDescriptor schemaDescriptor = input.readEntry(CatalogSchemaDescriptor.class);
+            return new NewSchemaEntry(schemaDescriptor);
+        }
+
+        @Override
+        public void writeTo(NewSchemaEntry value, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntry(value.descriptor());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSystemViewEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSystemViewEntrySerializers.java
index afb919d2730..1ead28f6d3b 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSystemViewEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewSystemViewEntrySerializers.java
@@ -20,11 +20,11 @@ package org.apache.ignite.internal.catalog.storage;
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSystemViewDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link NewSystemViewEntry}.
@@ -42,7 +42,7 @@ public class NewSystemViewEntrySerializers {
         }
 
         @Override
-        public NewSystemViewEntry readFrom(IgniteDataInput input) throws IOException {
+        public NewSystemViewEntry readFrom(CatalogObjectDataInput input)throws IOException {
             CatalogObjectSerializer<CatalogSystemViewDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_SYSTEM_VIEW.id());
 
@@ -52,8 +52,26 @@ public class NewSystemViewEntrySerializers {
         }
 
         @Override
-        public void writeTo(NewSystemViewEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(NewSystemViewEntry entry, CatalogObjectDataOutput output) throws IOException {
             serializers.get(1, entry.descriptor().typeId()).writeTo(entry.descriptor(), output);
         }
     }
+
+    /**
+     * Serializer for {@link NewSystemViewEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class NewSystemViewEntrySerializerV2 implements CatalogObjectSerializer<NewSystemViewEntry> {
+        @Override
+        public NewSystemViewEntry readFrom(CatalogObjectDataInput input) throws IOException {
+            CatalogSystemViewDescriptor descriptor = input.readEntry(CatalogSystemViewDescriptor.class);
+
+            return new NewSystemViewEntry(descriptor);
+        }
+
+        @Override
+        public void writeTo(NewSystemViewEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntry(entry.descriptor());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewTableEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewTableEntrySerializers.java
index 8c732e59501..be64065b26f 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewTableEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewTableEntrySerializers.java
@@ -20,11 +20,11 @@ package org.apache.ignite.internal.catalog.storage;
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link NewTableEntry}.
@@ -42,7 +42,7 @@ public class NewTableEntrySerializers {
         }
 
         @Override
-        public NewTableEntry readFrom(IgniteDataInput input) throws IOException {
+        public NewTableEntry readFrom(CatalogObjectDataInput input)throws IOException {
             CatalogObjectSerializer<CatalogTableDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_TABLE.id());
 
@@ -52,8 +52,26 @@ public class NewTableEntrySerializers {
         }
 
         @Override
-        public void writeTo(NewTableEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(NewTableEntry entry, CatalogObjectDataOutput output) throws IOException {
             serializers.get(1, entry.descriptor().typeId()).writeTo(entry.descriptor(), output);
         }
     }
+
+    /**
+     * Serializer for {@link NewTableEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class NewTableEntrySerializerV2 implements CatalogObjectSerializer<NewTableEntry> {
+        @Override
+        public NewTableEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            CatalogTableDescriptor descriptor = input.readEntry(CatalogTableDescriptor.class);
+
+            return new NewTableEntry(descriptor);
+        }
+
+        @Override
+        public void writeTo(NewTableEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntry(entry.descriptor());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewZoneEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewZoneEntrySerializers.java
index 0d8185ed5d9..67fe8a16b89 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewZoneEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/NewZoneEntrySerializers.java
@@ -20,11 +20,11 @@ package org.apache.ignite.internal.catalog.storage;
 import java.io.IOException;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link NewZoneEntry}.
@@ -42,7 +42,7 @@ public class NewZoneEntrySerializers {
         }
 
         @Override
-        public NewZoneEntry readFrom(IgniteDataInput input) throws IOException {
+        public NewZoneEntry readFrom(CatalogObjectDataInput input)throws IOException {
             CatalogObjectSerializer<CatalogZoneDescriptor> serializer =
                     serializers.get(1, MarshallableEntryType.DESCRIPTOR_ZONE.id());
 
@@ -52,8 +52,26 @@ public class NewZoneEntrySerializers {
         }
 
         @Override
-        public void writeTo(NewZoneEntry object, IgniteDataOutput output) throws IOException {
+        public void writeTo(NewZoneEntry object, CatalogObjectDataOutput output) throws IOException {
             serializers.get(1, object.descriptor().typeId()).writeTo(object.descriptor(), output);
         }
     }
+
+    /**
+     * Serializer for {@link NewZoneEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class NewZoneEntrySerializerV2 implements CatalogObjectSerializer<NewZoneEntry> {
+        @Override
+        public NewZoneEntry readFrom(CatalogObjectDataInput input) throws IOException {
+            CatalogZoneDescriptor descriptor = input.readEntry(CatalogZoneDescriptor.class);
+
+            return new NewZoneEntry(descriptor);
+        }
+
+        @Override
+        public void writeTo(NewZoneEntry object, CatalogObjectDataOutput output) throws IOException {
+            output.writeEntry(object.descriptor());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/ObjectIdGenUpdateEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/ObjectIdGenUpdateEntrySerializers.java
index b5e53ae1b1b..68f0da97774 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/ObjectIdGenUpdateEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/ObjectIdGenUpdateEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link ObjectIdGenUpdateEntry}.
@@ -33,14 +33,32 @@ public class ObjectIdGenUpdateEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class ObjectIdGenUpdateEntrySerializerV1 implements CatalogObjectSerializer<ObjectIdGenUpdateEntry> {
         @Override
-        public ObjectIdGenUpdateEntry readFrom(IgniteDataInput input) throws IOException {
+        public ObjectIdGenUpdateEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int delta = input.readVarIntAsInt();
 
             return new ObjectIdGenUpdateEntry(delta);
         }
 
         @Override
-        public void writeTo(ObjectIdGenUpdateEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(ObjectIdGenUpdateEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(entry.delta());
+        }
+    }
+
+    /**
+     * Serializer for {@link ObjectIdGenUpdateEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class ObjectIdGenUpdateEntrySerializerV2 implements CatalogObjectSerializer<ObjectIdGenUpdateEntry> {
+        @Override
+        public ObjectIdGenUpdateEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int delta = input.readVarIntAsInt();
+
+            return new ObjectIdGenUpdateEntry(delta);
+        }
+
+        @Override
+        public void writeTo(ObjectIdGenUpdateEntry entry, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(entry.delta());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RemoveIndexEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RemoveIndexEntrySerializers.java
index f5985a166bb..e4c1ce73a7d 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RemoveIndexEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RemoveIndexEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link RemoveIndexEntry}.
@@ -33,14 +33,32 @@ public class RemoveIndexEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class RemoveIndexEntrySerializerV1 implements CatalogObjectSerializer<RemoveIndexEntry> {
         @Override
-        public RemoveIndexEntry readFrom(IgniteDataInput input) throws IOException {
+        public RemoveIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int indexId = input.readVarIntAsInt();
 
             return new RemoveIndexEntry(indexId);
         }
 
         @Override
-        public void writeTo(RemoveIndexEntry entry, IgniteDataOutput out) throws IOException {
+        public void writeTo(RemoveIndexEntry entry, CatalogObjectDataOutput out) throws IOException {
+            out.writeVarInt(entry.indexId());
+        }
+    }
+
+    /**
+     * Serializer for {@link RemoveIndexEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class RemoveIndexEntrySerializerV2 implements CatalogObjectSerializer<RemoveIndexEntry> {
+        @Override
+        public RemoveIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int indexId = input.readVarIntAsInt();
+
+            return new RemoveIndexEntry(indexId);
+        }
+
+        @Override
+        public void writeTo(RemoveIndexEntry entry, CatalogObjectDataOutput out) throws IOException {
             out.writeVarInt(entry.indexId());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameIndexEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameIndexEntrySerializers.java
index b66dd753b23..b17b40591c5 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameIndexEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameIndexEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link RemoveIndexEntry}.
@@ -33,7 +33,7 @@ public class RenameIndexEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class RenameIndexEntrySerializerV1 implements CatalogObjectSerializer<RenameIndexEntry> {
         @Override
-        public RenameIndexEntry readFrom(IgniteDataInput input) throws IOException {
+        public RenameIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int indexId = input.readVarIntAsInt();
 
             String newIndexName = input.readUTF();
@@ -42,7 +42,28 @@ public class RenameIndexEntrySerializers {
         }
 
         @Override
-        public void writeTo(RenameIndexEntry entry, IgniteDataOutput out) throws IOException {
+        public void writeTo(RenameIndexEntry entry, CatalogObjectDataOutput out) throws IOException {
+            out.writeVarInt(entry.indexId());
+            out.writeUTF(entry.newIndexName());
+        }
+    }
+
+    /**
+     * Serializer for {@link RemoveIndexEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class RenameIndexEntrySerializerV2 implements CatalogObjectSerializer<RenameIndexEntry> {
+        @Override
+        public RenameIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int indexId = input.readVarIntAsInt();
+
+            String newIndexName = input.readUTF();
+
+            return new RenameIndexEntry(indexId, newIndexName);
+        }
+
+        @Override
+        public void writeTo(RenameIndexEntry entry, CatalogObjectDataOutput out) throws IOException {
             out.writeVarInt(entry.indexId());
             out.writeUTF(entry.newIndexName());
         }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntrySerializers.java
index 72068ef5f1d..10d1acc0165 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/RenameTableEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link RenameTableEntry}.
@@ -33,7 +33,7 @@ public class RenameTableEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class RenameTableEntrySerializerV1 implements CatalogObjectSerializer<RenameTableEntry> {
         @Override
-        public RenameTableEntry readFrom(IgniteDataInput input) throws IOException {
+        public RenameTableEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int tableId = input.readVarIntAsInt();
             String newTableName = input.readUTF();
 
@@ -41,7 +41,27 @@ public class RenameTableEntrySerializers {
         }
 
         @Override
-        public void writeTo(RenameTableEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(RenameTableEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(entry.tableId());
+            output.writeUTF(entry.newTableName());
+        }
+    }
+
+    /**
+     * Serializer for {@link RenameTableEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class RenameTableEntrySerializerV2 implements CatalogObjectSerializer<RenameTableEntry> {
+        @Override
+        public RenameTableEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int tableId = input.readVarIntAsInt();
+            String newTableName = input.readUTF();
+
+            return new RenameTableEntry(tableId, newTableName);
+        }
+
+        @Override
+        public void writeTo(RenameTableEntry entry, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(entry.tableId());
             output.writeUTF(entry.newTableName());
         }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SetDefaultZoneEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SetDefaultZoneEntrySerializers.java
index e69a258df30..bb64b4a52a6 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SetDefaultZoneEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SetDefaultZoneEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link SetDefaultZoneEntry}.
@@ -33,14 +33,32 @@ public class SetDefaultZoneEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class SetDefaultZoneEntrySerializerV1 implements CatalogObjectSerializer<SetDefaultZoneEntry> {
         @Override
-        public SetDefaultZoneEntry readFrom(IgniteDataInput input) throws IOException {
+        public SetDefaultZoneEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int zoneId = input.readVarIntAsInt();
 
             return new SetDefaultZoneEntry(zoneId);
         }
 
         @Override
-        public void writeTo(SetDefaultZoneEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(SetDefaultZoneEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(entry.zoneId());
+        }
+    }
+
+    /**
+     * Serializer for {@link SetDefaultZoneEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class SetDefaultZoneEntrySerializerV2 implements CatalogObjectSerializer<SetDefaultZoneEntry> {
+        @Override
+        public SetDefaultZoneEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int zoneId = input.readVarIntAsInt();
+
+            return new SetDefaultZoneEntry(zoneId);
+        }
+
+        @Override
+        public void writeTo(SetDefaultZoneEntry entry, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(entry.zoneId());
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SnapshotEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SnapshotEntrySerializers.java
index 932885656e6..053382769f2 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SnapshotEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/SnapshotEntrySerializers.java
@@ -18,15 +18,17 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializationUtils;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link SnapshotEntry}.
@@ -44,7 +46,7 @@ public class SnapshotEntrySerializers {
         }
 
         @Override
-        public SnapshotEntry readFrom(IgniteDataInput input) throws IOException {
+        public SnapshotEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int catalogVersion = input.readVarIntAsInt();
             long activationTime = input.readLong();
             int objectIdGenState = input.readVarIntAsInt();
@@ -66,7 +68,7 @@ public class SnapshotEntrySerializers {
         }
 
         @Override
-        public void writeTo(SnapshotEntry entry, IgniteDataOutput output) throws IOException {
+        public void writeTo(SnapshotEntry entry, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(entry.version());
             output.writeLong(entry.activationTime());
             output.writeVarInt(entry.objectIdGenState());
@@ -86,4 +88,50 @@ public class SnapshotEntrySerializers {
             }
         }
     }
+
+    /**
+     * Serializer for {@link SnapshotEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class SnapshotEntrySerializerV2 implements CatalogObjectSerializer<SnapshotEntry> {
+        @Override
+        public SnapshotEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int catalogVersion = input.readVarIntAsInt();
+            long activationTime = input.readLong();
+            int objectIdGenState = input.readVarIntAsInt();
+
+            List<CatalogZoneDescriptor> zones = input.readCompactEntryList(CatalogZoneDescriptor.class);
+            List<CatalogSchemaDescriptor> schemas = input.readCompactEntryList(CatalogSchemaDescriptor.class);
+
+            Integer defaultZoneId = null;
+            if (input.readBoolean()) {
+                defaultZoneId = input.readVarIntAsInt();
+            }
+
+            return new SnapshotEntry(
+                    catalogVersion,
+                    activationTime,
+                    objectIdGenState,
+                    zones.toArray(new CatalogZoneDescriptor[0]),
+                    schemas.toArray(new CatalogSchemaDescriptor[0]),
+                    defaultZoneId
+            );
+        }
+
+        @Override
+        public void writeTo(SnapshotEntry entry, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(entry.version());
+            output.writeLong(entry.activationTime());
+            output.writeVarInt(entry.objectIdGenState());
+
+            output.writeCompactEntryList(Arrays.asList(entry.zones()));
+            output.writeCompactEntryList(Arrays.asList(entry.schemas()));
+
+            Integer defaultZoneId = entry.defaultZoneId();
+            output.writeBoolean(defaultZoneId != null);
+            if (defaultZoneId != null) {
+                output.writeVarInt(defaultZoneId);
+            }
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/StartBuildingIndexEntrySerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/StartBuildingIndexEntrySerializers.java
index ecd75e78709..1d547b8bb3d 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/StartBuildingIndexEntrySerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/StartBuildingIndexEntrySerializers.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.catalog.storage;
 
 import java.io.IOException;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link StartBuildingIndexEntry}.
@@ -33,14 +33,32 @@ public class StartBuildingIndexEntrySerializers {
     @CatalogSerializer(version = 1, since = "3.0.0")
     static class StartBuildingIndexEntrySerializerV1 implements CatalogObjectSerializer<StartBuildingIndexEntry> {
         @Override
-        public StartBuildingIndexEntry readFrom(IgniteDataInput input) throws IOException {
+        public StartBuildingIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
             int indexId = input.readVarIntAsInt();
 
             return new StartBuildingIndexEntry(indexId);
         }
 
         @Override
-        public void writeTo(StartBuildingIndexEntry object, IgniteDataOutput output) throws IOException {
+        public void writeTo(StartBuildingIndexEntry object, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(object.indexId);
+        }
+    }
+
+    /**
+     * Serializer for {@link StartBuildingIndexEntry}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    static class StartBuildingIndexEntrySerializerV2 implements CatalogObjectSerializer<StartBuildingIndexEntry> {
+        @Override
+        public StartBuildingIndexEntry readFrom(CatalogObjectDataInput input)throws IOException {
+            int indexId = input.readVarIntAsInt();
+
+            return new StartBuildingIndexEntry(indexId);
+        }
+
+        @Override
+        public void writeTo(StartBuildingIndexEntry object, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(object.indexId);
         }
     }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/UpdateLogImpl.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/UpdateLogImpl.java
index aac43a8c08e..a50cf97ef5b 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/UpdateLogImpl.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/UpdateLogImpl.java
@@ -89,7 +89,7 @@ public class UpdateLogImpl implements UpdateLog {
      */
     public UpdateLogImpl(MetaStorageManager metastore) {
         this.metastore = metastore;
-        this.marshaller = new UpdateLogMarshallerImpl();
+        this.marshaller = new UpdateLogMarshallerImpl(2);
     }
 
     @TestOnly
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/VersionedUpdateSerializers.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/VersionedUpdateSerializers.java
index 0a1d31ad80b..aa88613c95e 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/VersionedUpdateSerializers.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/VersionedUpdateSerializers.java
@@ -21,11 +21,11 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntry;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Serializers for {@link VersionedUpdate}.
@@ -43,7 +43,7 @@ public class VersionedUpdateSerializers {
         }
 
         @Override
-        public VersionedUpdate readFrom(IgniteDataInput input) throws IOException {
+        public VersionedUpdate readFrom(CatalogObjectDataInput input)throws IOException {
             int ver = input.readVarIntAsInt();
             long delayDurationMs = input.readVarInt();
 
@@ -62,7 +62,7 @@ public class VersionedUpdateSerializers {
         }
 
         @Override
-        public void writeTo(VersionedUpdate update, IgniteDataOutput output) throws IOException {
+        public void writeTo(VersionedUpdate update, CatalogObjectDataOutput output) throws IOException {
             output.writeVarInt(update.version());
             output.writeVarInt(update.delayDurationMs());
 
@@ -74,4 +74,27 @@ public class VersionedUpdateSerializers {
             }
         }
     }
+
+    /**
+     * Serializer for {@link VersionedUpdate}.
+     */
+    @CatalogSerializer(version = 2, since = "3.1.0")
+    public static class VersionedUpdateSerializerV2 implements CatalogObjectSerializer<VersionedUpdate> {
+        @Override
+        public VersionedUpdate readFrom(CatalogObjectDataInput input) throws IOException {
+            int ver = input.readVarIntAsInt();
+            long delayDurationMs = input.readVarInt();
+
+            List<UpdateEntry> entries = input.readEntryList(UpdateEntry.class);
+
+            return new VersionedUpdate(ver, delayDurationMs, entries);
+        }
+
+        @Override
+        public void writeTo(VersionedUpdate value, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(value.version());
+            output.writeVarInt(value.delayDurationMs());
+            output.writeEntryList(value.entries());
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImpl.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImpl.java
index d7e2be51e13..e5ca1e1eb9d 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImpl.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImpl.java
@@ -170,6 +170,9 @@ class CatalogEntrySerializerProviderImpl implements CatalogEntrySerializerProvid
                     return (CatalogObjectSerializer<? extends MarshallableEntry>) constructor.newInstance();
                 }
 
+                // This constructor is only needed for serialization protocol V1,
+                // because there is no object version in that protocol, so we assigned them version 1
+                // to make CatalogEntrySerializerProvider API work for both protocol versions.
                 if (constructor.getParameterCount() == 1 && CatalogEntrySerializerProvider.class.isAssignableFrom(
                         constructor.getParameterTypes()[0])) {
                     return (CatalogObjectSerializer<? extends MarshallableEntry>) constructor.newInstance(provider);
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
index 37e73431a67..06600d7cf02 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
@@ -31,4 +31,8 @@ public class CatalogMarshallerException extends IgniteInternalException {
     CatalogMarshallerException(@Nullable Throwable cause) {
         super(Common.INTERNAL_ERR, cause);
     }
+
+    CatalogMarshallerException(String message) {
+        super(Common.INTERNAL_ERR, message);
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectDataInput.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectDataInput.java
new file mode 100644
index 00000000000..c7b800760d4
--- /dev/null
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectDataInput.java
@@ -0,0 +1,146 @@
+/*
+ * 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.catalog.storage.serialization;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.IntFunction;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataInput;
+
+/**
+ * Catalog object data input.
+ */
+public class CatalogObjectDataInput extends IgniteUnsafeDataInput {
+
+    private final CatalogEntrySerializerProvider serializers;
+
+    /**
+     * Constructor.
+     */
+    public CatalogObjectDataInput(CatalogEntrySerializerProvider serializers, byte[] bytes) {
+        super(bytes);
+
+        this.serializers = serializers;
+    }
+
+    /**
+     * Reads an entry using a serializer of the version it was written with.
+     */
+    public MarshallableEntry readEntry() throws IOException {
+        int typeId = readShort();
+        int entryVersion = readVarIntAsInt();
+
+        return serializers.get(entryVersion, typeId).readFrom(this);
+    }
+
+    /**
+     * Reads an entry.
+     *
+     * @param type Entry type.
+     */
+    public <T extends MarshallableEntry> T readEntry(Class<T> type) throws IOException {
+        int typeId = readShort();
+        int entryVersion = readVarIntAsInt();
+        MarshallableEntry entry = serializers.get(entryVersion, typeId).readFrom(this);
+        return type.cast(entry);
+    }
+
+    /**
+     * Reads an entry list.
+     *
+     * @param type Type.
+     */
+    public <T extends MarshallableEntry> List<T> readEntryList(Class<T> type) throws IOException {
+        int size = readVarIntAsInt();
+        List<T> list = new ArrayList<>(size);
+
+        for (int i = 0; i < size; i++) {
+            int typeId = readShort();
+            int entryVersion = readVarIntAsInt();
+
+            MarshallableEntry entry = serializers.get(entryVersion, typeId).readFrom(this);
+            list.add(type.cast(entry));
+        }
+
+        return list;
+    }
+
+    /**
+     * Reads a compact entry list.
+     *
+     * @param type Type.
+     */
+    public <T extends MarshallableEntry> List<T> readCompactEntryList(Class<T> type) throws IOException {
+        int size = readVarIntAsInt();
+        if (size == 0) {
+            return Collections.emptyList();
+        }
+
+        List<T> list = new ArrayList<>(size);
+        int typeId = readShort();
+        int entryVersion = readVarIntAsInt();
+        CatalogObjectSerializer<MarshallableEntry> serializer = serializers.get(entryVersion, typeId);
+
+        for (int i = 0; i < size; i++) {
+            MarshallableEntry entry = serializer.readFrom(this);
+            list.add(type.cast(entry));
+        }
+
+        return list;
+    }
+
+    /**
+     * Reads a collection of non-versioned objects.
+     * <b>NOTE: To read versioned elements use {@link #readEntryList(Class)} or {@link #readCompactEntryList(Class)} instead.</b>
+     *
+     * @param reader Element reader.
+     * @param newCollection Collection factory.
+     * @param <T> Element type.
+     *
+     * @return A collection of objects.
+     */
+    public <C extends Collection<T>, T> C readObjectCollection(ElementReader<T> reader, IntFunction<C> newCollection) throws IOException {
+        int size = readVarIntAsInt();
+        C col = newCollection.apply(size);
+
+        for (int i = 0; i < size; i++) {
+            T element = reader.read(this);
+            col.add(element);
+        }
+
+        return col;
+    }
+
+    /**
+     * Element reader.
+     *
+     * @param <T> Element type.
+     */
+    @FunctionalInterface
+    public interface ElementReader<T> {
+        /**
+         * Reads an element from the input.
+         *
+         * @param in Input.
+         */
+        T read(CatalogObjectDataInput in) throws IOException;
+    }
+}
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectDataOutput.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectDataOutput.java
new file mode 100644
index 00000000000..2dea89b3395
--- /dev/null
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectDataOutput.java
@@ -0,0 +1,134 @@
+/*
+ * 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.catalog.storage.serialization;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataOutput;
+
+/**
+ * Catalog object data output.
+ */
+public class CatalogObjectDataOutput extends IgniteUnsafeDataOutput {
+
+    /** Initial capacity (in bytes) of the buffer used for data output. */
+    private static final int INITIAL_BUFFER_CAPACITY = 256;
+
+    private final CatalogEntrySerializerProvider serializers;
+
+    /**
+     * Constructor.
+     */
+    public CatalogObjectDataOutput(CatalogEntrySerializerProvider serializers) {
+        super(INITIAL_BUFFER_CAPACITY);
+
+        this.serializers = serializers;
+    }
+
+    /**
+     * Writes an entry with the latest serializer.
+     *
+     * @param entry Entry.
+     */
+    public void writeEntry(MarshallableEntry entry) throws IOException {
+        int typeId = entry.typeId();
+        int entryVersion = serializers.latestSerializerVersion(typeId);
+
+        writeEntryHeader(entry, entryVersion);
+        serializers.get(entryVersion, typeId).writeTo(entry, this);
+    }
+
+    /**
+     * Writes a list of entries.
+     *
+     * @param entries Entries.
+     */
+    public <T extends MarshallableEntry> void writeEntryList(List<T> entries) throws IOException {
+        writeVarInt(entries.size());
+
+        for (T entry : entries) {
+            int typeId = entry.typeId();
+            int version = serializers.latestSerializerVersion(typeId);
+
+            writeEntryHeader(entry, version);
+            serializers.get(version, typeId).writeTo(entry, this);
+        }
+    }
+
+    /**
+     * Writes a list of entries of exactly the same type in a compact way.
+     *
+     * @param entries Entries.
+     */
+    public <T extends MarshallableEntry> void writeCompactEntryList(List<T> entries) throws IOException {
+        if (entries.isEmpty()) {
+            writeVarInt(0);
+            return;
+        }
+
+        writeVarInt(entries.size());
+
+        int typeId = entries.get(0).typeId();
+        int version = serializers.latestSerializerVersion(typeId);
+        writeEntryHeader(entries.get(0), version);
+
+        CatalogObjectSerializer<MarshallableEntry> serializer = serializers.get(version, typeId);
+
+        for (T entry : entries) {
+            assert entry.typeId() == typeId : "Entry type do not match";
+            serializer.writeTo(entry, this);
+        }
+    }
+
+    /**
+     * Writes a collection of non-versioned objects.
+     * <b>NOTE: To write versioned elements use {@link #writeEntryList(List)} or {@link #writeCompactEntryList(List)} instead.</b>
+     *
+     * @param writer Element writer.
+     * @param <T> Element type.
+     */
+    public <T> void writeObjectCollection(ElementWriter<T> writer, Collection<T> list) throws IOException {
+        writeVarInt(list.size());
+        for (T element : list) {
+            writer.write(this, element);
+        }
+    }
+
+    private void writeEntryHeader(MarshallableEntry entry, int entryVersion) throws IOException {
+        int typeId = entry.typeId();
+        writeShort(typeId);
+        writeVarInt(entryVersion);
+    }
+
+    /**
+     * Element writer.
+     *
+     * @param <T> Element type.
+     */
+    @FunctionalInterface
+    public interface ElementWriter<T> {
+        /**
+         * Writes the given element into the output.
+         *
+         * @param out Output.
+         * @param element Element.
+         */
+        void write(CatalogObjectDataOutput out, T element) throws IOException;
+    }
+}
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectSerializer.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectSerializer.java
index 47a815ea8bb..284b8c2d351 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectSerializer.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogObjectSerializer.java
@@ -18,8 +18,6 @@
 package org.apache.ignite.internal.catalog.storage.serialization;
 
 import java.io.IOException;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Catalog object serializer.
@@ -36,7 +34,7 @@ public interface CatalogObjectSerializer<T> {
      * @param input Data input.
      * @return Catalog entry.
      */
-    T readFrom(IgniteDataInput input) throws IOException;
+    T readFrom(CatalogObjectDataInput input) throws IOException;
 
     /**
      * Writes catalog object to data output.
@@ -44,5 +42,5 @@ public interface CatalogObjectSerializer<T> {
      * @param value Catalog entry.
      * @param output Data output.
      */
-    void writeTo(T value, IgniteDataOutput output) throws IOException;
+    void writeTo(T value, CatalogObjectDataOutput output) throws IOException;
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java
index c20ccc8dce6..899301e7cf3 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogSerializationUtils.java
@@ -27,8 +27,6 @@ import java.util.List;
 import java.util.function.IntFunction;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor.CatalogIndexDescriptorType;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -54,7 +52,7 @@ public class CatalogSerializationUtils {
     }
 
     /** Writes collection containing strings. */
-    public static void writeStringCollection(@Nullable Collection<String> list, IgniteDataOutput out) throws IOException {
+    public static void writeStringCollection(@Nullable Collection<String> list, CatalogObjectDataOutput out) throws IOException {
         if (list == null) {
             out.writeVarInt(-1);
 
@@ -69,7 +67,7 @@ public class CatalogSerializationUtils {
     }
 
     /** Reads collection containing strings. */
-    public static <T extends Collection<String>> @Nullable T readStringCollection(IgniteDataInput in, IntFunction<T> factory)
+    public static <T extends Collection<String>> @Nullable T readStringCollection(CatalogObjectDataInput in, IntFunction<T> factory)
             throws IOException {
         int size = in.readVarIntAsInt();
 
@@ -87,7 +85,7 @@ public class CatalogSerializationUtils {
     }
 
     /** Reads array of objects. */
-    public static <T> T[] readArray(CatalogObjectSerializer<T> serializer, IgniteDataInput input, Class<T> clazz)
+    public static <T> T[] readArray(CatalogObjectSerializer<T> serializer, CatalogObjectDataInput input, Class<T> clazz)
             throws IOException {
         int len = input.readVarIntAsInt();
 
@@ -101,7 +99,7 @@ public class CatalogSerializationUtils {
     }
 
     /** Writes array of objects. */
-    public static <T> void writeArray(T[] items, CatalogObjectSerializer<T> serializer, IgniteDataOutput output)
+    public static <T> void writeArray(T[] items, CatalogObjectSerializer<T> serializer, CatalogObjectDataOutput output)
             throws IOException {
         output.writeVarInt(items.length);
 
@@ -111,7 +109,7 @@ public class CatalogSerializationUtils {
     }
 
     /** Reads list of objects. */
-    public static <T> List<T> readList(CatalogObjectSerializer<T> serializer, IgniteDataInput input)
+    public static <T> List<T> readList(CatalogObjectSerializer<T> serializer, CatalogObjectDataInput input)
             throws IOException {
         int len = input.readVarIntAsInt();
 
@@ -128,7 +126,7 @@ public class CatalogSerializationUtils {
 
     /** Writes list of objects. */
     public static <T> void writeList(List<T> items, CatalogObjectSerializer<T> serializer,
-            IgniteDataOutput output) throws IOException {
+            CatalogObjectDataOutput output) throws IOException {
         output.writeVarInt(items.size());
 
         for (T item : items) {
@@ -151,7 +149,7 @@ public class CatalogSerializationUtils {
         }
 
         @Override
-        public CatalogIndexDescriptor readFrom(IgniteDataInput input) throws IOException {
+        public CatalogIndexDescriptor readFrom(CatalogObjectDataInput input) throws IOException {
             int idxType = input.readByte();
 
             CatalogIndexDescriptorType type = CatalogIndexDescriptorType.forId(idxType);
@@ -166,7 +164,7 @@ public class CatalogSerializationUtils {
         }
 
         @Override
-        public void writeTo(CatalogIndexDescriptor descriptor, IgniteDataOutput output) throws IOException {
+        public void writeTo(CatalogIndexDescriptor descriptor, CatalogObjectDataOutput output) throws IOException {
             output.writeByte(descriptor.indexType().id());
 
             serializers.get(1, descriptor.typeId()).writeTo(descriptor, output);
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogVersionAwareSerializer.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogVersionAwareSerializer.java
index 3a7fe6e57c2..8697fad6936 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogVersionAwareSerializer.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogVersionAwareSerializer.java
@@ -18,8 +18,6 @@
 package org.apache.ignite.internal.catalog.storage.serialization;
 
 import java.io.IOException;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 
 /**
  * Decorator for {@link CatalogObjectSerializer} that adds a method to get the serializer version.
@@ -37,12 +35,12 @@ public class CatalogVersionAwareSerializer<T extends MarshallableEntry> implemen
     }
 
     @Override
-    public T readFrom(IgniteDataInput input) throws IOException {
+    public T readFrom(CatalogObjectDataInput input)throws IOException {
         return delegate.readFrom(input);
     }
 
     @Override
-    public void writeTo(T value, IgniteDataOutput output) throws IOException {
+    public void writeTo(T value, CatalogObjectDataOutput output) throws IOException {
         delegate.writeTo(value, output);
     }
 
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/UpdateLogMarshallerImpl.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/UpdateLogMarshallerImpl.java
index 5883d7a38e7..eb0d8a54c1d 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/UpdateLogMarshallerImpl.java
+++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/UpdateLogMarshallerImpl.java
@@ -20,8 +20,6 @@ package org.apache.ignite.internal.catalog.storage.serialization;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 
 import org.apache.ignite.internal.catalog.storage.UpdateLogEvent;
-import org.apache.ignite.internal.util.io.IgniteUnsafeDataInput;
-import org.apache.ignite.internal.util.io.IgniteUnsafeDataOutput;
 import org.jetbrains.annotations.TestOnly;
 
 /**
@@ -58,32 +56,37 @@ import org.jetbrains.annotations.TestOnly;
  */
 public class UpdateLogMarshallerImpl implements UpdateLogMarshaller {
     /** Current data format version. */
-    private static final int PROTOCOL_VERSION = 1;
-
-    /** Initial capacity (in bytes) of the buffer used for data output. */
-    private static final int INITIAL_BUFFER_CAPACITY = 256;
+    private static final int PROTOCOL_VERSION = 2;
 
     /** Serializers provider. */
     private final CatalogEntrySerializerProvider serializers;
 
-    public UpdateLogMarshallerImpl() {
-        this.serializers = CatalogEntrySerializerProvider.DEFAULT_PROVIDER;
-    }
+    private final int protocolVersion;
 
     @TestOnly
     public UpdateLogMarshallerImpl(CatalogEntrySerializerProvider serializers) {
+        this(serializers, PROTOCOL_VERSION);
+    }
+
+    public UpdateLogMarshallerImpl(int protocolVersion) {
+        this(CatalogEntrySerializerProvider.DEFAULT_PROVIDER, protocolVersion);
+    }
+
+    public UpdateLogMarshallerImpl(CatalogEntrySerializerProvider serializers, int protocolVersion) {
         this.serializers = serializers;
+        this.protocolVersion = protocolVersion;
     }
 
     @Override
     public byte[] marshall(UpdateLogEvent update) {
-        try (IgniteUnsafeDataOutput output = new IgniteUnsafeDataOutput(INITIAL_BUFFER_CAPACITY)) {
-            output.writeShort(PROTOCOL_VERSION);
-
-            output.writeShort(update.typeId());
-
-            serializers.get(1, update.typeId()).writeTo(update, output);
-
+        try (CatalogObjectDataOutput output = new CatalogObjectDataOutput(serializers)) {
+            output.writeShort(protocolVersion);
+            if (protocolVersion == 1) {
+                output.writeShort(update.typeId());
+                serializers.get(1, update.typeId()).writeTo(update, output);
+            } else {
+                output.writeEntry(update);
+            }
             return output.array();
         } catch (Throwable t) {
             throw new CatalogMarshallerException(t);
@@ -92,18 +95,20 @@ public class UpdateLogMarshallerImpl implements UpdateLogMarshaller {
 
     @Override
     public UpdateLogEvent unmarshall(byte[] bytes) {
-        try (IgniteUnsafeDataInput input = new IgniteUnsafeDataInput(bytes)) {
+        try (CatalogObjectDataInput input = new CatalogObjectDataInput(serializers, bytes)) {
             int protoVersion = input.readShort();
 
             switch (protoVersion) {
-                case 1:
+                case 1: {
                     int typeId = input.readShort();
-
                     return (UpdateLogEvent) serializers.get(1, typeId).readFrom(input);
-
+                }
+                case 2: {
+                    return (UpdateLogEvent) input.readEntry();
+                }
                 default:
                     throw new IllegalStateException(format("An object could not be deserialized because it was using a newer"
-                            + " version of the serialization protocol [supported={}, actual={}].", PROTOCOL_VERSION, protoVersion));
+                            + " version of the serialization protocol [supported={}, actual={}].", protocolVersion, protoVersion));
             }
         } catch (Throwable t) {
             throw new CatalogMarshallerException(t);
diff --git a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
index 45b2f9b4d8d..35494576e86 100644
--- a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
@@ -29,6 +29,7 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Base64;
 import java.util.List;
 import java.util.Random;
@@ -57,6 +58,8 @@ import org.apache.ignite.internal.catalog.descriptors.CatalogTableSchemaVersions
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.ConsistencyMode;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntry;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
 import org.apache.ignite.internal.catalog.storage.serialization.UpdateLogMarshaller;
@@ -73,8 +76,6 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.EnumSource;
-import org.junit.jupiter.params.provider.EnumSource.Mode;
 import org.junit.jupiter.params.provider.MethodSource;
 
 /**
@@ -85,16 +86,24 @@ public class CatalogEntrySerializationTest extends BaseIgniteAbstractTest {
 
     private static final Random RND = new Random(SEED);
 
-    private final UpdateLogMarshallerImpl marshaller = new UpdateLogMarshallerImpl();
+    private UpdateLogMarshallerImpl marshaller;
 
     @BeforeEach
     public void setup() {
         log.info("Seed: {}", SEED);
     }
 
+    private static Stream<Arguments> marshallableEntryTypes() {
+        return Arrays.stream(MarshallableEntryType.values())
+                .filter(t -> t != MarshallableEntryType.VERSIONED_UPDATE)
+                .flatMap(t -> Stream.of(Arguments.of(t, 1), Arguments.of(t, 2)));
+    }
+
     @ParameterizedTest
-    @EnumSource(value = MarshallableEntryType.class, names = "VERSIONED_UPDATE", mode = Mode.EXCLUDE)
-    void test(MarshallableEntryType type) {
+    @MethodSource("marshallableEntryTypes")
+    void test(MarshallableEntryType type, int version) {
+        marshaller = new UpdateLogMarshallerImpl(version);
+
         switch (type) {
             case ALTER_COLUMN:
                 checkAlterColumnEntry();
@@ -473,13 +482,13 @@ public class CatalogEntrySerializationTest extends BaseIgniteAbstractTest {
             for (int v = 1; v <= serializers.latestSerializerVersion(entry.typeId()); v++) {
                 byte[] bytes;
 
-                try (IgniteUnsafeDataOutput output = new IgniteUnsafeDataOutput(64)) {
+                try (CatalogObjectDataOutput output = new CatalogObjectDataOutput(serializers)) {
                     serializers.get(v, entry.typeId()).writeTo(entry, output);
 
                     bytes = output.array();
                 }
 
-                try (IgniteUnsafeDataInput input = new IgniteUnsafeDataInput(bytes)) {
+                try (CatalogObjectDataInput input = new CatalogObjectDataInput(serializers, bytes)) {
                     MarshallableEntry deserialized = serializers.get(v, entry.typeId()).readFrom(input);
 
                     BDDAssertions.assertThat(entry)
diff --git a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityTest.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityTest.java
index 1f9da8d6a79..c3b19da6217 100644
--- a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityTest.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.catalog.storage;
 
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
@@ -32,9 +33,14 @@ import java.util.stream.Collectors;
 import org.apache.ignite.internal.catalog.Catalog;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
+import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntry;
+import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
 import org.apache.ignite.internal.catalog.storage.serialization.UpdateLogMarshallerImpl;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.assertj.core.api.BDDAssertions;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -56,11 +62,9 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 zones.get(0).id()
         );
 
-        SnapshotEntry expectedEntry = new SnapshotEntry(catalog1);
-        SnapshotEntry actualEntry = checkEntry(SnapshotEntry.class, "SnapshotEntry_1.bin");
+        SnapshotEntry snapshotEntry = new SnapshotEntry(catalog1);
 
-        assertEquals(expectedEntry.typeId(), actualEntry.typeId());
-        BDDAssertions.assertThat(expectedEntry.snapshot()).usingRecursiveComparison().isEqualTo(actualEntry.snapshot());
+        compareSnapshotEntry(snapshotEntry, "SnapshotEntry", entryVersion(MarshallableEntryType.SNAPSHOT));
     }
 
     @Test
@@ -74,25 +78,16 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 null
         );
 
-        SnapshotEntry expectedEntry = new SnapshotEntry(catalog1);
-        SnapshotEntry actualEntry = checkEntry(SnapshotEntry.class, "SnapshotEntryNoDefaultZone_1.bin");
+        SnapshotEntry snapshotEntry = new SnapshotEntry(catalog1);
 
-        assertEquals(expectedEntry.typeId(), actualEntry.typeId());
-        BDDAssertions.assertThat(expectedEntry.snapshot()).usingRecursiveComparison().isEqualTo(actualEntry.snapshot());
+        compareSnapshotEntry(snapshotEntry, "SnapshotEntryNoDefaultZone", entryVersion(MarshallableEntryType.SNAPSHOT));
     }
 
     @Test
     public void objectIdUpdate() {
         List<UpdateEntry> entries = List.of(new ObjectIdGenUpdateEntry(23431), new ObjectIdGenUpdateEntry(1204));
-        List<UpdateEntry> actual = checkEntries(entries, "ObjectIdGenUpdateEntry_1.bin");
-
-        assertEquals(entries.size(), actual.size());
-        for (int i = 0; i < entries.size(); i++) {
-            ObjectIdGenUpdateEntry expectedEntry = (ObjectIdGenUpdateEntry) entries.get(i);
-            ObjectIdGenUpdateEntry actualEntry = (ObjectIdGenUpdateEntry) actual.get(i);
 
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "ObjectIdGenUpdateEntry", entryVersion(MarshallableEntryType.ID_GENERATOR));
     }
 
     // Zones
@@ -101,15 +96,8 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
     public void newZone() {
         List<CatalogZoneDescriptor> zones = TestCatalogObjectDescriptors.zones(state);
         List<UpdateEntry> entries = zones.stream().map(NewZoneEntry::new).collect(Collectors.toList());
-        List<UpdateEntry> actual = checkEntries(entries, "NewZoneEntry_1.bin");
 
-        assertEquals(entries.size(), actual.size());
-        for (int i = 0; i < entries.size(); i++) {
-            NewZoneEntry expectedEntry = (NewZoneEntry) entries.get(i);
-            NewZoneEntry actualEntry = (NewZoneEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "NewZoneEntry", entryVersion(MarshallableEntryType.NEW_ZONE));
     }
 
     @Test
@@ -120,14 +108,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new AlterZoneEntry(zones.get(2))
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "AlterZoneEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-        for (int i = 0; i < entries.size(); i++) {
-            AlterZoneEntry expectedEntry = (AlterZoneEntry) entries.get(i);
-            AlterZoneEntry actualEntry = (AlterZoneEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "AlterZoneEntry", entryVersion(MarshallableEntryType.ALTER_ZONE));
     }
 
     @Test
@@ -136,15 +117,8 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new SetDefaultZoneEntry(state.id()),
                 new SetDefaultZoneEntry(state.id())
         );
-        List<UpdateEntry> actual = checkEntries(entries, "SetDefaultZoneEntry_1.bin");
 
-        assertEquals(entries.size(), actual.size());
-        for (int i = 0; i < entries.size(); i++) {
-            SetDefaultZoneEntry expectedEntry = (SetDefaultZoneEntry) entries.get(i);
-            SetDefaultZoneEntry actualEntry = (SetDefaultZoneEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "SetDefaultZoneEntry", entryVersion(MarshallableEntryType.SET_DEFAULT_ZONE));
     }
 
     @Test
@@ -154,15 +128,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new DropZoneEntry(state.id())
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "DropZoneEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            DropZoneEntry expectedEntry = (DropZoneEntry) entries.get(i);
-            DropZoneEntry actualEntry = (DropZoneEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "DropZoneEntry", entryVersion(MarshallableEntryType.DROP_ZONE));
     }
 
     // Schemas
@@ -174,15 +140,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 .map(NewSchemaEntry::new)
                 .collect(Collectors.toList());
 
-        List<UpdateEntry> actual = checkEntries(entries, "NewSchemaEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            NewSchemaEntry expectedEntry = (NewSchemaEntry) entries.get(i);
-            NewSchemaEntry actualEntry = (NewSchemaEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "NewSchemaEntry", entryVersion(MarshallableEntryType.NEW_SCHEMA));
     }
 
     @Test
@@ -192,15 +150,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new DropSchemaEntry(state.id())
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "DropSchemaEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            DropSchemaEntry expectedEntry = (DropSchemaEntry) entries.get(i);
-            DropSchemaEntry actualEntry = (DropSchemaEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "DropSchemaEntry", entryVersion(MarshallableEntryType.DROP_SCHEMA));
     }
 
     // Tables
@@ -212,15 +162,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 .map(NewTableEntry::new)
                 .collect(Collectors.toList());
 
-        List<UpdateEntry> actual = checkEntries(entries, "NewTableEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            NewTableEntry expectedEntry = (NewTableEntry) entries.get(i);
-            NewTableEntry actualEntry = (NewTableEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "NewTableEntry", entryVersion(MarshallableEntryType.NEW_TABLE));
     }
 
     @Test
@@ -230,15 +172,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new RenameTableEntry(state.id(), "NEW_NAME2")
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "RenameTableEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            RenameTableEntry expectedEntry = (RenameTableEntry) entries.get(i);
-            RenameTableEntry actualEntry = (RenameTableEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "RenameTableEntry", entryVersion(MarshallableEntryType.RENAME_TABLE));
     }
 
     @Test
@@ -248,15 +182,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new DropTableEntry(state.id())
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "DropTableEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            DropTableEntry expectedEntry = (DropTableEntry) entries.get(i);
-            DropTableEntry actualEntry = (DropTableEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "DropTableEntry", entryVersion(MarshallableEntryType.DROP_TABLE));
     }
 
     // Columns
@@ -280,30 +206,14 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
 
         Collections.shuffle(entries, state.random());
 
-        List<UpdateEntry> actual = checkEntries(entries, "NewIndexEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            NewIndexEntry expectedEntry = (NewIndexEntry) entries.get(i);
-            NewIndexEntry actualEntry = (NewIndexEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "NewIndexEntry", entryVersion(MarshallableEntryType.NEW_INDEX));
     }
 
     @Test
     public void renameIndex() {
         List<UpdateEntry> entries = List.of(new RenameIndexEntry(state.id(), "NEW_NAME"));
 
-        List<UpdateEntry> actual = checkEntries(entries, "RenameIndexEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            RenameIndexEntry expectedEntry = (RenameIndexEntry) entries.get(i);
-            RenameIndexEntry actualEntry = (RenameIndexEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "RenameIndexEntry", entryVersion(MarshallableEntryType.RENAME_INDEX));
     }
 
     @Test
@@ -313,15 +223,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new RemoveIndexEntry(state.id())
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "RemoveIndexEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            RemoveIndexEntry expectedEntry = (RemoveIndexEntry) entries.get(i);
-            RemoveIndexEntry actualEntry = (RemoveIndexEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "RemoveIndexEntry", entryVersion(MarshallableEntryType.REMOVE_INDEX));
     }
 
     @Test
@@ -331,15 +233,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new MakeIndexAvailableEntry(state.id())
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "MakeIndexAvailableEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            MakeIndexAvailableEntry expectedEntry = (MakeIndexAvailableEntry) entries.get(i);
-            MakeIndexAvailableEntry actualEntry = (MakeIndexAvailableEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "MakeIndexAvailableEntry", entryVersion(MarshallableEntryType.MAKE_INDEX_AVAILABLE));
     }
 
     @Test
@@ -349,15 +243,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new StartBuildingIndexEntry(state.id())
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "StartBuildingIndexEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            StartBuildingIndexEntry expectedEntry = (StartBuildingIndexEntry) entries.get(i);
-            StartBuildingIndexEntry actualEntry = (StartBuildingIndexEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "StartBuildingIndexEntry", entryVersion(MarshallableEntryType.START_BUILDING_INDEX));
     }
 
     @Test
@@ -367,15 +253,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new DropIndexEntry(state.id())
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "DropIndexEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            DropIndexEntry expectedEntry = (DropIndexEntry) entries.get(i);
-            DropIndexEntry actualEntry = (DropIndexEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "DropIndexEntry", entryVersion(MarshallableEntryType.DROP_INDEX));
     }
 
     // Columns
@@ -393,15 +271,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new NewColumnsEntry(state.id(), columns2)
         );
 
-        List<UpdateEntry> actual = checkEntries(entries, "NewColumnsEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            NewColumnsEntry expectedEntry = (NewColumnsEntry) entries.get(i);
-            NewColumnsEntry actualEntry = (NewColumnsEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "NewColumnsEntry", entryVersion(MarshallableEntryType.NEW_COLUMN));
     }
 
     @Test
@@ -413,16 +283,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new AlterColumnEntry(state.id(), columns.get(0)),
                 new AlterColumnEntry(state.id(), columns.get(1))
         );
-
-        List<UpdateEntry> actual = checkEntries(entries, "AlterColumnsEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            AlterColumnEntry expectedEntry = (AlterColumnEntry) entries.get(i);
-            AlterColumnEntry actualEntry = (AlterColumnEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "AlterColumnsEntry", entryVersion(MarshallableEntryType.ALTER_COLUMN));
     }
 
     @Test
@@ -437,16 +298,7 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 new DropColumnsEntry(state.id(), Set.of("C1", "C2")),
                 new DropColumnsEntry(state.id(), Set.of("C3"))
         );
-
-        List<UpdateEntry> actual = checkEntries(entries, "DropColumnsEntry_1.bin");
-        assertEquals(entries.size(), actual.size());
-
-        for (int i = 0; i < entries.size(); i++) {
-            DropColumnsEntry expectedEntry = (DropColumnsEntry) entries.get(i);
-            DropColumnsEntry actualEntry = (DropColumnsEntry) actual.get(i);
-
-            BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
-        }
+        compareEntries(entries, "DropColumnsEntry", entryVersion(MarshallableEntryType.DROP_COLUMN));
     }
 
     // System views
@@ -458,21 +310,36 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
                 .map(NewSystemViewEntry::new)
                 .collect(Collectors.toList());
 
-        List<UpdateEntry> actual = checkEntries(entries, "NewSystemViewEntry_1.bin");
+        compareEntries(entries, "NewSystemViewEntry", entryVersion(MarshallableEntryType.NEW_SYS_VIEW));
+    }
+
+    protected void compareSnapshotEntry(SnapshotEntry expectedEntry, String fileName, int version) {
+        SnapshotEntry actualEntry = checkEntry(SnapshotEntry.class, fileName, version);
+
+        assertEquals(expectedEntry.typeId(), actualEntry.typeId());
+        assertEquals(expectedEntry.activationTime(), actualEntry.activationTime(), "activationTime");
+        assertEquals(expectedEntry.objectIdGenState(), actualEntry.objectIdGenState(), "objectIdGenState");
+        assertEquals(expectedEntry.defaultZoneId(), actualEntry.defaultZoneId(), "defaultZoneId");
+
+        BDDAssertions.assertThat(expectedEntry.snapshot()).usingRecursiveComparison().isEqualTo(actualEntry.snapshot());
+    }
+
+    protected void compareEntries(List<UpdateEntry> entries, String fileName, int version) {
+        List<UpdateEntry> actual = checkEntries(entries, fileName, version);
         assertEquals(entries.size(), actual.size());
 
         for (int i = 0; i < entries.size(); i++) {
-            NewSystemViewEntry expectedEntry = (NewSystemViewEntry) entries.get(i);
-            NewSystemViewEntry actualEntry = (NewSystemViewEntry) actual.get(i);
+            UpdateEntry expectedEntry = entries.get(i);
+            UpdateEntry actualEntry = actual.get(i);
 
             BDDAssertions.assertThat(actualEntry).as("entry#" + i).usingRecursiveComparison().isEqualTo(expectedEntry);
         }
     }
 
     @SuppressWarnings({"unchecked", "rawtypes"})
-    private <T extends UpdateEntry> List<T> checkEntries(List<T> entries, String fileName) {
+    private <T extends UpdateEntry> List<T> checkEntries(List<? extends T> entries, String fileName, int version) {
         VersionedUpdate update = new VersionedUpdate(1, 100L, (List<UpdateEntry>) entries);
-        VersionedUpdate deserializedUpdate = checkEntry(VersionedUpdate.class, fileName);
+        VersionedUpdate deserializedUpdate = checkEntry(VersionedUpdate.class, fileName, version);
 
         assertEquals(update.version(), deserializedUpdate.version());
         assertEquals(update.typeId(), deserializedUpdate.typeId());
@@ -481,10 +348,36 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
         return (List) deserializedUpdate.entries();
     }
 
-    private <T extends UpdateLogEvent> T checkEntry(Class<T> entryClass, String fileName) {
-        String resourceName = "serialization_v1/" + fileName;
+    protected int protocolVersion() {
+        return 1;
+    }
 
-        UpdateLogMarshallerImpl marshaller = new UpdateLogMarshallerImpl();
+    protected int entryVersion(MarshallableEntryType entryType) {
+        return 1;
+    }
+
+    protected String dirName() {
+        return "serialization_v1";
+    }
+
+    protected boolean expectExactVersion() {
+        return false;
+    }
+
+    private <T extends UpdateLogEvent> T checkEntry(Class<T> entryClass, String entryFileName, int version) {
+        String fileName = format("{}_{}.bin", entryFileName, version);
+        String resourceName = dirName() + "/" + fileName;
+
+        CatalogEntrySerializerProvider provider;
+        if (expectExactVersion()) {
+            provider = new VersionCheckingProvider(protocolVersion());
+        } else {
+            provider = CatalogEntrySerializerProvider.DEFAULT_PROVIDER;
+        }
+
+        log.info("Read fileName: {}, class: {}, version: {}", fileName, entryClass.getSimpleName(), version);
+
+        UpdateLogMarshallerImpl marshaller = new UpdateLogMarshallerImpl(provider, protocolVersion());
 
         byte[] srcBytes;
 
@@ -501,4 +394,38 @@ public class CatalogSerializationCompatibilityTest extends BaseIgniteAbstractTes
 
         return entryClass.cast(marshaller.unmarshall(srcBytes));
     }
+
+    private static class VersionCheckingProvider implements CatalogEntrySerializerProvider {
+
+        private final CatalogEntrySerializerProvider provider;
+
+        private final int expected;
+
+        private VersionCheckingProvider(int expected) {
+            this.provider = DEFAULT_PROVIDER;
+            this.expected = expected;
+        }
+
+        @Override
+        public <T extends MarshallableEntry> CatalogObjectSerializer<T> get(int version, int typeId) {
+            CatalogObjectSerializer<MarshallableEntry> serializer = provider.get(version, typeId);
+
+            checkVersion(typeId, version);
+
+            return (CatalogObjectSerializer<T>) serializer;
+        }
+
+        @Override
+        public int latestSerializerVersion(int typeId) {
+            int latest = provider.latestSerializerVersion(typeId);
+            checkVersion(typeId, latest);
+            return latest;
+        }
+
+        private void checkVersion(int typeId, int version) {
+            if (version != expected) {
+                Assertions.fail("Requested unexpected version for type " + typeId + ". All versions must be " + expected);
+            }
+        }
+    }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV1ReadsV1Test.java
similarity index 56%
copy from modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
copy to modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV1ReadsV1Test.java
index 37e73431a67..44c7885bd75 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV1ReadsV1Test.java
@@ -15,20 +15,27 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.storage.serialization;
+package org.apache.ignite.internal.catalog.storage;
 
-import org.apache.ignite.internal.lang.IgniteInternalException;
-import org.apache.ignite.lang.ErrorGroups.Common;
-import org.jetbrains.annotations.Nullable;
+import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
 
 /**
- * This exception is caused by a failure to marshall or unmarshall catalog event.
- *  The failure can be due to compatibility reason or any other unrecoverable error.
+ * Tests for catalog storage objects. Protocol version 1 read v1.
  */
-public class CatalogMarshallerException extends IgniteInternalException {
-    private static final long serialVersionUID = 3401185592041257347L;
+public class CatalogSerializationCompatibilityV1ReadsV1Test extends CatalogSerializationCompatibilityTest {
 
-    CatalogMarshallerException(@Nullable Throwable cause) {
-        super(Common.INTERNAL_ERR, cause);
+    @Override
+    protected int protocolVersion() {
+        return 1;
+    }
+
+    @Override
+    protected int entryVersion(MarshallableEntryType entryType) {
+        return 1;
+    }
+
+    @Override
+    protected boolean expectExactVersion() {
+        return true;
     }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV1Test.java
similarity index 54%
copy from modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
copy to modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV1Test.java
index 37e73431a67..efe467226ab 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV1Test.java
@@ -15,20 +15,32 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.storage.serialization;
+package org.apache.ignite.internal.catalog.storage;
 
-import org.apache.ignite.internal.lang.IgniteInternalException;
-import org.apache.ignite.lang.ErrorGroups.Common;
-import org.jetbrains.annotations.Nullable;
+import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
 
 /**
- * This exception is caused by a failure to marshall or unmarshall catalog event.
- *  The failure can be due to compatibility reason or any other unrecoverable error.
+ * Tests for catalog storage objects. Protocol version 2 reads protocol 1.
  */
-public class CatalogMarshallerException extends IgniteInternalException {
-    private static final long serialVersionUID = 3401185592041257347L;
+public class CatalogSerializationCompatibilityV2ReadsV1Test extends CatalogSerializationCompatibilityTest {
 
-    CatalogMarshallerException(@Nullable Throwable cause) {
-        super(Common.INTERNAL_ERR, cause);
+    @Override
+    protected int protocolVersion() {
+        return 2;
+    }
+
+    @Override
+    protected String dirName() {
+        return "serialization_v1";
+    }
+
+    @Override
+    protected int entryVersion(MarshallableEntryType entryType) {
+        return 1;
+    }
+
+    @Override
+    protected boolean expectExactVersion() {
+        return false;
     }
 }
diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV2Test.java
similarity index 54%
copy from modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
copy to modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV2Test.java
index 37e73431a67..897fbaeda4e 100644
--- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogMarshallerException.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogSerializationCompatibilityV2ReadsV2Test.java
@@ -15,20 +15,32 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.catalog.storage.serialization;
+package org.apache.ignite.internal.catalog.storage;
 
-import org.apache.ignite.internal.lang.IgniteInternalException;
-import org.apache.ignite.lang.ErrorGroups.Common;
-import org.jetbrains.annotations.Nullable;
+import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
 
 /**
- * This exception is caused by a failure to marshall or unmarshall catalog event.
- *  The failure can be due to compatibility reason or any other unrecoverable error.
+ * Tests for catalog storage objects. Protocol version 2 reads protocol 2.
  */
-public class CatalogMarshallerException extends IgniteInternalException {
-    private static final long serialVersionUID = 3401185592041257347L;
+public class CatalogSerializationCompatibilityV2ReadsV2Test extends CatalogSerializationCompatibilityTest {
 
-    CatalogMarshallerException(@Nullable Throwable cause) {
-        super(Common.INTERNAL_ERR, cause);
+    @Override
+    protected int protocolVersion() {
+        return 2;
+    }
+
+    @Override
+    protected int entryVersion(MarshallableEntryType entryType) {
+        return 2;
+    }
+
+    @Override
+    protected String dirName() {
+        return "serialization_v2";
+    }
+
+    @Override
+    protected boolean expectExactVersion() {
+        return true;
     }
 }
diff --git a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/UpdateLogImplTest.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/UpdateLogImplTest.java
index 19a85f8a355..858e83e2eb7 100644
--- a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/UpdateLogImplTest.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/UpdateLogImplTest.java
@@ -44,6 +44,8 @@ import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.catalog.storage.UpdateLog.OnUpdateHandler;
 import org.apache.ignite.internal.catalog.storage.VersionedUpdateSerializers.VersionedUpdateSerializerV1;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogEntrySerializerProvider;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataInput;
+import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectDataOutput;
 import org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntry;
 import org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
@@ -59,8 +61,6 @@ import org.apache.ignite.internal.metastorage.server.SimpleInMemoryKeyValueStora
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.apache.ignite.internal.tostring.S;
 import org.apache.ignite.internal.util.ByteUtils;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -415,13 +415,13 @@ class UpdateLogImplTest extends BaseIgniteAbstractTest {
 
     private static class TestEntrySerializerProvider implements CatalogEntrySerializerProvider {
         @Override
-        public CatalogObjectSerializer<MarshallableEntry> get(int version, int id) {
+        public CatalogObjectSerializer<MarshallableEntry> get(int versionNotUsed, int id) {
             CatalogObjectSerializer<? extends MarshallableEntry> serializer;
 
             if (id < 0) {
                 serializer = new CatalogObjectSerializer<>() {
                     @Override
-                    public MarshallableEntry readFrom(IgniteDataInput input) throws IOException {
+                    public MarshallableEntry readFrom(CatalogObjectDataInput input) throws IOException {
                         int length = input.readVarIntAsInt();
                         byte[] data = input.readByteArray(length);
 
@@ -429,7 +429,7 @@ class UpdateLogImplTest extends BaseIgniteAbstractTest {
                     }
 
                     @Override
-                    public void writeTo(MarshallableEntry value, IgniteDataOutput output) throws IOException {
+                    public void writeTo(MarshallableEntry value, CatalogObjectDataOutput output) throws IOException {
                         byte[] bytes = ByteUtils.toBytes(value);
 
                         output.writeVarInt(bytes.length);
@@ -439,7 +439,7 @@ class UpdateLogImplTest extends BaseIgniteAbstractTest {
             } else if (id == MarshallableEntryType.VERSIONED_UPDATE.id()) {
                 serializer = new VersionedUpdateSerializerV1(this);
             } else {
-                serializer = CatalogEntrySerializerProvider.DEFAULT_PROVIDER.get(1, id);
+                serializer = CatalogEntrySerializerProvider.DEFAULT_PROVIDER.get(2, id);
             }
 
             return (CatalogObjectSerializer<MarshallableEntry>) serializer;
@@ -447,7 +447,7 @@ class UpdateLogImplTest extends BaseIgniteAbstractTest {
 
         @Override
         public int latestSerializerVersion(int typeId) {
-            throw new UnsupportedOperationException();
+            return 2;
         }
     }
 }
diff --git a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogDataInputOutputTest.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogDataInputOutputTest.java
new file mode 100644
index 00000000000..fecc320a185
--- /dev/null
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogDataInputOutputTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.catalog.storage.serialization;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Random;
+import java.util.function.IntFunction;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link CatalogObjectDataOutput} / {@link CatalogObjectDataInput}.
+ */
+public class CatalogDataInputOutputTest extends BaseIgniteAbstractTest {
+
+    private final TestCatalogEntrySerializerProvider provider = new TestCatalogEntrySerializerProvider();
+
+    private final Random random = new Random();
+
+    @Test
+    public void testReadWriteEntryList() throws IOException {
+        try (CatalogObjectDataOutput output = new CatalogObjectDataOutput(provider)) {
+            TestEntryVar1 entry1 = new TestEntryVar1(random.nextInt());
+            TestEntryVar2 entry2 = new TestEntryVar2(random.nextInt());
+
+            output.writeEntry(entry1);
+            output.writeEntry(entry2);
+
+            byte[] data = output.array();
+            try (CatalogObjectDataInput input = new CatalogObjectDataInput(provider, data)) {
+                TestEntryVar1 actual1 = input.readEntry(TestEntryVar1.class);
+                TestEntryVar2 actual2 = input.readEntry(TestEntryVar2.class);
+
+                expectEntries(List.of(entry1, entry2), List.of(actual1, actual2));
+            }
+        }
+    }
+
+    @Test
+    public void testReadWriteCompactEntryList() throws IOException {
+        try (CatalogObjectDataOutput output = new CatalogObjectDataOutput(provider)) {
+            TestEntryVar1 entry1 = new TestEntryVar1(random.nextInt());
+            TestEntryVar1 entry2 = new TestEntryVar1(random.nextInt());
+
+            output.writeCompactEntryList(List.of(entry1, entry2));
+
+            byte[] data = output.array();
+            try (CatalogObjectDataInput input = new CatalogObjectDataInput(provider, data)) {
+                List<BaseTestEntry> list = input.readCompactEntryList(BaseTestEntry.class);
+
+                expectEntries(List.of(entry1, entry2), list);
+            }
+        }
+    }
+
+    private static void expectEntries(List<? extends BaseTestEntry> expected, List<? extends BaseTestEntry> actual) {
+        assertEquals(expected.size(), actual.size());
+
+        for (int i = 0; i < expected.size(); i++) {
+            BaseTestEntry e = expected.get(i);
+            BaseTestEntry a = actual.get(i);
+            assertEquals(e.value, a.value, "value entry #" + i);
+            assertEquals(e.typeId(), a.typeId(), "typeId entry #" + i);
+        }
+    }
+
+    @Test
+    public void testReadWriteLatestEntry() throws Exception {
+        try (CatalogObjectDataOutput output = new CatalogObjectDataOutput(provider)) {
+            TestEntryVar1 entry1 = new TestEntryVar1(random.nextInt());
+            TestEntryVar2 entry2 = new TestEntryVar2(random.nextInt());
+
+            output.writeEntry(entry1);
+            output.writeEntry(entry2);
+
+            byte[] data = output.array();
+            try (CatalogObjectDataInput input = new CatalogObjectDataInput(provider, data)) {
+                TestEntryVar1 actual1 = (TestEntryVar1) input.readEntry();
+                TestEntryVar2 actual2 = (TestEntryVar2) input.readEntry();
+
+                expectEntries(List.of(entry1, entry2), List.of(actual1, actual2));
+            }
+        }
+    }
+
+    @Test
+    public void testReadWriteObjectList() throws IOException {
+        try (CatalogObjectDataOutput output = new CatalogObjectDataOutput(provider)) {
+            List<Map.Entry<Integer, String>> list = List.of(Map.entry(1, "42"), Map.entry(2, "109"));
+
+            output.writeObjectCollection((out, element) -> {
+                out.writeInt(element.getKey());
+                out.writeUTF(element.getValue());
+            }, list);
+
+            byte[] data = output.array();
+            try (CatalogObjectDataInput input = new CatalogObjectDataInput(provider, data)) {
+                List<Map.Entry<Integer, String>> actual = input.readObjectCollection(
+                        in -> Map.entry(in.readInt(), in.readUTF()), ArrayList::new);
+                assertEquals(list, actual);
+            }
+        }
+    }
+
+    private static class SimpleTestEntrySerializer implements CatalogObjectSerializer<BaseTestEntry> {
+
+        private final IntFunction<BaseTestEntry> newEntry;
+
+        private SimpleTestEntrySerializer(IntFunction<BaseTestEntry> newEntry) {
+            this.newEntry = newEntry;
+        }
+
+        @Override
+        public BaseTestEntry readFrom(CatalogObjectDataInput input) throws IOException {
+            return newEntry.apply(input.readVarIntAsInt());
+        }
+
+        @Override
+        public void writeTo(BaseTestEntry value, CatalogObjectDataOutput output) throws IOException {
+            output.writeVarInt(value.value);
+        }
+    }
+
+    private static class TestCatalogEntrySerializerProvider implements CatalogEntrySerializerProvider {
+
+        private final Map<Map.Entry<Integer, Integer>, CatalogObjectSerializer<?>> serializerMap = new HashMap<>();
+
+        TestCatalogEntrySerializerProvider() {
+            serializerMap.put(Map.entry(TestEntryVar1.TYPE_ID, 2), new SimpleTestEntrySerializer(TestEntryVar1::new));
+            serializerMap.put(Map.entry(TestEntryVar2.TYPE_ID, 3), new SimpleTestEntrySerializer(TestEntryVar2::new));
+        }
+
+        @Override
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public <T extends MarshallableEntry> CatalogObjectSerializer<T> get(int version, int typeId) {
+            Entry<Integer, Integer> versionKey = Map.entry(typeId, version);
+            CatalogObjectSerializer serializer = serializerMap.get(versionKey);
+
+            Objects.requireNonNull(serializer, "No serializer for " + versionKey + " available: " + serializerMap.keySet());
+            return serializer;
+        }
+
+        @Override
+        public int latestSerializerVersion(int typeId) {
+            for (var versionKey : serializerMap.keySet()) {
+                if (Objects.equals(typeId, versionKey.getKey())) {
+                    return versionKey.getValue();
+                }
+            }
+            throw new IllegalStateException("Unexpected type: " + typeId);
+        }
+    }
+
+    private abstract static class BaseTestEntry implements MarshallableEntry {
+
+        final int value;
+
+        private BaseTestEntry(int value) {
+            this.value = value;
+        }
+    }
+
+    private static class TestEntryVar1 extends BaseTestEntry {
+
+        private static final int TYPE_ID = 41;
+
+        private TestEntryVar1(int val) {
+            super(val);
+        }
+
+        @Override
+        public int typeId() {
+            return TYPE_ID;
+        }
+    }
+
+    private static class TestEntryVar2 extends BaseTestEntry {
+
+        private static final int TYPE_ID = 42;
+
+        private TestEntryVar2(int val) {
+            super(val);
+        }
+
+        @Override
+        public int typeId() {
+            return TYPE_ID;
+        }
+    }
+}
diff --git a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImplTest.java b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImplTest.java
index 4e54846298c..bc30db77e89 100644
--- a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImplTest.java
+++ b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProviderImplTest.java
@@ -25,8 +25,6 @@ import static org.junit.jupiter.api.Named.named;
 import java.util.List;
 import java.util.stream.Stream;
 import org.apache.ignite.internal.testframework.IgniteTestUtils;
-import org.apache.ignite.internal.util.io.IgniteDataInput;
-import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -308,12 +306,12 @@ public class CatalogEntrySerializerProviderImplTest {
 
     static class DummySerializer implements CatalogObjectSerializer<MarshallableEntry> {
         @Override
-        public MarshallableEntry readFrom(IgniteDataInput input) {
+        public MarshallableEntry readFrom(CatalogObjectDataInput input) {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public void writeTo(MarshallableEntry value, IgniteDataOutput output) {
+        public void writeTo(MarshallableEntry value, CatalogObjectDataOutput output) {
             throw new UnsupportedOperationException();
         }
     }
diff --git a/modules/catalog/src/test/resources/serialization_v2/AlterColumnsEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/AlterColumnsEntry_2.bin
new file mode 100644
index 00000000000..1deb159fdd3
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/AlterColumnsEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/AlterZoneEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/AlterZoneEntry_2.bin
new file mode 100644
index 00000000000..ecb02567b11
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/AlterZoneEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/DropColumnsEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/DropColumnsEntry_2.bin
new file mode 100644
index 00000000000..4a6283c77c3
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/DropColumnsEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/DropIndexEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/DropIndexEntry_2.bin
new file mode 100644
index 00000000000..bd1b4046337
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/DropIndexEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/DropSchemaEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/DropSchemaEntry_2.bin
new file mode 100644
index 00000000000..6fa9f494603
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/DropSchemaEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/DropTableEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/DropTableEntry_2.bin
new file mode 100644
index 00000000000..fbae8200f57
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/DropTableEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/DropZoneEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/DropZoneEntry_2.bin
new file mode 100644
index 00000000000..bfadd897a6e
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/DropZoneEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/MakeIndexAvailableEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/MakeIndexAvailableEntry_2.bin
new file mode 100644
index 00000000000..0729ca3bb69
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/MakeIndexAvailableEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/NewColumnsEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/NewColumnsEntry_2.bin
new file mode 100644
index 00000000000..a7740913353
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/NewColumnsEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/NewIndexEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/NewIndexEntry_2.bin
new file mode 100644
index 00000000000..e533b172bdc
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/NewIndexEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/NewSchemaEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/NewSchemaEntry_2.bin
new file mode 100644
index 00000000000..098148e8710
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/NewSchemaEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/NewSystemViewEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/NewSystemViewEntry_2.bin
new file mode 100644
index 00000000000..271d6791e10
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/NewSystemViewEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/NewTableEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/NewTableEntry_2.bin
new file mode 100644
index 00000000000..a5e8521c204
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/NewTableEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/NewZoneEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/NewZoneEntry_2.bin
new file mode 100644
index 00000000000..f282b675eff
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/NewZoneEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/ObjectIdGenUpdateEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/ObjectIdGenUpdateEntry_2.bin
new file mode 100644
index 00000000000..24818409f45
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/ObjectIdGenUpdateEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/RemoveIndexEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/RemoveIndexEntry_2.bin
new file mode 100644
index 00000000000..fc7bd91a108
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/RemoveIndexEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/RenameIndexEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/RenameIndexEntry_2.bin
new file mode 100644
index 00000000000..399da4a1f2b
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/RenameIndexEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/RenameTableEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/RenameTableEntry_2.bin
new file mode 100644
index 00000000000..3ff7555c76d
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/RenameTableEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/SetDefaultZoneEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/SetDefaultZoneEntry_2.bin
new file mode 100644
index 00000000000..aee4f20654c
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/SetDefaultZoneEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/SnapshotEntryNoDefaultZone_2.bin b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntryNoDefaultZone_2.bin
new file mode 100644
index 00000000000..75735223e08
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntryNoDefaultZone_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/SnapshotEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntry_2.bin
new file mode 100644
index 00000000000..c26c740e64a
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/SnapshotEntry_2.bin differ
diff --git a/modules/catalog/src/test/resources/serialization_v2/StartBuildingIndexEntry_2.bin b/modules/catalog/src/test/resources/serialization_v2/StartBuildingIndexEntry_2.bin
new file mode 100644
index 00000000000..c0da54578d4
Binary files /dev/null and b/modules/catalog/src/test/resources/serialization_v2/StartBuildingIndexEntry_2.bin differ