From 67839c172ef2963974f95a1ef3baf62ce2971728 Mon Sep 17 00:00:00 2001 From: Teng Ma Date: Wed, 22 Apr 2026 20:49:52 +0800 Subject: [PATCH] [Store] Fix serializer backward compat: accept legacy 4-element Replica format The new tiered storage feature added a storage_level field to the Replica msgpack format, bumping the array size from 4 to 5. Existing snapshots and persisted replicas that were serialized with the old 4-element format would fail deserialization with a hard error. Accept both sizes: when size==4 (legacy), default storage_level to RAM and use payload_index=3; when size==5 (new), read storage_level and use payload_index=4. This preserves full read compatibility with pre-upgrade data. --- mooncake-store/src/serialize/serializer.cpp | 28 +++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/mooncake-store/src/serialize/serializer.cpp b/mooncake-store/src/serialize/serializer.cpp index fff481e2cb..8edda2b05d 100644 --- a/mooncake-store/src/serialize/serializer.cpp +++ b/mooncake-store/src/serialize/serializer.cpp @@ -737,14 +737,16 @@ auto Serializer::deserialize(const msgpack::object &obj, "data, expected array")); } - // Verify array size is correct (should have 5 elements: id, status, - // replica_type, storage_level, payload) - if (obj.via.array.size != 5) { + // Accept both legacy 4-element format [id, status, replica_type, payload] + // and new 5-element format [id, status, replica_type, storage_level, payload]. + // Legacy replicas default to StorageLevel::RAM. + const auto array_size = obj.via.array.size; + if (array_size != 4 && array_size != 5) { return tl::unexpected(SerializationError( ErrorCode::DESERIALIZE_FAIL, fmt::format("deserialize_msgpack Replica invalid array size: " - "expected 5, got {}", - obj.via.array.size))); + "expected 4 or 5, got {}", + array_size))); } auto *array_items = obj.via.array.ptr; @@ -758,8 +760,14 @@ auto Serializer::deserialize(const msgpack::object &obj, // 3. Deserialize replica_type auto replica_type_code = array_items[2].as(); - // 4. Deserialize storage_level - auto storage_level = static_cast(array_items[3].as()); + // 4. Deserialize storage_level (missing in legacy 4-element format → RAM) + StorageLevel storage_level = StorageLevel::RAM; + size_t payload_index = 3; + if (array_size == 5) { + storage_level = + static_cast(array_items[3].as()); + payload_index = 4; + } // 5. Parse payload by type std::shared_ptr replica; @@ -767,7 +775,7 @@ auto Serializer::deserialize(const msgpack::object &obj, case static_cast(ReplicaType::MEMORY): { // MEMORY: payload is AllocatedBuffer auto buffer_result = Serializer::deserialize( - array_items[4], segment_view); + array_items[payload_index], segment_view); if (!buffer_result) { return tl::unexpected(buffer_result.error()); } @@ -776,7 +784,7 @@ auto Serializer::deserialize(const msgpack::object &obj, break; } case static_cast(ReplicaType::DISK): { - const auto &payload = array_items[4]; + const auto &payload = array_items[payload_index]; if (payload.type != msgpack::type::ARRAY || payload.via.array.size != 2) { return tl::unexpected( @@ -793,7 +801,7 @@ auto Serializer::deserialize(const msgpack::object &obj, break; } case static_cast(ReplicaType::LOCAL_DISK): { - const auto &payload = array_items[4]; + const auto &payload = array_items[payload_index]; if (payload.type != msgpack::type::ARRAY || payload.via.array.size != 3) { return tl::unexpected(