4141 #include INCLUDE_AWS_S3(model/GetBucketVersioningRequest.h)
4242 #include INCLUDE_AWS_S3(model/HeadObjectRequest.h)
4343 #include INCLUDE_AWS_S3(model/HeadBucketRequest.h)
44+ #include INCLUDE_AWS_S3(model/ListMultipartUploadsRequest.h)
4445 #include INCLUDE_AWS_S3(model/ListObjectsV2Request.h)
46+ #include INCLUDE_AWS_S3(model/ListPartsRequest.h)
4547 #include INCLUDE_AWS_S3(model/Object.h)
4648 #include INCLUDE_AWS_S3(model/ObjectLockRule.h)
4749 #include INCLUDE_AWS_S3(model/PutBucketAclRequest.h)
@@ -3882,15 +3884,9 @@ void LocalWorker::s3ModeIterateObjects()
38823884 IF_UNLIKELY ( (fileIndex % INTERRUPTION_CHECK_INTERVAL) == 0 )
38833885 checkInterruptionRequest ();
38843886
3885- // generate current dir path
3886- int printRes;
3887-
3888- if (haveSubdirs)
3889- printRes = snprintf (currentPath.data (), PATH_BUF_LEN, " r%zu/d%zu/r%zu-f%zu" ,
3890- workerDirRank, dirIndex, workerRank, fileIndex);
3891- else
3892- printRes = snprintf (currentPath.data (), PATH_BUF_LEN, " r%zu-f%zu" ,
3893- workerRank, fileIndex);
3887+ // generate current file path (subdir is added to objectPrefix below)
3888+ int printRes = snprintf (currentPath.data (), PATH_BUF_LEN, " r%zu-f%zu" ,
3889+ workerRank, fileIndex);
38943890
38953891 IF_UNLIKELY (printRes >= PATH_BUF_LEN)
38963892 throw WorkerException (" object path too long for static buffer. "
@@ -3902,6 +3898,16 @@ void LocalWorker::s3ModeIterateObjects()
39023898 if (objectPrefixRand)
39033899 objectPrefix = getS3RandObjectPrefix (
39043900 workerRank, dirIndex, fileIndex, progArgs->getS3ObjectPrefix () );
3901+ else if (haveSubdirs)
3902+ {
3903+ // add subdir to objectPrefix instead of currentPath
3904+ char subdirBuf[PATH_BUF_LEN];
3905+ snprintf (subdirBuf, PATH_BUF_LEN, " r%zu/d%zu/" ,
3906+ workerDirRank, dirIndex);
3907+ objectPrefix = progArgs->getS3ObjectPrefix () + std::string (subdirBuf);
3908+ }
3909+ else
3910+ objectPrefix = progArgs->getS3ObjectPrefix ();
39053911
39063912 unsigned bucketIndex = (workerRank + dirIndex) % bucketVec.size ();
39073913 std::string currentObjectPath = objectPrefix + currentPath.data ();
@@ -3914,7 +3920,9 @@ void LocalWorker::s3ModeIterateObjects()
39143920 if ( (benchPhase == BenchPhase_CREATEFILES) && !isRWMixedReader)
39153921 {
39163922 if (blockSize < fileSize)
3917- s3ModeUploadObjectMultiPart (bucketVec[bucketIndex], currentObjectPath);
3923+ s3ModeUploadObjectMultiPart (bucketVec[bucketIndex],
3924+ objectPrefix,
3925+ currentObjectPath);
39183926 else
39193927 s3ModeUploadObjectSinglePart (bucketVec[bucketIndex], currentObjectPath);
39203928 }
@@ -4133,18 +4141,21 @@ void LocalWorker::s3ModeIterateCustomObjects()
41334141
41344142 if (benchPhase == BenchPhase_CREATEFILES)
41354143 {
4136- if (rangeLen < fileSize)
4137- s3ModeUploadObjectMultiPartShared (bucketName,
4138- objectPrefix + currentPathElem.path , fileSize);
4139- else
4140- { // this worker uploads the whole object
4141- if (blockSize < fileSize)
4142- s3ModeUploadObjectMultiPart (bucketName,
4143- objectPrefix + currentPathElem.path );
4144- else
4145- s3ModeUploadObjectSinglePart (bucketName,
4146- objectPrefix + currentPathElem.path );
4147- }
4144+ if (rangeLen < fileSize)
4145+ {
4146+ s3ModeUploadObjectMultiPartShared (bucketName,
4147+ objectPrefix + currentPathElem.path , fileSize);
4148+ }
4149+ else
4150+ { // this worker uploads the whole object
4151+ if (blockSize < fileSize)
4152+ s3ModeUploadObjectMultiPart (bucketName,
4153+ objectPrefix,
4154+ objectPrefix + currentPathElem.path );
4155+ else
4156+ s3ModeUploadObjectSinglePart (bucketName,
4157+ objectPrefix + currentPathElem.path );
4158+ }
41484159 }
41494160
41504161 if (benchPhase == BenchPhase_READFILES)
@@ -4211,7 +4222,7 @@ void LocalWorker::s3ModeThrowOnError(
42114222 IF_LIKELY (outcome.IsSuccess () )
42124223 return ;
42134224
4214- const auto s3Error = outcome.GetError ();
4225+ const S3ErrorType s3Error = outcome.GetError ();
42154226
42164227 std::stringstream errStr;
42174228 errStr << failMessage << std::endl <<
@@ -4734,7 +4745,7 @@ void LocalWorker::s3ModeUploadObjectSinglePart(std::string bucketName, std::stri
47344745 *
47354746 * @throw WorkerException on error.
47364747 */
4737- void LocalWorker::s3ModeUploadObjectMultiPart (std::string bucketName, std::string objectName)
4748+ void LocalWorker::s3ModeUploadObjectMultiPart (std::string bucketName, std::string prefix, std::string objectName)
47384749{
47394750#ifndef S3_SUPPORT
47404751 throw WorkerException (std::string (__func__) + " called, but this was built without S3 support" );
@@ -4887,6 +4898,81 @@ void LocalWorker::s3ModeUploadObjectMultiPart(std::string bucketName, std::strin
48874898 atomicLiveOps.numIOPSDone ++;
48884899 }
48894900
4901+ // List parts before completion to validate
4902+ if (progArgs->getDoS3ListParts ()) {
4903+ S3::ListPartsRequest listPartsRequest;
4904+ listPartsRequest.WithBucket (bucketName)
4905+ .WithKey (objectName)
4906+ .WithUploadId (uploadID);
4907+
4908+ OPLOG_PRE_OP (" S3ListParts" , bucketName + " /" + objectName, 0 , 0 );
4909+
4910+ auto listPartsOutcome = s3Client->ListParts (listPartsRequest);
4911+
4912+ OPLOG_POST_OP (" S3ListParts" , bucketName + " /" + objectName, 0 , 0 ,
4913+ !listPartsOutcome.IsSuccess ());
4914+
4915+ IF_UNLIKELY (!listPartsOutcome.IsSuccess ()) {
4916+ auto s3Error = listPartsOutcome.GetError ();
4917+ if (!ignoreS3Errors) {
4918+ throw WorkerException (
4919+ std::string (" List parts failed. " ) + " Bucket: " + bucketName + " ; " +
4920+ " Key: " + objectName + " ; " + " UploadId: " + uploadID + " ; " +
4921+ " Error: " + s3Error.GetExceptionName () + " ; " +
4922+ " Message: " + s3Error.GetMessage ());
4923+ }
4924+ }
4925+ // Validate that the number of parts matches
4926+ const auto &parts = listPartsOutcome.GetResult ().GetParts ();
4927+ size_t expectedPartCount = completedMultipartUpload.GetParts ().size ();
4928+ size_t actualPartCount = parts.size ();
4929+
4930+ IF_UNLIKELY (actualPartCount != expectedPartCount) {
4931+ if (!ignoreS3Errors) {
4932+ throw WorkerException (
4933+ std::string (" List parts count mismatch. " ) + " Bucket: " + bucketName +
4934+ " ; " + " Key: " + objectName + " ; " +
4935+ " Expected parts: " + std::to_string (expectedPartCount) + " ; " +
4936+ " Actual parts: " + std::to_string (actualPartCount));
4937+ }
4938+ }
4939+ }
4940+
4941+ // List multipart uploads before completion
4942+ if (progArgs->getDoS3ListMPU ()) {
4943+ S3::ListMultipartUploadsRequest listRequest;
4944+ listRequest.WithBucket (bucketName).WithPrefix (prefix);
4945+
4946+ OPLOG_PRE_OP (" S3ListMultipartUploads" , bucketName, 0 , 0 );
4947+
4948+ auto listOutcome = s3Client->ListMultipartUploads (listRequest);
4949+
4950+ OPLOG_POST_OP (" S3ListMultipartUploads" , bucketName, 0 , 0 ,
4951+ !listOutcome.IsSuccess ());
4952+
4953+ IF_UNLIKELY (!listOutcome.IsSuccess ()) {
4954+ auto s3Error = listOutcome.GetError ();
4955+ if (!ignoreS3Errors) {
4956+ throw WorkerException (std::string (" Multipart upload listing failed. " ) +
4957+ " Bucket: " + bucketName + " ; " +
4958+ " Error: " + s3Error.GetExceptionName () + " ; " +
4959+ " Message: " + s3Error.GetMessage ());
4960+ }
4961+ }
4962+
4963+ auto outcome = listOutcome.GetResult ();
4964+ std::cout << " Uploads size: " << outcome.GetUploads ().size () << " For prefix " << outcome.GetPrefix () << std::endl;
4965+
4966+ // We expect exactly 1 multipart upload because the prefix contains the worker rank,
4967+ // making it specific enough to match only the current upload
4968+ IF_UNLIKELY (outcome.GetUploads ().size () != 1 ) {
4969+ throw WorkerException (
4970+ std::string (" Expected exactly 1 multipart upload, but found " ) +
4971+ std::to_string (outcome.GetUploads ().size ()) + " ; " +
4972+ " Bucket: " + bucketName + " ; " + " Prefix: " + outcome.GetPrefix ());
4973+ }
4974+ }
4975+
48904976 // S T E P 3: submit upload completion
48914977
48924978 IF_UNLIKELY (s3NoMpuCompletion)
0 commit comments