|
139 | 139 | import org.apache.hadoop.ozone.om.helpers.KeyInfoWithVolumeContext; |
140 | 140 | import org.apache.hadoop.ozone.om.helpers.OmKeyArgs; |
141 | 141 | import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo; |
| 142 | +import org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteInfo; |
142 | 143 | import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus; |
143 | 144 | import org.apache.hadoop.ozone.om.helpers.SnapshotInfo; |
144 | 145 | import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; |
@@ -3439,4 +3440,286 @@ public void testSnapshotDiffWithCompleteInvisibleMPULifecycle() throws Exception |
3439 | 3440 | assertThrows(OMException.class, () -> bucket.getKey(mpuKey2)); |
3440 | 3441 | assertThrows(OMException.class, () -> bucket.getKey(mpuKey3)); |
3441 | 3442 | } |
| 3443 | + |
| 3444 | + private static final int MIN_PART_SIZE = 5 * 1024 * 1024; |
| 3445 | + |
| 3446 | + private void completeSinglePartMPU(OzoneBucket bucket, String keyName, String data) throws IOException { |
| 3447 | + OmMultipartInfo mpuInfo = bucket.initiateMultipartUpload(keyName, getDefaultReplication()); |
| 3448 | + String uploadId = mpuInfo.getUploadID(); |
| 3449 | + |
| 3450 | + byte[] partData = createLargePartData(data, MIN_PART_SIZE); |
| 3451 | + OzoneOutputStream partStream = bucket.createMultipartKey(keyName, partData.length, 1, uploadId); |
| 3452 | + partStream.write(partData); |
| 3453 | + partStream.close(); |
| 3454 | + |
| 3455 | + OzoneMultipartUploadPartListParts partsList = bucket.listParts(keyName, uploadId, 0, 100); |
| 3456 | + String realETag = partsList.getPartInfoList().get(0).getPartName(); |
| 3457 | + |
| 3458 | + Map<Integer, String> partsMap = Collections.singletonMap(1, realETag); |
| 3459 | + bucket.completeMultipartUpload(keyName, uploadId, partsMap); |
| 3460 | + } |
| 3461 | + |
| 3462 | + private void completeMultiplePartMPU(OzoneBucket bucket, String keyName, List<String> partDataList) throws IOException { |
| 3463 | + OmMultipartInfo mpuInfo = bucket.initiateMultipartUpload(keyName, getDefaultReplication()); |
| 3464 | + String uploadId = mpuInfo.getUploadID(); |
| 3465 | + |
| 3466 | + for (int i = 0; i < partDataList.size(); i++) { |
| 3467 | + int partNum = i + 1; |
| 3468 | + byte[] partData = createLargePartData(partDataList.get(i), MIN_PART_SIZE); |
| 3469 | + |
| 3470 | + try (OzoneOutputStream partStream = bucket.createMultipartKey( |
| 3471 | + keyName, partData.length, partNum, uploadId)) { |
| 3472 | + partStream.write(partData); |
| 3473 | + } |
| 3474 | + } |
| 3475 | + |
| 3476 | + OzoneMultipartUploadPartListParts partsList = bucket.listParts(keyName, uploadId, 0, partDataList.size()); |
| 3477 | + Map<Integer, String> partsMap = new HashMap<>(); |
| 3478 | + |
| 3479 | + for (OzoneMultipartUploadPartListParts.PartInfo partInfo : partsList.getPartInfoList()) { |
| 3480 | + partsMap.put(partInfo.getPartNumber(), partInfo.getPartName()); |
| 3481 | + } |
| 3482 | + |
| 3483 | + bucket.completeMultipartUpload(keyName, uploadId, partsMap); |
| 3484 | + } |
| 3485 | + |
| 3486 | + private void completeMixedPartMPU(OzoneBucket bucket, String keyName, String regularData, String streamData) throws IOException { |
| 3487 | + OmMultipartInfo mpuInfo = bucket.initiateMultipartUpload(keyName, getDefaultReplication()); |
| 3488 | + String uploadId = mpuInfo.getUploadID(); |
| 3489 | + |
| 3490 | + byte[] part1Data = createLargePartData(regularData, MIN_PART_SIZE); |
| 3491 | + try (OzoneOutputStream partStream = bucket.createMultipartKey( |
| 3492 | + keyName, part1Data.length, 1, uploadId)) { |
| 3493 | + partStream.write(part1Data); |
| 3494 | + } |
| 3495 | + |
| 3496 | + byte[] part2Data = createLargePartData(streamData, MIN_PART_SIZE); |
| 3497 | + try (OzoneDataStreamOutput partStream = bucket.createMultipartStreamKey( |
| 3498 | + keyName, part2Data.length, 2, uploadId)) { |
| 3499 | + partStream.write(part2Data); |
| 3500 | + } |
| 3501 | + |
| 3502 | + OzoneMultipartUploadPartListParts partsList = bucket.listParts(keyName, uploadId, 0, 2); |
| 3503 | + Map<Integer, String> partsMap = new HashMap<>(); |
| 3504 | + |
| 3505 | + for (OzoneMultipartUploadPartListParts.PartInfo partInfo : partsList.getPartInfoList()) { |
| 3506 | + partsMap.put(partInfo.getPartNumber(), partInfo.getPartName()); |
| 3507 | + } |
| 3508 | + |
| 3509 | + bucket.completeMultipartUpload(keyName, uploadId, partsMap); |
| 3510 | + } |
| 3511 | + |
| 3512 | + private void completeMPUWithReplication(OzoneBucket bucket, String keyName, ReplicationConfig replicationConfig) throws IOException { |
| 3513 | + OmMultipartInfo mpuInfo; |
| 3514 | + if (replicationConfig != null) { |
| 3515 | + mpuInfo = bucket.initiateMultipartUpload(keyName, replicationConfig); |
| 3516 | + } else { |
| 3517 | + mpuInfo = bucket.initiateMultipartUpload(keyName); |
| 3518 | + } |
| 3519 | + |
| 3520 | + String uploadId = mpuInfo.getUploadID(); |
| 3521 | + |
| 3522 | + byte[] partData = createLargePartData("Replication test data for " + keyName, MIN_PART_SIZE); |
| 3523 | + try (OzoneOutputStream partStream = bucket.createMultipartKey( |
| 3524 | + keyName, partData.length, 1, uploadId)) { |
| 3525 | + partStream.write(partData); |
| 3526 | + } |
| 3527 | + |
| 3528 | + OzoneMultipartUploadPartListParts partsList = bucket.listParts(keyName, uploadId, 0, 1); |
| 3529 | + String realETag = partsList.getPartInfoList().get(0).getPartName(); |
| 3530 | + |
| 3531 | + Map<Integer, String> partsMap = Collections.singletonMap(1, realETag); |
| 3532 | + bucket.completeMultipartUpload(keyName, uploadId, partsMap); |
| 3533 | + } |
| 3534 | + |
| 3535 | + private byte[] createLargePartData(String baseContent, int targetSize) { |
| 3536 | + StringBuilder sb = new StringBuilder(); |
| 3537 | + sb.append(baseContent); |
| 3538 | + |
| 3539 | + String padding = " - padding data"; |
| 3540 | + while (sb.length() < targetSize) { |
| 3541 | + sb.append(padding); |
| 3542 | + if (sb.length() + padding.length() > targetSize) { |
| 3543 | + // Add partial padding to reach exact size |
| 3544 | + int remaining = targetSize - sb.length(); |
| 3545 | + sb.append(padding.substring(0, Math.min(remaining, padding.length()))); |
| 3546 | + } |
| 3547 | + } |
| 3548 | + |
| 3549 | + String result = sb.toString(); |
| 3550 | + if (result.length() > targetSize) { |
| 3551 | + result = result.substring(0, targetSize); |
| 3552 | + } |
| 3553 | + |
| 3554 | + return result.getBytes(UTF_8); |
| 3555 | + } |
| 3556 | + |
| 3557 | + @Test |
| 3558 | + public void testSnapshotDiffMPU_CreateNewKey() throws Exception { |
| 3559 | + String testVolumeName = "vol-create-new-" + counter.incrementAndGet(); |
| 3560 | + String testBucketName = "bucket-create-new-" + counter.incrementAndGet(); |
| 3561 | + |
| 3562 | + store.createVolume(testVolumeName); |
| 3563 | + OzoneVolume volume = store.getVolume(testVolumeName); |
| 3564 | + createBucket(volume, testBucketName); |
| 3565 | + OzoneBucket bucket = volume.getBucket(testBucketName); |
| 3566 | + |
| 3567 | + String existingKey = "existing-baseline-" + counter.incrementAndGet(); |
| 3568 | + createFileKey(bucket, existingKey); |
| 3569 | + |
| 3570 | + String snap1 = "snap-before-create-" + counter.incrementAndGet(); |
| 3571 | + createSnapshot(testVolumeName, testBucketName, snap1); |
| 3572 | + |
| 3573 | + String mpuKey1 = "multi-mpu-1-" + counter.incrementAndGet(); |
| 3574 | + String mpuKey2 = "multi-mpu-2-" + counter.incrementAndGet(); |
| 3575 | + String mpuKey3 = "multi-mpu-3-" + counter.incrementAndGet(); |
| 3576 | + |
| 3577 | + completeSinglePartMPU(bucket, mpuKey1, "Single part MPU data"); |
| 3578 | + |
| 3579 | + completeMultiplePartMPU(bucket, mpuKey2, |
| 3580 | + Arrays.asList("Multi part 1", "Multi part 2", "Multi part 3")); |
| 3581 | + |
| 3582 | + completeMixedPartMPU(bucket, mpuKey3, |
| 3583 | + "Mixed regular part", "Mixed stream part"); |
| 3584 | + |
| 3585 | + String regularKey = "regular-compare-" + counter.incrementAndGet(); |
| 3586 | + createFileKey(bucket, regularKey); |
| 3587 | + |
| 3588 | + String snap2 = "snap-multiple-final-" + counter.incrementAndGet(); |
| 3589 | + createSnapshot(testVolumeName, testBucketName, snap2); |
| 3590 | + |
| 3591 | + SnapshotDiffReportOzone diff = getSnapDiffReport(testVolumeName, testBucketName, snap1, snap2); |
| 3592 | + |
| 3593 | + List<SnapshotDiffReport.DiffReportEntry> expectedDiffs = Arrays.asList( |
| 3594 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, mpuKey1), |
| 3595 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, mpuKey2), |
| 3596 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, mpuKey3), |
| 3597 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, regularKey)); |
| 3598 | + assertEquals(expectedDiffs, diff.getDiffList()); |
| 3599 | + } |
| 3600 | + |
| 3601 | + @Test |
| 3602 | + public void testSnapshotDiffMPU_CreateMultipleKeys() throws Exception { |
| 3603 | + String testVolumeName = "vol-create-multiple-" + counter.incrementAndGet(); |
| 3604 | + String testBucketName = "bucket-create-multiple-" + counter.incrementAndGet(); |
| 3605 | + |
| 3606 | + store.createVolume(testVolumeName); |
| 3607 | + OzoneVolume volume = store.getVolume(testVolumeName); |
| 3608 | + createBucket(volume, testBucketName); |
| 3609 | + OzoneBucket bucket = volume.getBucket(testBucketName); |
| 3610 | + |
| 3611 | + String snap1 = "snap-multiple-baseline-" + counter.incrementAndGet(); |
| 3612 | + createSnapshot(testVolumeName, testBucketName, snap1); |
| 3613 | + |
| 3614 | + String mpuKey1 = "multi-mpu-1-" + counter.incrementAndGet(); |
| 3615 | + String mpuKey2 = "multi-mpu-2-" + counter.incrementAndGet(); |
| 3616 | + String mpuKey3 = "multi-mpu-3-" + counter.incrementAndGet(); |
| 3617 | + |
| 3618 | + completeSinglePartMPU(bucket, mpuKey1, "Single part MPU data"); |
| 3619 | + completeMultiplePartMPU(bucket, mpuKey2, |
| 3620 | + Arrays.asList("Multi part 1", "Multi part 2", "Multi part 3")); |
| 3621 | + completeMixedPartMPU(bucket, mpuKey3, |
| 3622 | + "Mixed regular part", "Mixed stream part"); |
| 3623 | + |
| 3624 | + String regularKey = "regular-compare-" + counter.incrementAndGet(); |
| 3625 | + createFileKey(bucket, regularKey); |
| 3626 | + |
| 3627 | + String snap2 = "snap-multiple-final-" + counter.incrementAndGet(); |
| 3628 | + createSnapshot(testVolumeName, testBucketName, snap2); |
| 3629 | + |
| 3630 | + SnapshotDiffReportOzone diff = getSnapDiffReport(testVolumeName, testBucketName, snap1, snap2); |
| 3631 | + |
| 3632 | + List<SnapshotDiffReport.DiffReportEntry> expectedDiffs = Arrays.asList( |
| 3633 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, mpuKey1), |
| 3634 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, mpuKey2), |
| 3635 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, mpuKey3), |
| 3636 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, regularKey)); |
| 3637 | + assertEquals(expectedDiffs, diff.getDiffList()); |
| 3638 | + } |
| 3639 | + |
| 3640 | + @Test |
| 3641 | + public void testSnapshotDiffMPU_CreateWithMetadataAndTags() throws Exception { |
| 3642 | + String testVolumeName = "vol-create-meta-" + counter.incrementAndGet(); |
| 3643 | + String testBucketName = "bucket-create-meta-" + counter.incrementAndGet(); |
| 3644 | + |
| 3645 | + store.createVolume(testVolumeName); |
| 3646 | + OzoneVolume volume = store.getVolume(testVolumeName); |
| 3647 | + createBucket(volume, testBucketName); |
| 3648 | + OzoneBucket bucket = volume.getBucket(testBucketName); |
| 3649 | + |
| 3650 | + String snap1 = "snap-meta-baseline-" + counter.incrementAndGet(); |
| 3651 | + createSnapshot(testVolumeName, testBucketName, snap1); |
| 3652 | + |
| 3653 | + String mpuKeyWithMeta = "mpu-with-metadata-" + counter.incrementAndGet(); |
| 3654 | + |
| 3655 | + Map<String, String> richMetadata = new HashMap<>(); |
| 3656 | + richMetadata.put("content-type", "application/octet-stream"); |
| 3657 | + richMetadata.put("content-encoding", "gzip"); |
| 3658 | + richMetadata.put("cache-control", "no-cache"); |
| 3659 | + richMetadata.put("custom-header", "test-value"); |
| 3660 | + |
| 3661 | + Map<String, String> richTags = new HashMap<>(); |
| 3662 | + richTags.put("project", "ozone-testing"); |
| 3663 | + richTags.put("environment", "integration"); |
| 3664 | + richTags.put("cost-center", "engineering"); |
| 3665 | + richTags.put("backup-policy", "daily"); |
| 3666 | + |
| 3667 | + OmMultipartInfo mpuInfo = bucket.initiateMultipartUpload( |
| 3668 | + mpuKeyWithMeta, getDefaultReplication(), richMetadata, richTags); |
| 3669 | + String uploadId = mpuInfo.getUploadID(); |
| 3670 | + |
| 3671 | + byte[] partData = createLargePartData("MPU with rich metadata and tags", 1024); |
| 3672 | + OzoneDataStreamOutput partStream = bucket. |
| 3673 | + createMultipartStreamKey(mpuKeyWithMeta, partData.length, 1, uploadId); |
| 3674 | + partStream.write(partData); |
| 3675 | + partStream.close(); |
| 3676 | + |
| 3677 | + OzoneMultipartUploadPartListParts partsList = bucket. |
| 3678 | + listParts(mpuKeyWithMeta, uploadId, 0, 100); |
| 3679 | + String realETag = partsList.getPartInfoList().get(0).getPartName(); |
| 3680 | + Map<Integer, String> partsMap = Collections.singletonMap(1, realETag); |
| 3681 | + bucket.completeMultipartUpload(mpuKeyWithMeta, uploadId, partsMap); |
| 3682 | + |
| 3683 | + String snap2 = "snap-meta-final-" + counter.incrementAndGet(); |
| 3684 | + createSnapshot(testVolumeName, testBucketName, snap2); |
| 3685 | + |
| 3686 | + SnapshotDiffReportOzone diff = getSnapDiffReport(testVolumeName, testBucketName, snap1, snap2); |
| 3687 | + |
| 3688 | + List<SnapshotDiffReport.DiffReportEntry> expectedDiffs = Arrays.asList( |
| 3689 | + SnapshotDiffReportOzone.getDiffReportEntry( |
| 3690 | + SnapshotDiffReport.DiffType.CREATE, mpuKeyWithMeta)); |
| 3691 | + assertEquals(expectedDiffs, diff.getDiffList()); |
| 3692 | + } |
| 3693 | + |
| 3694 | + @Test |
| 3695 | + public void testSnapshotDiffMPU_CreateWithDifferentReplication() throws Exception { |
| 3696 | + String testVolumeName = "vol-create-repl-" + counter.incrementAndGet(); |
| 3697 | + String testBucketName = "bucket-create-repl-" + counter.incrementAndGet(); |
| 3698 | + |
| 3699 | + store.createVolume(testVolumeName); |
| 3700 | + OzoneVolume volume = store.getVolume(testVolumeName); |
| 3701 | + createBucket(volume, testBucketName); |
| 3702 | + OzoneBucket bucket = volume.getBucket(testBucketName); |
| 3703 | + |
| 3704 | + String snap1 = "snap-repl-baseline-" + counter.incrementAndGet(); |
| 3705 | + createSnapshot(testVolumeName, testBucketName, snap1); |
| 3706 | + |
| 3707 | + String standaloneKey = "standalone-mpu-" + counter.incrementAndGet(); |
| 3708 | + String defaultKey = "default-mpu-" + counter.incrementAndGet(); |
| 3709 | + |
| 3710 | + ReplicationConfig standaloneConfig = StandaloneReplicationConfig.getInstance( |
| 3711 | + HddsProtos.ReplicationFactor.ONE); |
| 3712 | + completeMPUWithReplication(bucket, standaloneKey, standaloneConfig); |
| 3713 | + completeMPUWithReplication(bucket, defaultKey, null); |
| 3714 | + |
| 3715 | + String snap2 = "snap-repl-final-" + counter.incrementAndGet(); |
| 3716 | + createSnapshot(testVolumeName, testBucketName, snap2); |
| 3717 | + |
| 3718 | + SnapshotDiffReportOzone diff = getSnapDiffReport(testVolumeName, testBucketName, snap1, snap2); |
| 3719 | + |
| 3720 | + List<SnapshotDiffReport.DiffReportEntry> expectedDiffs = Arrays.asList( |
| 3721 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, standaloneKey), |
| 3722 | + SnapshotDiffReportOzone.getDiffReportEntry(SnapshotDiffReport.DiffType.CREATE, defaultKey)); |
| 3723 | + assertEquals(expectedDiffs, diff.getDiffList()); |
| 3724 | + } |
3442 | 3725 | } |
0 commit comments