From: Oguzhan Ozmen Date: Tue, 7 Jul 2026 12:27:14 +0000 (+0000) Subject: rgw: copy_obj_data() record uncompressed size for a compressed source X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=5addb80abac1f19cc779744150b8d9b8aebb8f93;p=ceph.git rgw: copy_obj_data() record uncompressed size for a compressed source When copying an object and a compressor/encryption processor is configured for the destination, the bucket-index accounted_size was set from `ofs`. If the source is compressed but the destination is not (re-)compressed (e.g. compression is off, or rgw_compression_type=random rolled no-compression), then `ofs` is the compressed size, not the logical size. In multisite this diverges from the peer zone, which recomputes its own accounted_size via fetch_remote_obj() (already corrected in #63306). ListObjectVersions then reports different sizes per zone. Pass along the source's logical size (astate->accounted_size, derived from the object's own attrs and correct for plain/compressed/encrypted) into copy_obj_data() and use it when the destination is not compressed. The rewrite_obj() and transition_obj() callers pass 0 and keep the prior ofs-based behaviour. This is the copy_obj analog of the fetch_remote_obj fix in #63306. No on-disk/encoding change; corrects newly copied objects only (existing index entries are unaffected) and is safe across a mixed-version upgrade. Fixes: https://tracker.ceph.com/issues/75716 Fixes: https://tracker.ceph.com/issues/75715 Signed-off-by: Oguzhan Ozmen --- diff --git a/src/rgw/driver/rados/rgw_rados.cc b/src/rgw/driver/rados/rgw_rados.cc index 5907e907534..b46975b3891 100644 --- a/src/rgw/driver/rados/rgw_rados.cc +++ b/src/rgw/driver/rados/rgw_rados.cc @@ -5304,7 +5304,8 @@ int RGWRados::copy_obj(RGWObjectCtx& src_obj_ctx, attrs.erase(RGW_ATTR_CRYPT_PART_NUMS); attrs.erase(RGW_ATTR_CRYPT_PREFETCH_ALIGN); return copy_obj_data(dest_obj_ctx, owner, dest_bucket_info, dest_placement, read_op, obj_size - 1, dest_obj, - mtime, real_time(), attrs, olh_epoch, delete_at, petag, dp_factory, dpp, y); + mtime, real_time(), attrs, olh_epoch, delete_at, petag, dp_factory, dpp, y, + astate->accounted_size); } /* This has been in for 2 years, so we can safely assume amanifest is not NULL */ @@ -5475,6 +5476,7 @@ int RGWRados::copy_obj_data(RGWObjectCtx& obj_ctx, rgw::sal::DataProcessorFactory *dp_factory, const DoutPrefixProvider *dpp, optional_yield y, + uint64_t src_accounted_size, bool log_op) { string tag; @@ -5543,8 +5545,13 @@ int RGWRados::copy_obj_data(RGWObjectCtx& obj_ctx, ldpp_dout(dpp, 0) << "ERROR: failed to read compression info" << dendl; return ret; } - // pass original size if compressed - accounted_size = compressed ? cs_info.orig_size : ofs; + // The bucket index accounted_size must be the object's logical (uncompressed) + // size. When the destination is compressed we take it from the compression info. + // Otherwise `ofs` is the number of RAW bytes read from the source: but if the source + // itself is compressed, the ofs is the *compressed* size, not the logical + // size. So, prefer the source's known logical size. + accounted_size = compressed ? cs_info.orig_size + : (src_accounted_size ? src_accounted_size : ofs); } const req_context rctx{dpp, y, nullptr}; @@ -5650,6 +5657,7 @@ int RGWRados::transition_obj(RGWObjectCtx& obj_ctx, nullptr, /* dp_factory */ dpp, y, + 0, /* src_accounted_size: unknown here, keep ofs-based size */ log_op); if (ret < 0) { return ret; diff --git a/src/rgw/driver/rados/rgw_rados.h b/src/rgw/driver/rados/rgw_rados.h index e7ccb0175af..57215ec812d 100644 --- a/src/rgw/driver/rados/rgw_rados.h +++ b/src/rgw/driver/rados/rgw_rados.h @@ -1273,6 +1273,7 @@ public: rgw::sal::DataProcessorFactory *dp_factory, const DoutPrefixProvider *dpp, optional_yield y, + uint64_t src_accounted_size = 0, bool log_op = true); int transition_obj(RGWObjectCtx& obj_ctx,