From 64cbee28ca79e10adea93cca26a9e66658c473aa Mon Sep 17 00:00:00 2001 From: egezon berisha Date: Fri, 21 Aug 2026 11:40:38 +0200 Subject: [PATCH] Fix xbcloud put out-of-order chunk failure on sparse chunks put_func advanced the expected per-file offset by chunk.length only, ignoring the sparse map, and skipped offset/checksum validation for XB_CHUNK_TYPE_SPARSE chunks. Streaming any tablespace that uses InnoDB page compression (COMPRESSION="zlib") therefore aborts with "out-of-order chunk" at the first payload chunk that follows a sparse chunk of the same file, while the identical stream extracts fine with xbstream -x. Mirror mode_extract's handling: validate checksum and offset for sparse chunks and advance the expected offset by the sparse map skips. Also free chunk.sparse_map before the per-iteration reset - the raw buffer's ownership moves to the upload buffer, the sparse map's does not, so it was leaked for every sparse chunk processed. Co-Authored-By: Claude Fable 5 --- storage/innobase/xtrabackup/src/xbcloud/xbcloud.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/storage/innobase/xtrabackup/src/xbcloud/xbcloud.cc b/storage/innobase/xtrabackup/src/xbcloud/xbcloud.cc index 5746288d6051..6bcd4d579679 100644 --- a/storage/innobase/xtrabackup/src/xbcloud/xbcloud.cc +++ b/storage/innobase/xtrabackup/src/xbcloud/xbcloud.cc @@ -860,7 +860,8 @@ void put_func(put_thread_ctxt_t &cntx) { entry->path = chunk.path; } - if (chunk.type == XB_CHUNK_TYPE_PAYLOAD) { + if (chunk.type == XB_CHUNK_TYPE_PAYLOAD || + chunk.type == XB_CHUNK_TYPE_SPARSE) { res = (xb_rstream_result_t)xb_stream_validate_checksum(&chunk); if (res != XB_STREAM_READ_CHUNK) { break; @@ -909,6 +910,10 @@ void put_func(put_thread_ctxt_t &cntx) { std::placeholders::_1, object_name, chunk.raw_length, cntx.has_errors)); + if (chunk.type == XB_CHUNK_TYPE_SPARSE) { + for (size_t i = 0; i < chunk.sparse_map_size; ++i) + entry->offset += chunk.sparse_map[i].skip; + } entry->offset += chunk.length; entry->chunk_idx++; @@ -916,7 +921,10 @@ void put_func(put_thread_ctxt_t &cntx) { filehash.erase(entry->path); } - /* Reset chunk */ + /* Reset chunk. chunk.raw_data is now owned by the upload buffer, but + chunk.sparse_map is not referenced by it, so free it here to avoid + leaking it on every iteration. */ + my_free(chunk.sparse_map); memset(&chunk, 0, sizeof(chunk)); } while (!cntx.has_errors->load());