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 f856adae607 HDDS-8781. Allow on demand metadata scanning of open 
containers (#8442)
f856adae607 is described below

commit f856adae607e8587daa5587814c276748c209b5b
Author: Tejaskriya <87555809+tejaskr...@users.noreply.github.com>
AuthorDate: Sat Jul 12 17:22:22 2025 +0530

    HDDS-8781. Allow on demand metadata scanning of open containers (#8442)
---
 .../ozone/container/common/impl/ContainerSet.java  |  6 +--
 .../BackgroundContainerMetadataScanner.java        | 19 +--------
 .../container/ozoneimpl/ContainerScanHelper.java   | 34 ++++++++++++++--
 ...aScanner.java => OnDemandContainerScanner.java} | 18 ++++++---
 .../ozone/container/ozoneimpl/OzoneContainer.java  |  4 +-
 .../container/common/impl/TestContainerSet.java    |  6 +--
 ...stContainerReconciliationWithMockDatanodes.java |  6 +--
 .../container/keyvalue/TestKeyValueHandler.java    |  4 +-
 ...nner.java => TestOnDemandContainerScanner.java} | 34 +++++++++++++---
 ...> TestOnDemandContainerScannerIntegration.java} | 47 ++++++++++++++++++++--
 10 files changed, 129 insertions(+), 49 deletions(-)

diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
index e03e61605eb..8e998c1aef2 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/impl/ContainerSet.java
@@ -48,7 +48,7 @@
 import org.apache.hadoop.ozone.container.common.statemachine.StateContext;
 import org.apache.hadoop.ozone.container.common.utils.ContainerLogger;
 import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
-import 
org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerDataScanner;
+import org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerScanner;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,7 +69,7 @@ public class ContainerSet implements Iterable<Container<?>> {
   private long recoveringTimeout;
   private final Table<ContainerID, String> containerIdsTable;
   // Handler that will be invoked when a scan of a container in this set is 
requested.
-  private OnDemandContainerDataScanner containerScanner;
+  private OnDemandContainerScanner containerScanner;
 
   public static ContainerSet newReadOnlyContainerSet(long recoveringTimeout) {
     return new ContainerSet(null, recoveringTimeout);
@@ -131,7 +131,7 @@ public void ensureContainerNotMissing(long containerId, 
State state) throws Stor
   /**
    * @param scanner The scanner instance will be invoked when a scan of a 
container in this set is requested.
    */
-  public void registerOnDemandScanner(OnDemandContainerDataScanner scanner) {
+  public void registerOnDemandScanner(OnDemandContainerScanner scanner) {
     this.containerScanner = scanner;
   }
 
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerMetadataScanner.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerMetadataScanner.java
index cb57fc1929f..ae634a8a62f 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerMetadataScanner.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerMetadataScanner.java
@@ -54,24 +54,7 @@ public Iterator<Container<?>> getContainerIterator() {
   @Override
   public void scanContainer(Container<?> container)
       throws IOException, InterruptedException {
-    if (!scanHelper.shouldScanMetadata(container)) {
-      return;
-    }
-
-    long containerID = container.getContainerData().getContainerID();
-
-    MetadataScanResult result = container.scanMetaData();
-    if (result.isDeleted()) {
-      LOG.debug("Container [{}] has been deleted during the metadata scan.", 
containerID);
-      return;
-    }
-    if (result.hasErrors()) {
-      scanHelper.handleUnhealthyScanResult(containerID, result);
-    }
-
-    // Do not update the scan timestamp after the scan since this was just a
-    // metadata scan, not a full data scan.
-    metrics.incNumContainersScanned();
+    scanHelper.scanMetadata(container);
   }
 
   @Override
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/ContainerScanHelper.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/ContainerScanHelper.java
index 6cd7b80f02f..c0e16c7de93 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/ContainerScanHelper.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/ContainerScanHelper.java
@@ -63,7 +63,7 @@ public void scanData(Container<?> container, 
DataTransferThrottler throttler, Ca
     }
     ContainerData containerData = container.getContainerData();
     long containerId = containerData.getContainerID();
-    logScanStart(containerData);
+    logScanStart(containerData, "data");
     DataScanResult result = container.scanData(throttler, canceler);
 
     if (result.isDeleted()) {
@@ -88,6 +88,32 @@ public void scanData(Container<?> container, 
DataTransferThrottler throttler, Ca
     logScanCompleted(containerData, now);
   }
 
+  public void scanMetadata(Container<?> container)
+      throws IOException, InterruptedException {
+    if (!shouldScanMetadata(container)) {
+      return;
+    }
+    ContainerData containerData = container.getContainerData();
+    long containerId = containerData.getContainerID();
+    logScanStart(containerData, "only metadata");
+
+    MetadataScanResult result = container.scanMetaData();
+    if (result.isDeleted()) {
+      log.debug("Container [{}] has been deleted during metadata scan.", 
containerId);
+      return;
+    }
+    if (result.hasErrors()) {
+      handleUnhealthyScanResult(containerId, result);
+    }
+
+    Instant now = Instant.now();
+    // Do not update the scan timestamp after the scan since this was just a
+    // metadata scan, not a full data scan.
+    metrics.incNumContainersScanned();
+    // Even if the container was deleted, mark the scan as completed since we 
already logged it as starting.
+    logScanCompleted(containerData, now);
+  }
+
   public void handleUnhealthyScanResult(long containerID, ScanResult result) 
throws IOException {
 
     log.error("Corruption detected in container [{}]. Marking it UNHEALTHY. 
{}", containerID, result);
@@ -147,12 +173,12 @@ private boolean recentlyScanned(ContainerData 
containerData) {
     return recentlyScanned;
   }
 
-  private void logScanStart(ContainerData containerData) {
+  private void logScanStart(ContainerData containerData, String scanType) {
     if (log.isDebugEnabled()) {
       Optional<Instant> scanTimestamp = containerData.lastDataScanTime();
       Object lastScanTime = scanTimestamp.map(ts -> "at " + 
ts).orElse("never");
-      log.debug("Scanning container {}, last scanned {}",
-          containerData.getContainerID(), lastScanTime);
+      log.debug("Scanning {} of container {}, last scanned {}",
+          scanType, containerData.getContainerID(), lastScanTime);
     }
   }
 
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OnDemandContainerDataScanner.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OnDemandContainerScanner.java
similarity index 89%
rename from 
hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OnDemandContainerDataScanner.java
rename to 
hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OnDemandContainerScanner.java
index a85358406bd..62f0c08d71e 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OnDemandContainerDataScanner.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OnDemandContainerScanner.java
@@ -32,10 +32,12 @@
 
 /**
  * Class for performing on demand scans of containers.
+ * Note: [OPEN] containers are scanned only for metadata,
+ *       [CLOSED, QUASI_CLOSED] containers are scanned for metadata and data.
  */
-public final class OnDemandContainerDataScanner {
+public final class OnDemandContainerScanner {
   private static final Logger LOG =
-      LoggerFactory.getLogger(OnDemandContainerDataScanner.class);
+      LoggerFactory.getLogger(OnDemandContainerScanner.class);
 
   private final ExecutorService scanExecutor;
   private final DataTransferThrottler throttler;
@@ -46,7 +48,7 @@ public final class OnDemandContainerDataScanner {
   private final ContainerScanHelper scannerHelper;
   private final ContainerScanHelper scannerHelperWithoutGap;
 
-  public OnDemandContainerDataScanner(
+  public OnDemandContainerScanner(
       ContainerScannerConfiguration conf, ContainerController controller) {
     throttler = new DataTransferThrottler(
         conf.getOnDemandBandwidthPerVolume());
@@ -77,7 +79,7 @@ public Optional<Future<?>> 
scanContainerWithoutGap(Container<?> container) {
   }
 
   private Optional<Future<?>> scanContainer(Container<?> container, 
ContainerScanHelper helper) {
-    if (!helper.shouldScanData(container)) {
+    if (!helper.shouldScanMetadata(container)) {
       return Optional.empty();
     }
 
@@ -103,7 +105,13 @@ private void removeContainerFromScheduledContainers(
 
   private void performOnDemandScan(Container<?> container, ContainerScanHelper 
helper) {
     try {
-      helper.scanData(container, throttler, canceler);
+      if (helper.shouldScanData(container)) {
+        helper.scanData(container, throttler, canceler);
+      } else {
+        // for containers that qualify for metadata scan and not data scan,
+        // like OPEN containers, trigger a metadata-only scan
+        helper.scanMetadata(container);
+      }
     } catch (IOException e) {
       LOG.warn("Unexpected exception while scanning container "
           + container.getContainerData().getContainerID(), e);
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
index cc46784bbef..985909a294e 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/OzoneContainer.java
@@ -125,7 +125,7 @@ public class OzoneContainer {
   private final XceiverServerSpi readChannel;
   private final ContainerController controller;
   private BackgroundContainerMetadataScanner metadataScanner;
-  private OnDemandContainerDataScanner onDemandScanner;
+  private OnDemandContainerScanner onDemandScanner;
   private List<BackgroundContainerDataScanner> dataScanners;
   private List<AbstractBackgroundContainerScanner> backgroundScanners;
   private final BlockDeletingService blockDeletingService;
@@ -439,7 +439,7 @@ private void 
initOnDemandContainerScanner(ContainerScannerConfiguration c) {
           "so the on-demand container data scanner will not start.");
       return;
     }
-    onDemandScanner = new OnDemandContainerDataScanner(c, controller);
+    onDemandScanner = new OnDemandContainerScanner(c, controller);
     containerSet.registerOnDemandScanner(onDemandScanner);
   }
 
diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
index 10c897b1f54..ad12e1be8ed 100644
--- 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/impl/TestContainerSet.java
@@ -51,7 +51,7 @@
 import org.apache.hadoop.ozone.container.keyvalue.ContainerLayoutTestInfo;
 import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainer;
 import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainerData;
-import 
org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerDataScanner;
+import org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerScanner;
 
 /**
  * Class used to test ContainerSet operations.
@@ -296,7 +296,7 @@ public void testContainerScanHandler(ContainerLayoutVersion 
layout) throws Excep
     containerSet.scanContainer(FIRST_ID);
 
     AtomicLong invocationCount = new AtomicLong();
-    OnDemandContainerDataScanner mockScanner = 
mock(OnDemandContainerDataScanner.class);
+    OnDemandContainerScanner mockScanner = 
mock(OnDemandContainerScanner.class);
     when(mockScanner.scanContainer(any())).then(inv -> {
       KeyValueContainer c = inv.getArgument(0);
       // If the handler was incorrectly triggered for a non-existent 
container, this assert would fail.
@@ -323,7 +323,7 @@ public void 
testContainerScanHandlerWithoutGap(ContainerLayoutVersion layout) th
     containerSet.scanContainer(FIRST_ID);
 
     AtomicLong invocationCount = new AtomicLong();
-    OnDemandContainerDataScanner mockScanner = 
mock(OnDemandContainerDataScanner.class);
+    OnDemandContainerScanner mockScanner = 
mock(OnDemandContainerScanner.class);
     when(mockScanner.scanContainerWithoutGap(any())).then(inv -> {
       KeyValueContainer c = inv.getArgument(0);
       // If the handler was incorrectly triggered for a non-existent 
container, this assert would fail.
diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestContainerReconciliationWithMockDatanodes.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestContainerReconciliationWithMockDatanodes.java
index e95fc3ab3ec..7fce76a0e4d 100644
--- 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestContainerReconciliationWithMockDatanodes.java
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestContainerReconciliationWithMockDatanodes.java
@@ -81,7 +81,7 @@
 import org.apache.hadoop.ozone.container.keyvalue.helpers.BlockUtils;
 import org.apache.hadoop.ozone.container.ozoneimpl.ContainerController;
 import 
org.apache.hadoop.ozone.container.ozoneimpl.ContainerScannerConfiguration;
-import 
org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerDataScanner;
+import org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerScanner;
 import org.apache.ozone.test.GenericTestUtils;
 import org.apache.ratis.thirdparty.com.google.protobuf.ByteString;
 import org.junit.jupiter.api.AfterAll;
@@ -299,7 +299,7 @@ private static void mockContainerProtocolCalls() {
   private static class MockDatanode {
     private final KeyValueHandler handler;
     private final DatanodeDetails dnDetails;
-    private final OnDemandContainerDataScanner onDemandScanner;
+    private final OnDemandContainerScanner onDemandScanner;
     private final ContainerSet containerSet;
     private final OzoneConfiguration conf;
 
@@ -322,7 +322,7 @@ private static class MockDatanode {
 
       ContainerController controller = new ContainerController(containerSet,
           
Collections.singletonMap(ContainerProtos.ContainerType.KeyValueContainer, 
handler));
-      onDemandScanner = new OnDemandContainerDataScanner(
+      onDemandScanner = new OnDemandContainerScanner(
           conf.getObject(ContainerScannerConfiguration.class), controller);
       // Register the on-demand container scanner with the container set used 
by the KeyValueHandler.
       containerSet.registerOnDemandScanner(onDemandScanner);
diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueHandler.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueHandler.java
index 503d8c0855d..97fd5a4b8c1 100644
--- 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueHandler.java
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueHandler.java
@@ -98,7 +98,7 @@
 import org.apache.hadoop.ozone.container.common.volume.VolumeSet;
 import org.apache.hadoop.ozone.container.ozoneimpl.ContainerController;
 import 
org.apache.hadoop.ozone.container.ozoneimpl.ContainerScannerConfiguration;
-import 
org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerDataScanner;
+import org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerScanner;
 import org.apache.hadoop.util.Sets;
 import org.apache.hadoop.util.Time;
 import org.apache.ozone.test.GenericTestUtils;
@@ -818,7 +818,7 @@ private KeyValueHandler createKeyValueHandler(Path path) 
throws IOException {
     // Register the on-demand container scanner with the container set used by 
the KeyValueHandler.
     ContainerController controller = new ContainerController(containerSet,
         Collections.singletonMap(ContainerType.KeyValueContainer, kvHandler));
-    OnDemandContainerDataScanner onDemandScanner = new 
OnDemandContainerDataScanner(
+    OnDemandContainerScanner onDemandScanner = new OnDemandContainerScanner(
         conf.getObject(ContainerScannerConfiguration.class), controller);
     containerSet.registerOnDemandScanner(onDemandScanner);
 
diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestOnDemandContainerDataScanner.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestOnDemandContainerScanner.java
similarity index 91%
rename from 
hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestOnDemandContainerDataScanner.java
rename to 
hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestOnDemandContainerScanner.java
index 42b3da8050d..98a65ca761d 100644
--- 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestOnDemandContainerDataScanner.java
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestOnDemandContainerScanner.java
@@ -64,16 +64,16 @@
  * Unit tests for the on-demand container scanner.
  */
 @MockitoSettings(strictness = Strictness.LENIENT)
-public class TestOnDemandContainerDataScanner extends
+public class TestOnDemandContainerScanner extends
     TestContainerScannersAbstract {
-  
-  private OnDemandContainerDataScanner onDemandScanner;
+
+  private OnDemandContainerScanner onDemandScanner;
 
   @Override
   @BeforeEach
   public void setup() {
     super.setup();
-    onDemandScanner = new OnDemandContainerDataScanner(conf, controller);
+    onDemandScanner = new OnDemandContainerScanner(conf, controller);
   }
 
   @Test
@@ -169,6 +169,28 @@ public void testSameContainerQueuedMultipleTimes() throws 
Exception {
         eq(corruptData.getContainerData().getContainerID()), any());
   }
 
+  @Test
+  public void testSameOpenContainerQueuedMultipleTimes() throws Exception {
+    //Given a container that has not finished scanning
+    CountDownLatch latch = new CountDownLatch(1);
+    when(openCorruptMetadata.scanMetaData())
+        .thenAnswer((Answer<ScanResult>) invocation -> {
+          latch.await();
+          return getUnhealthyDataScanResult();
+        });
+    Optional<Future<?>> onGoingScan = 
onDemandScanner.scanContainer(openCorruptMetadata);
+    assertTrue(onGoingScan.isPresent());
+    assertFalse(onGoingScan.get().isDone());
+    //When scheduling the same container again
+    Optional<Future<?>> secondScan = 
onDemandScanner.scanContainer(openCorruptMetadata);
+    //Then the second scan is not scheduled and the first scan can still finish
+    assertFalse(secondScan.isPresent());
+    latch.countDown();
+    onGoingScan.get().get();
+    verify(controller, atLeastOnce()).markContainerUnhealthy(
+        eq(openCorruptMetadata.getContainerData().getContainerID()), any());
+  }
+
   @Test
   @Override
   public void testScannerMetrics() throws Exception {
@@ -184,7 +206,7 @@ public void testScannerMetrics() throws Exception {
     //Containers with shouldScanData = false shouldn't increase
     // the number of scanned containers
     assertEquals(0, metrics.getNumUnHealthyContainers());
-    assertEquals(2, metrics.getNumContainersScanned());
+    assertEquals(4, metrics.getNumContainersScanned());
   }
 
   @Test
@@ -209,7 +231,7 @@ public void testUnhealthyContainersDetected() throws 
Exception {
     scanContainer(corruptData);
     verifyContainerMarkedUnhealthy(corruptData, atLeastOnce());
     scanContainer(openCorruptMetadata);
-    verifyContainerMarkedUnhealthy(openCorruptMetadata, never());
+    verifyContainerMarkedUnhealthy(openCorruptMetadata, atLeastOnce());
     scanContainer(openContainer);
     verifyContainerMarkedUnhealthy(openContainer, never());
     // Deleted containers should not be marked unhealthy
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerDataScannerIntegration.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerScannerIntegration.java
similarity index 79%
rename from 
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerDataScannerIntegration.java
rename to 
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerScannerIntegration.java
index d3b3ed46fde..136a307e0ab 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerDataScannerIntegration.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/dn/scanner/TestOnDemandContainerScannerIntegration.java
@@ -26,6 +26,8 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.Collection;
+import java.util.EnumSet;
+import java.util.Set;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
 import 
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto.State;
@@ -33,7 +35,7 @@
 import org.apache.hadoop.ozone.container.common.utils.ContainerLogger;
 import org.apache.hadoop.ozone.container.keyvalue.TestContainerCorruptions;
 import 
org.apache.hadoop.ozone.container.ozoneimpl.ContainerScannerConfiguration;
-import 
org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerDataScanner;
+import org.apache.hadoop.ozone.container.ozoneimpl.OnDemandContainerScanner;
 import org.apache.ozone.test.GenericTestUtils;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -44,7 +46,7 @@
  * is triggered when there is an error while a client interacts with a
  * container.
  */
-class TestOnDemandContainerDataScannerIntegration
+class TestOnDemandContainerScannerIntegration
     extends TestContainerScannerIntegrationAbstract {
 
   private final GenericTestUtils.LogCapturer logCapturer =
@@ -72,6 +74,13 @@ static Collection<TestContainerCorruptions> 
supportedCorruptionTypes() {
         TestContainerCorruptions.TRUNCATED_BLOCK);
   }
 
+  static Collection<TestContainerCorruptions> 
supportedCorruptionTypesForOpen() {
+    Set<TestContainerCorruptions> set = 
EnumSet.copyOf(supportedCorruptionTypes());
+    // Open containers will be checked only for metadata corruption, so 
missing block is not a valid corruption type.
+    set.remove(TestContainerCorruptions.MISSING_BLOCK);
+    return set;
+  }
+
   @BeforeAll
   static void init() throws Exception {
     OzoneConfiguration ozoneConfig = new OzoneConfiguration();
@@ -90,7 +99,7 @@ static void init() throws Exception {
   }
 
   /**
-   * {@link OnDemandContainerDataScanner} should detect corrupted blocks
+   * {@link OnDemandContainerScanner} should detect corrupted blocks
    * in a closed container when a client reads from it.
    */
   @ParameterizedTest
@@ -137,4 +146,36 @@ void testCorruptionDetected(TestContainerCorruptions 
corruption)
       assertEquals(newReportedDataChecksum, 
updatedChecksumInfo.getContainerMerkleTree().getDataChecksum());
     }
   }
+
+  /**
+   * {@link OnDemandContainerScanner} should detect corrupted blocks
+   * in an open container when a client reads from it.
+   */
+  @ParameterizedTest
+  @MethodSource("supportedCorruptionTypesForOpen")
+  void testCorruptionDetectedForOpenContainers(TestContainerCorruptions 
corruption)
+      throws Exception {
+    String keyName = "keyName";
+
+    long openContainerID = writeDataToOpenContainer();
+    Container<?> openContainer = getDnContainer(openContainerID);
+    assertEquals(State.OPEN, openContainer.getContainerState());
+
+    // Corrupt the container.
+    corruption.applyTo(openContainer);
+    // This method will check that reading from the corrupted key returns an
+    // error to the client.
+    readFromCorruptedKey(keyName);
+
+    // Reading from the corrupted key should have triggered an on-demand scan
+    // of the container, which will detect the corruption.
+    GenericTestUtils.waitFor(
+        () -> openContainer.getContainerState() == State.UNHEALTHY,
+        500, 5000);
+
+    // Wait for SCM to get a report of the unhealthy replica.
+    waitForScmToSeeReplicaState(openContainerID, UNHEALTHY);
+    corruption.assertLogged(openContainerID, 1, logCapturer);
+  }
+
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@ozone.apache.org
For additional commands, e-mail: commits-h...@ozone.apache.org

Reply via email to