fix multipart RDMA: propagate rdmaclient, per-part CRC64NVME, complete XML#226
Merged
harshavardhana merged 2 commits intoMay 27, 2026
Merged
Conversation
…e XML The RDMA UploadPart server path requires a per-part CRC64NVME checksum when CreateMultipartUpload declared the algorithm, and the CompleteMultipartUpload request must include the per-part checksum so the server can verify the assembled object. Three latent bugs in the multipart RDMA path caused every multipart upload to either silently fall back to HTTP or fail with InvalidPart on Complete: 1. Client::PutObject's multipart loop never set `up_args.rdmaclient = args.rdmaclient`, so BaseClient::UploadPart's RDMA branch (which keys on `args.rdmaclient != nullptr`) was unreachable for multipart. Every part went over plain HTTP. 2. Even with rdmaclient propagated, no per-part CRC64NVME was computed or attached, so the server rejected with `(checksum missing, want "CRC64NVME", got "")` after we started declaring the algorithm on Create. 3. The Part struct had no checksum field and the Complete XML omitted `<ChecksumCRC64NVME>`, so the server's part-list validation failed with InvalidPart even when the parts uploaded successfully. Changes: - utils: add `Crc64Nvme` / `Crc64NvmeBase64` (NVMe E2E CRC-64, polynomial 0xad93d23594c93659, reflected, init/xor 0xffffffffffffffff). Table is lazily built once via local static, verified against the NVMe spec test vector `123456789 -> 0xae8b14860a799888`. - types: add `Part::checksum_crc64nvme` and a 3-arg constructor. - baseclient: emit `<ChecksumCRC64NVME>` in CompleteMultipartUpload XML when the per-part checksum is non-empty. - client: in PutObject's multipart loop, declare CRC64NVME on Create, propagate `up_args.rdmaclient`, compute the per-part checksum on host buffers (`cuObjClient::getMemoryType == CUOBJ_MEMORY_SYSTEM`), set both `up_args.checksum_crc64nvme` (consumed by the RDMA path in rdma.h) and `up_args.headers["x-amz-checksum-crc64nvme"]` (consumed by the HTTP fallback in BaseClient::UploadPart), and propagate the checksum into the assembled Part list. GPU buffers cannot be hashed by the CPU; when `getMemoryType` returns CUDA_DEVICE / CUDA_MANAGED we leave the checksum empty and the RDMA path will surface the 501 to the caller (or the caller supplies a pre-computed checksum via headers). Verified end-to-end against a `minio.rdma` server with a 20 MiB file forcing 2 parts (16 MiB + 4 MiB) on the standard `PutObject` example: final ETag `fe2f8e526e386a020064a32911319808-2`.
There was a problem hiding this comment.
Pull request overview
Fixes three latent bugs in the multipart RDMA upload path introduced in #214: (1) rdmaclient was not propagated to per-part UploadPartArgs, (2) no per-part CRC64NVME was computed or sent, and (3) the Part struct/CompleteMultipartUpload XML had no ChecksumCRC64NVME field, causing InvalidPart failures from servers that require the checksum.
Changes:
- Add
utils::Crc64Nvme/utils::Crc64NvmeBase64helpers (table-driven NVMe E2E CRC-64). - Add
Part::checksum_crc64nvmeand emit<ChecksumCRC64NVME>in the Complete XML. - In
Client::PutObject's multipart loop, declare CRC64NVME on Create, propagaterdmaclient, compute per-part CRC on host buffers, and pass the checksum through to the assembledPart.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| include/miniocpp/utils.h | Declares the new CRC64NVME helpers. |
| src/utils.cc | Implements the reflected-poly CRC64NVME and its base64 wrapper. |
| include/miniocpp/types.h | Adds checksum_crc64nvme field and 3-arg constructor to Part. |
| src/baseclient.cc | Emits <ChecksumCRC64NVME> in CompleteMultipartUpload XML when present. |
| src/client.cc | Multipart loop now declares CRC64NVME, propagates rdmaclient, computes per-part CRC, and forwards it into the Part list. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Run clang-format-20 --style=Google on src/utils.cc and src/client.cc to satisfy the repo's coding style check. - In Client::PutObject's multipart loop, build the assembled Part with up_args.checksum_crc64nvme instead of resp.checksum_crc64nvme. BaseClient::UploadPart's HTTP fallback path does not populate the response field from x-amz-checksum-crc64nvme, so an HTTP-fallback part would otherwise omit <ChecksumCRC64NVME> in CompleteMultipartUpload and trigger InvalidPart when the algorithm was declared on Create.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The multipart RDMA path (added in #214) silently never went over RDMA, and once a server-side CRC64NVME requirement was added it started failing with
InvalidParton Complete. Three latent bugs:Client::PutObject's multipart loop never propagatedrdmaclienttoUploadPartArgs.BaseClient::UploadPart's RDMA branch (which keys onargs.rdmaclient != nullptr) was unreachable for multipart — every part went over plain HTTP.(checksum missing, want "CRC64NVME", got "").Partstruct had no checksum field and CompleteMultipartUpload XML omitted<ChecksumCRC64NVME>. Even if parts uploaded successfully, the server's part-list validation failed withInvalidPart.Changes
utils.h/utils.cc: addCrc64Nvme(data, len)andCrc64NvmeBase64(data, len). Table-driven NVMe E2E CRC-64 (polynomial0xad93d23594c93659, reflected, init/xor0xffffffffffffffff), lazily initialised via local static. Verified against the NVMe spec test vector123456789 → 0xae8b14860a799888.types.h: addPart::checksum_crc64nvmeand a 3-arg constructor.baseclient.cc: emit<ChecksumCRC64NVME>in CompleteMultipartUpload XML when the per-part checksum is non-empty.client.cc: in PutObject's multipart loop — declarex-amz-checksum-algorithm: CRC64NVMEon Create, propagateup_args.rdmaclient, compute per-part CRC64NVME on host buffers (cuObjClient::getMemoryType == CUOBJ_MEMORY_SYSTEM), set bothup_args.checksum_crc64nvme(consumed byrdma.h) andup_args.headers[\"x-amz-checksum-crc64nvme\"](consumed by the HTTP fallback inBaseClient::UploadPart), and propagate the checksum into the assembledPartlist.GPU buffers (
CUDA_DEVICE/CUDA_MANAGED) cannot be hashed by the CPU; in that case we leave the checksum empty and the RDMA path will surface a 501 to the caller, or the caller supplies a pre-computed checksum viaargs.headers.Verification
Tested against a live
minio.rdmaserver (15.15.15.59:9200) across a Mellanox mlx5_0 fabric, using the existingPutObjectexample with a 20 MiB file forcing 2 parts (16 MiB + 4 MiB):The
-2suffix on the final ETag confirms the multipart path ran, and the upload no longer falls back to HTTP.Test plan
-DMINIO_CPP_ENABLE_RDMA=ON— should compile clean.Companion PR
A matching native multipart-RDMA implementation in minio-rs is posted alongside this one (it ran into the same CRC64NVME and Complete-XML requirements during initial development; both SDKs now agree against the same server).