|
18 | 18 | package org.apache.hadoop.ozone.container.diskbalancer; |
19 | 19 |
|
20 | 20 | import static org.apache.hadoop.ozone.container.common.ContainerTestUtils.createDbInstancesForTestIfNeeded; |
| 21 | +import static org.apache.hadoop.ozone.container.common.volume.StorageVolume.TMP_DIR_NAME; |
21 | 22 | import static org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getVolumeUsages; |
22 | 23 | import static org.junit.jupiter.api.Assertions.assertEquals; |
23 | 24 | import static org.junit.jupiter.api.Assertions.assertFalse; |
| 25 | +import static org.junit.jupiter.api.Assertions.assertNull; |
24 | 26 | import static org.junit.jupiter.api.Assertions.assertThrows; |
25 | 27 | import static org.junit.jupiter.api.Assertions.assertTrue; |
26 | 28 | import static org.mockito.ArgumentMatchers.any; |
|
52 | 54 | import org.apache.hadoop.ozone.container.common.volume.HddsVolume; |
53 | 55 | import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet; |
54 | 56 | import org.apache.hadoop.ozone.container.common.volume.StorageVolume; |
| 57 | +import org.apache.hadoop.ozone.container.common.volume.VolumeSet; |
55 | 58 | import org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.VolumeFixedUsage; |
56 | 59 | import org.apache.hadoop.ozone.container.diskbalancer.policy.ContainerChoosingPolicy; |
57 | 60 | import org.apache.hadoop.ozone.container.diskbalancer.policy.DefaultContainerChoosingPolicy; |
@@ -114,6 +117,45 @@ public void cleanup() throws IOException { |
114 | 117 | volumeSet.shutdown(); |
115 | 118 | } |
116 | 119 |
|
| 120 | + /** |
| 121 | + * Creates stale diskBalancer directories to simulate leftover directories |
| 122 | + * from previous failed container moves. |
| 123 | + * |
| 124 | + * @param volumeSet the volume set containing volumes to create stale dirs for |
| 125 | + * @param clusterId the cluster ID to use when constructing paths for uninitialized volumes |
| 126 | + * @throws IOException if directory creation fails |
| 127 | + */ |
| 128 | + private void createStaleDiskBalancerDirs(VolumeSet volSet, String clusterId) |
| 129 | + throws IOException { |
| 130 | + List<StorageVolume> volumes = volSet.getVolumesList(); |
| 131 | + for (StorageVolume volume : volumes) { |
| 132 | + if (volume instanceof HddsVolume) { |
| 133 | + HddsVolume hddsVolume = (HddsVolume) volume; |
| 134 | + File staleDiskBalancerDir; |
| 135 | + |
| 136 | + File volumeTmpDir = hddsVolume.getTmpDir(); |
| 137 | + if (volumeTmpDir != null) { |
| 138 | + // If tmpDir is initialized, use it directly |
| 139 | + staleDiskBalancerDir = new File(volumeTmpDir, DiskBalancerService.DISK_BALANCER_DIR); |
| 140 | + } else { |
| 141 | + // If tmpDir is not initialized, construct the path manually |
| 142 | + File clusterIdDir = new File(hddsVolume.getHddsRootDir(), clusterId); |
| 143 | + File tmpDirPath = new File(clusterIdDir, TMP_DIR_NAME); |
| 144 | + staleDiskBalancerDir = new File(tmpDirPath, DiskBalancerService.DISK_BALANCER_DIR); |
| 145 | + } |
| 146 | + |
| 147 | + // Create stale directory with some content |
| 148 | + assertTrue(staleDiskBalancerDir.mkdirs(), |
| 149 | + "Failed to create stale diskBalancer directory: " + staleDiskBalancerDir.getAbsolutePath()); |
| 150 | + File staleContainerDir = new File(staleDiskBalancerDir, "12345"); |
| 151 | + assertTrue(staleContainerDir.mkdirs()); |
| 152 | + // Verify stale directory exists before cleanup |
| 153 | + assertTrue(staleDiskBalancerDir.exists(), |
| 154 | + "Stale diskBalancer directory should exist before cleanup"); |
| 155 | + } |
| 156 | + } |
| 157 | + } |
| 158 | + |
117 | 159 | @ContainerTestVersionInfo.ContainerTest |
118 | 160 | public void testUpdateService(ContainerTestVersionInfo versionInfo) throws Exception { |
119 | 161 | setLayoutAndSchemaForTest(versionInfo); |
@@ -361,4 +403,116 @@ public void testDiskBalancerConfigurationThresholdValidation(double threshold, |
361 | 403 | assertEquals(expectedThreshold, config.getThreshold(), 0.0001); |
362 | 404 | } |
363 | 405 | } |
| 406 | + |
| 407 | + @ContainerTestVersionInfo.ContainerTest |
| 408 | + public void testDiskBalancerCleansUpStaleTmpDir(ContainerTestVersionInfo versionInfo) throws Exception { |
| 409 | + setLayoutAndSchemaForTest(versionInfo); |
| 410 | + // Start volumes to initialize tmp directories |
| 411 | + volumeSet.startAllVolume(); |
| 412 | + |
| 413 | + ContainerSet containerSet = ContainerSet.newReadOnlyContainerSet(1000); |
| 414 | + ContainerMetrics metrics = ContainerMetrics.create(conf); |
| 415 | + KeyValueHandler keyValueHandler = |
| 416 | + new KeyValueHandler(conf, datanodeUuid, containerSet, volumeSet, |
| 417 | + metrics, c -> { |
| 418 | + }, new ContainerChecksumTreeManager(conf)); |
| 419 | + |
| 420 | + // Create stale diskBalancer directories to simulate leftover from previous run |
| 421 | + createStaleDiskBalancerDirs(volumeSet, scmId); |
| 422 | + |
| 423 | + // Use actual DiskBalancerService (not TestImpl) to test the real start() method |
| 424 | + OzoneContainer ozoneContainer = mockDependencies(containerSet, keyValueHandler, null); |
| 425 | + DiskBalancerService svc = new DiskBalancerService(ozoneContainer, 1000, 1000, |
| 426 | + TimeUnit.MILLISECONDS, 1, conf); |
| 427 | + |
| 428 | + // Start the service, which should clean up stale tmp directories via cleanupTmpDir() |
| 429 | + svc.start(); |
| 430 | + |
| 431 | + // Verify stale diskBalancer tmp directories are cleaned up |
| 432 | + for (StorageVolume volume : volumeSet.getVolumesList()) { |
| 433 | + if (volume instanceof HddsVolume) { |
| 434 | + HddsVolume hddsVolume = (HddsVolume) volume; |
| 435 | + File volumeTmpDir = hddsVolume.getTmpDir(); |
| 436 | + File diskBalancerTmpDir = new File(volumeTmpDir, DiskBalancerService.DISK_BALANCER_DIR); |
| 437 | + |
| 438 | + // Verify stale directory is cleaned up (should not exist) |
| 439 | + assertFalse(diskBalancerTmpDir.exists(), |
| 440 | + "Stale diskBalancer tmp directory should be cleaned up on startup"); |
| 441 | + } |
| 442 | + } |
| 443 | + |
| 444 | + svc.shutdown(); |
| 445 | + } |
| 446 | + |
| 447 | + @ContainerTestVersionInfo.ContainerTest |
| 448 | + public void testDiskBalancerCleanupWhenTmpDirNotInitialized(ContainerTestVersionInfo versionInfo) throws Exception { |
| 449 | + setLayoutAndSchemaForTest(versionInfo); |
| 450 | + // Create a fresh volume set WITHOUT calling createDbInstancesForTestIfNeeded |
| 451 | + // This simulates volumes that are formatted but tmpDir is not initialized |
| 452 | + MutableVolumeSet testVolumeSet = new MutableVolumeSet(datanodeUuid, scmId, conf, null, |
| 453 | + StorageVolume.VolumeType.DATA_VOLUME, null); |
| 454 | + |
| 455 | + // Format volumes and ensure clusterID directory exists, but DON'T create tmp dirs |
| 456 | + // This simulates the scenario where tmpDir is null |
| 457 | + for (StorageVolume volume : testVolumeSet.getVolumesList()) { |
| 458 | + if (volume instanceof HddsVolume) { |
| 459 | + HddsVolume hddsVolume = (HddsVolume) volume; |
| 460 | + // Format volume to set clusterID |
| 461 | + hddsVolume.format(scmId); |
| 462 | + // Manually create the clusterID directory (needed for tmpDir creation) |
| 463 | + // but don't call createWorkingDir() or createTmpDirs() |
| 464 | + File clusterIdDir = new File(hddsVolume.getHddsRootDir(), scmId); |
| 465 | + if (!clusterIdDir.exists()) { |
| 466 | + assertTrue(clusterIdDir.mkdirs(), |
| 467 | + "Failed to create clusterID directory: " + clusterIdDir.getAbsolutePath()); |
| 468 | + } |
| 469 | + // Verify tmpDir is null (not initialized) |
| 470 | + assertNull(hddsVolume.getTmpDir()); |
| 471 | + } |
| 472 | + } |
| 473 | + |
| 474 | + // Create stale diskBalancer directories manually to simulate leftover from failed move |
| 475 | + // This tests the scenario where stale dirs exist even though tmpDir is not initialized |
| 476 | + createStaleDiskBalancerDirs(testVolumeSet, scmId); |
| 477 | + |
| 478 | + ContainerSet containerSet = ContainerSet.newReadOnlyContainerSet(1000); |
| 479 | + ContainerMetrics metrics = ContainerMetrics.create(conf); |
| 480 | + KeyValueHandler keyValueHandler = |
| 481 | + new KeyValueHandler(conf, datanodeUuid, containerSet, testVolumeSet, |
| 482 | + metrics, c -> { |
| 483 | + }, new ContainerChecksumTreeManager(conf)); |
| 484 | + |
| 485 | + // Use actual DiskBalancerService (not TestImpl) to test the real start() method |
| 486 | + OzoneContainer ozoneContainer = mockDependencies(containerSet, keyValueHandler, null); |
| 487 | + // Override getVolumeSet to return our test volume set |
| 488 | + when(ozoneContainer.getVolumeSet()).thenReturn(testVolumeSet); |
| 489 | + |
| 490 | + DiskBalancerService svc = new DiskBalancerService(ozoneContainer, 1000, 1000, |
| 491 | + TimeUnit.MILLISECONDS, 1, conf); |
| 492 | + |
| 493 | + // Start the service - cleanup should handle volumes with uninitialized tmpDir |
| 494 | + // and clean up stale directories even when tmpDir is null |
| 495 | + svc.start(); |
| 496 | + |
| 497 | + // Verify stale directories are cleaned up even though tmpDir is not initialized |
| 498 | + List<StorageVolume> volumes = testVolumeSet.getVolumesList(); |
| 499 | + for (StorageVolume volume : volumes) { |
| 500 | + if (volume instanceof HddsVolume) { |
| 501 | + HddsVolume hddsVolume = (HddsVolume) volume; |
| 502 | + // tmpDir should still be null - cleanup doesn't initialize it |
| 503 | + assertNull(hddsVolume.getTmpDir(), |
| 504 | + "tmpDir should not be initialized by cleanup, it will be created lazily"); |
| 505 | + |
| 506 | + // Verify stale diskBalancer directory is cleaned up |
| 507 | + File hddsRootDir = hddsVolume.getHddsRootDir(); |
| 508 | + File expectedDiskBalancerTmpDir = new File(new File(hddsRootDir, scmId), |
| 509 | + TMP_DIR_NAME + File.separator + DiskBalancerService.DISK_BALANCER_DIR); |
| 510 | + assertFalse(expectedDiskBalancerTmpDir.exists(), |
| 511 | + "Stale diskBalancer directory should be cleaned up even when tmpDir is not initialized"); |
| 512 | + } |
| 513 | + } |
| 514 | + |
| 515 | + svc.shutdown(); |
| 516 | + testVolumeSet.shutdown(); |
| 517 | + } |
364 | 518 | } |
0 commit comments