This is an automated email from the ASF dual-hosted git repository.

adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new b97a11bed33 HDDS-15651. Test case for DiskBalancer when 
markContainerForDelete fails (#10593)
b97a11bed33 is described below

commit b97a11bed338d19945be5935cbeb5f9d53faeabd
Author: Arun Sarin <[email protected]>
AuthorDate: Sat Jul 4 21:55:52 2026 +0530

    HDDS-15651. Test case for DiskBalancer when markContainerForDelete fails 
(#10593)
---
 .../diskbalancer/TestDiskBalancerTask.java         | 110 +++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerTask.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerTask.java
index ef9dc8f57b8..e15eaa3a293 100644
--- 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerTask.java
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerTask.java
@@ -20,6 +20,7 @@
 import static 
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.CONTAINER_INTERNAL_ERROR;
 import static 
org.apache.hadoop.ozone.container.common.ContainerTestUtils.createDbInstancesForTestIfNeeded;
 import static 
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerService.DISK_BALANCER_DIR;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -504,6 +505,115 @@ public void 
moveFailsDuringInMemoryUpdate(ContainerTestVersionInfo versionInfo)
     assertEquals(initialSourceDelta, 
diskBalancerService.getDeltaSizes().get(sourceVolume));
   }
 
+  /**
+   * When markContainerForDelete fails after import and ContainerSet update,
+   * the move is still reported as success, the destination replica is active, 
the source
+   * replica is queued for lazy deletion, and cleanup removes it after the 
delay.
+   */
+  @ContainerTestVersionInfo.ContainerTest
+  public void moveSucceedsWhenMarkContainerForDeleteFails(
+      ContainerTestVersionInfo versionInfo)
+      throws IOException, InterruptedException, TimeoutException {
+    setLayoutAndSchemaForTest(versionInfo);
+    long delay = 2_000L;
+    diskBalancerService.setReplicaDeletionDelay(delay);
+
+    KeyValueContainer container = createContainer(CONTAINER_ID, sourceVolume, 
State.CLOSED);
+    File oldContainerDir = new 
File(container.getContainerData().getContainerPath());
+    Path destDirPath = Paths.get(
+        KeyValueContainerLocationUtil.getBaseContainerLocation(
+            destVolume.getHddsRootDir().toString(), scmId, CONTAINER_ID));
+    assertThat(destDirPath.toFile())
+        .as("Destination container should not exist before task execution")
+        .doesNotExist();
+
+    KeyValueContainer spyContainer = spy(container);
+    containerSet.removeContainer(CONTAINER_ID);
+    containerSet.addContainer(spyContainer);
+    doThrow(new RuntimeException("simulated markContainerForDelete failure"))
+        .when(spyContainer).markContainerForDelete();
+
+    LogCapturer serviceLog = 
GenericTestUtils.LogCapturer.captureLogs(DiskBalancerService.class);
+    DiskBalancerService.DiskBalancerTask task = getTask();
+    task.call();
+
+    assertThat(serviceLog.getOutput())
+        .contains("Failed to mark the old container " + CONTAINER_ID + " for 
delete");
+    assertThat(serviceLog.getOutput())
+        .as("move should not roll back when markContainerForDelete fails")
+        .doesNotContain("Rolling back move");
+
+    assertEquals(1, diskBalancerService.getMetrics().getSuccessCount());
+    assertEquals(0, diskBalancerService.getMetrics().getFailureCount());
+    assertEquals(CONTAINER_SIZE, 
diskBalancerService.getMetrics().getSuccessBytes());
+
+    Container activeReplica = containerSet.getContainer(CONTAINER_ID);
+    assertThat(activeReplica).isNotSameAs(spyContainer);
+    assertEquals(destVolume, activeReplica.getContainerData().getVolume());
+    assertThat(new 
File(activeReplica.getContainerData().getContainerPath())).exists();
+    assertThat(oldContainerDir)
+        .as("Source replica should remain on disk until lazy deletion runs")
+        .exists();
+    assertEquals(1, diskBalancerService.getPendingDeletionQueueSize(),
+        "Source replica should be queued for lazy deletion after mark 
failure");
+
+    clock.fastForward(delay);
+    diskBalancerService.cleanupPendingDeletionContainers();
+
+    assertThat(oldContainerDir)
+        .as("Source replica should be removed after lazy deletion delay")
+        .doesNotExist();
+    assertEquals(0, diskBalancerService.getPendingDeletionQueueSize());
+  }
+
+  /**
+   * When lazy deletion fails, the pending queue entry is dropped and
+   * the source replica is not retried for deletion.
+   */
+  @ContainerTestVersionInfo.ContainerTest
+  public void lazyDeletionFailureDoesNotRetry(
+      ContainerTestVersionInfo versionInfo) throws Exception {
+    setLayoutAndSchemaForTest(versionInfo);
+    long delay = 2_000L;
+    diskBalancerService.setReplicaDeletionDelay(delay);
+
+    Container container = createContainer(CONTAINER_ID, sourceVolume, 
State.CLOSED);
+    File oldContainerDir = new 
File(container.getContainerData().getContainerPath());
+
+    DiskBalancerService.DiskBalancerTask task = getTask();
+    task.call();
+
+    assertEquals(1, diskBalancerService.getMetrics().getSuccessCount());
+    assertEquals(1, diskBalancerService.getPendingDeletionQueueSize());
+    assertThat(oldContainerDir).exists();
+
+    clock.fastForward(delay);
+
+    LogCapturer serviceLog = 
GenericTestUtils.LogCapturer.captureLogs(DiskBalancerService.class);
+    try (MockedStatic<KeyValueContainerUtil> mockedUtil =
+             mockStatic(KeyValueContainerUtil.class, 
Mockito.CALLS_REAL_METHODS)) {
+      mockedUtil.when(() -> KeyValueContainerUtil.removeContainer(
+              any(KeyValueContainerData.class), any(OzoneConfiguration.class)))
+          .thenThrow(new IOException("simulated lazy deletion failure"));
+
+      diskBalancerService.cleanupPendingDeletionContainers();
+
+      assertThat(oldContainerDir)
+          .as("Source replica should remain when lazy deletion fails")
+          .exists();
+      assertEquals(0, diskBalancerService.getPendingDeletionQueueSize(),
+          "Failed deletion should be removed from the pending queue");
+      assertThat(serviceLog.getOutput())
+          .contains("Failed to delete old container " + CONTAINER_ID);
+      assertThat(serviceLog.getOutput()).contains("background scanners");
+    }
+
+    diskBalancerService.cleanupPendingDeletionContainers();
+    assertThat(oldContainerDir)
+        .as("Source replica should not be retried after lazy deletion failure")
+        .exists();
+  }
+
   @ContainerTestVersionInfo.ContainerTest
   public void moveFailsDuringOldContainerRemove(ContainerTestVersionInfo 
versionInfo) throws IOException {
     setLayoutAndSchemaForTest(versionInfo);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to