This is an automated email from the ASF dual-hosted git repository.
sumitagrawal 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 b95a2ca673 HDDS-7100. Container scanner incorrectly marks containers
unhealthy when DN is shutdown (#4951)
b95a2ca673 is described below
commit b95a2ca6738c128f02037e21c91002088de5d032
Author: Ethan Rose <[email protected]>
AuthorDate: Mon Jun 26 19:28:21 2023 -0700
HDDS-7100. Container scanner incorrectly marks containers unhealthy when DN
is shutdown (#4951)
---
.../container/common/interfaces/Container.java | 6 ++--
.../container/common/volume/MutableVolumeSet.java | 12 +++----
.../container/keyvalue/KeyValueContainer.java | 5 +--
.../container/keyvalue/KeyValueContainerCheck.java | 13 +++++--
.../AbstractBackgroundContainerScanner.java | 41 +++++++++++++---------
.../ozoneimpl/BackgroundContainerDataScanner.java | 3 +-
.../BackgroundContainerMetadataScanner.java | 3 +-
.../ozoneimpl/OnDemandContainerDataScanner.java | 4 +++
.../ozone/container/common/ContainerTestUtils.java | 12 +++++--
.../TestBackgroundContainerDataScanner.java | 33 +++++++++++++++--
.../TestBackgroundContainerMetadataScanner.java | 28 +++++++++++++--
.../ozoneimpl/TestContainerScannersAbstract.java | 3 ++
.../TestOnDemandContainerDataScanner.java | 22 ++++++++++--
13 files changed, 142 insertions(+), 43 deletions(-)
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java
index 9a17d1fc71..5652db5d71 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java
@@ -174,7 +174,7 @@ public interface Container<CONTAINERDATA extends
ContainerData> extends RwLock {
* @return true if the integrity checks pass
* Scan the container metadata to detect corruption.
*/
- boolean scanMetaData();
+ boolean scanMetaData() throws InterruptedException;
/**
* Return if the container data should be checksum verified to detect
@@ -193,6 +193,8 @@ public interface Container<CONTAINERDATA extends
ContainerData> extends RwLock {
* I/O bandwidth throttling (e.g. for shutdown purpose).
* @return true if the checksum verification succeeds
* false otherwise
+ * @throws InterruptedException if the scan is interrupted.
*/
- boolean scanData(DataTransferThrottler throttler, Canceler canceler);
+ boolean scanData(DataTransferThrottler throttler, Canceler canceler)
+ throws InterruptedException;
}
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
index 863f50de52..f4a5d0b9ad 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/MutableVolumeSet.java
@@ -39,12 +39,10 @@ import
org.apache.hadoop.ozone.container.common.impl.StorageLocationReport;
import
org.apache.hadoop.ozone.container.common.statemachine.DatanodeConfiguration;
import org.apache.hadoop.ozone.container.common.statemachine.StateContext;
import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException;
-import org.apache.hadoop.util.ShutdownHookManager;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import static org.apache.hadoop.util.RunJar.SHUTDOWN_HOOK_PRIORITY;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -85,13 +83,13 @@ public class MutableVolumeSet implements VolumeSet {
private final String datanodeUuid;
private String clusterID;
- private Runnable shutdownHook;
private final StorageVolumeChecker volumeChecker;
private Runnable failedVolumeListener;
private StateContext context;
private final StorageVolumeFactory volumeFactory;
private final StorageVolume.VolumeType volumeType;
private int maxVolumeFailuresTolerated;
+ private boolean initialized;
public MutableVolumeSet(String dnUuid, ConfigurationSource conf,
StateContext context, StorageVolume.VolumeType volumeType,
@@ -103,6 +101,7 @@ public class MutableVolumeSet implements VolumeSet {
ConfigurationSource conf, StateContext context,
StorageVolume.VolumeType volumeType, StorageVolumeChecker volumeChecker
) throws IOException {
+ this.initialized = false;
this.context = context;
this.datanodeUuid = dnUuid;
this.clusterID = clusterID;
@@ -201,10 +200,7 @@ public class MutableVolumeSet implements VolumeSet {
}
checkAllVolumes();
-
- // Ensure volume threads are stopped and scm df is saved during shutdown.
- ShutdownHookManager.get().addShutdownHook(this::shutdown,
- SHUTDOWN_HOOK_PRIORITY);
+ initialized = true;
}
/**
@@ -258,7 +254,7 @@ public class MutableVolumeSet implements VolumeSet {
// check failed volume tolerated
if (!hasEnoughVolumes()) {
// on startup, we could not try to stop uninitialized services
- if (shutdownHook == null) {
+ if (!initialized) {
throw new IOException("Don't have enough good volumes on startup,"
+ " bad volumes detected: " + failedVolumes.size()
+ " max tolerated: " + maxVolumeFailuresTolerated);
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java
index 702a9315ec..85e356388d 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java
@@ -878,7 +878,7 @@ public class KeyValueContainer implements
Container<KeyValueContainerData> {
}
@Override
- public boolean scanMetaData() {
+ public boolean scanMetaData() throws InterruptedException {
long containerId = containerData.getContainerID();
KeyValueContainerCheck checker =
new KeyValueContainerCheck(containerData.getMetadataPath(), config,
@@ -900,7 +900,8 @@ public class KeyValueContainer implements
Container<KeyValueContainerData> {
}
@Override
- public boolean scanData(DataTransferThrottler throttler, Canceler canceler) {
+ public boolean scanData(DataTransferThrottler throttler, Canceler canceler)
+ throws InterruptedException {
if (!shouldScanData()) {
throw new IllegalStateException("The checksum verification can not be" +
" done for container in state "
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainerCheck.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainerCheck.java
index 2a83a3892e..0425ed31b7 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainerCheck.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainerCheck.java
@@ -87,7 +87,7 @@ public class KeyValueContainerCheck {
*
* @return true : integrity checks pass, false : otherwise.
*/
- public boolean fastCheck() {
+ public boolean fastCheck() throws InterruptedException {
LOG.debug("Running basic checks for container {};", containerID);
boolean valid = false;
try {
@@ -97,6 +97,10 @@ public class KeyValueContainerCheck {
valid = true;
} catch (IOException e) {
+ if (Thread.currentThread().isInterrupted()) {
+ throw new InterruptedException("Metadata scan of container " +
+ containerID + " interrupted.");
+ }
handleCorruption(e);
}
@@ -114,7 +118,8 @@ public class KeyValueContainerCheck {
*
* @return true : integrity checks pass, false : otherwise.
*/
- public boolean fullCheck(DataTransferThrottler throttler, Canceler canceler)
{
+ public boolean fullCheck(DataTransferThrottler throttler,
+ Canceler canceler) throws InterruptedException {
boolean valid;
try {
@@ -123,6 +128,10 @@ public class KeyValueContainerCheck {
scanData(throttler, canceler);
}
} catch (IOException e) {
+ if (Thread.currentThread().isInterrupted()) {
+ throw new InterruptedException("Data scan of container " + containerID
+
+ " interrupted.");
+ }
handleCorruption(e);
valid = false;
}
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/AbstractBackgroundContainerScanner.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/AbstractBackgroundContainerScanner.java
index c956e9a032..139952d212 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/AbstractBackgroundContainerScanner.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/AbstractBackgroundContainerScanner.java
@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Base class for scheduled scanners on a Datanode.
@@ -37,15 +38,12 @@ public abstract class AbstractBackgroundContainerScanner
extends Thread {
private final long dataScanInterval;
- /**
- * True if the thread is stopping.<p/>
- * Protected by this object's lock.
- */
- private volatile boolean stopping = false;
+ private final AtomicBoolean stopping;
public AbstractBackgroundContainerScanner(String name,
long dataScanInterval) {
this.dataScanInterval = dataScanInterval;
+ this.stopping = new AtomicBoolean(false);
setName(name);
setDaemon(true);
}
@@ -54,7 +52,7 @@ public abstract class AbstractBackgroundContainerScanner
extends Thread {
public final void run() {
AbstractContainerScannerMetrics metrics = getMetrics();
try {
- while (!stopping) {
+ while (!stopping.get()) {
runIteration();
metrics.resetNumContainersScanned();
metrics.resetNumUnhealthyContainers();
@@ -74,7 +72,7 @@ public abstract class AbstractBackgroundContainerScanner
extends Thread {
long startTime = System.nanoTime();
scanContainers();
long totalDuration = System.nanoTime() - startTime;
- if (stopping) {
+ if (stopping.get()) {
return;
}
AbstractContainerScannerMetrics metrics = getMetrics();
@@ -94,10 +92,12 @@ public abstract class AbstractBackgroundContainerScanner
extends Thread {
public final void scanContainers() {
Iterator<Container<?>> itr = getContainerIterator();
- while (!stopping && itr.hasNext()) {
+ while (!stopping.get() && itr.hasNext()) {
Container<?> c = itr.next();
try {
scanContainer(c);
+ } catch (InterruptedException ex) {
+ stopping.set(true);
} catch (IOException ex) {
LOG.warn("Unexpected exception while scanning container "
+ c.getContainerData().getContainerID(), ex);
@@ -107,28 +107,35 @@ public abstract class AbstractBackgroundContainerScanner
extends Thread {
public abstract Iterator<Container<?>> getContainerIterator();
- public abstract void scanContainer(Container<?> c) throws IOException;
+ public abstract void scanContainer(Container<?> c)
+ throws IOException, InterruptedException;
public final void handleRemainingSleep(long remainingSleep) {
if (remainingSleep > 0) {
try {
Thread.sleep(remainingSleep);
} catch (InterruptedException ignored) {
- this.stopping = true;
+ stopping.set(true);
LOG.warn("Background container scan was interrupted.");
Thread.currentThread().interrupt();
}
}
}
+ /**
+ * Shutdown the current container scanning thread.
+ * If the thread is already being shutdown, the call will block until the
+ * shutdown completes.
+ */
public synchronized void shutdown() {
- this.stopping = true;
- this.interrupt();
- try {
- this.join();
- } catch (InterruptedException ex) {
- LOG.warn("Unexpected exception while stopping data scanner.", ex);
- Thread.currentThread().interrupt();
+ if (stopping.compareAndSet(false, true)) {
+ this.interrupt();
+ try {
+ this.join();
+ } catch (InterruptedException ex) {
+ LOG.warn("Unexpected exception while stopping data scanner.", ex);
+ Thread.currentThread().interrupt();
+ }
}
}
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerDataScanner.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerDataScanner.java
index 73539840a1..39bb930386 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerDataScanner.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/ozoneimpl/BackgroundContainerDataScanner.java
@@ -69,7 +69,8 @@ public class BackgroundContainerDataScanner extends
}
@Override
- public void scanContainer(Container<?> c) throws IOException {
+ public void scanContainer(Container<?> c)
+ throws IOException, InterruptedException {
// There is one background container data scanner per volume.
// If the volume fails, its scanning thread should terminate.
if (volume.isFailed()) {
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 ee67b68d3d..75f6488301 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
@@ -57,7 +57,8 @@ public class BackgroundContainerMetadataScanner extends
@VisibleForTesting
@Override
- public void scanContainer(Container<?> container) throws IOException {
+ public void scanContainer(Container<?> container)
+ throws IOException, InterruptedException {
// There is one background container metadata scanner per datanode.
// If this container's volume has failed, skip the container.
// The iterator returned by getContainerIterator may have stale results.
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/OnDemandContainerDataScanner.java
index 460ad50736..4f0aa042a2 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/OnDemandContainerDataScanner.java
@@ -141,6 +141,10 @@ public final class OnDemandContainerDataScanner {
} catch (IOException e) {
LOG.warn("Unexpected exception while scanning container "
+ containerId, e);
+ } catch (InterruptedException ex) {
+ // This should only happen as part of shutdown, which will stop the
+ // ExecutorService.
+ LOG.info("On demand container scan interrupted.");
}
}
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/ContainerTestUtils.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/ContainerTestUtils.java
index 513197b10b..a8fc6e4250 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/ContainerTestUtils.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/ContainerTestUtils.java
@@ -178,11 +178,17 @@ public final class ContainerTestUtils {
when(data.getContainerID()).thenReturn(containerIdSeq.getAndIncrement());
when(c.getContainerData()).thenReturn(data);
when(c.shouldScanData()).thenReturn(shouldScanData);
- when(c.scanData(any(DataTransferThrottler.class), any(Canceler.class)))
- .thenReturn(scanDataSuccess);
when(c.shouldScanMetadata()).thenReturn(true);
- Mockito.lenient().when(c.scanMetaData()).thenReturn(scanMetaDataSuccess);
when(c.getContainerData().getVolume()).thenReturn(vol);
+
+ try {
+ when(c.scanData(any(DataTransferThrottler.class), any(Canceler.class)))
+ .thenReturn(scanDataSuccess);
+ Mockito.lenient().when(c.scanMetaData()).thenReturn(scanMetaDataSuccess);
+ } catch (InterruptedException ex) {
+ // Mockito.when invocations will not throw this exception. It is just
+ // required for compilation.
+ }
}
public static KeyValueContainer addContainerToDeletedDir(
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerDataScanner.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerDataScanner.java
index eb644d0350..134ec050e2 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerDataScanner.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerDataScanner.java
@@ -31,13 +31,17 @@ import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
+import java.time.Duration;
import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto.State.UNHEALTHY;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
@@ -62,7 +66,7 @@ public class TestBackgroundContainerDataScanner extends
@Test
@Override
- public void testRecentlyScannedContainerIsSkipped() {
+ public void testRecentlyScannedContainerIsSkipped() throws Exception {
setScannedTimestampRecent(healthy);
scanner.runIteration();
Mockito.verify(healthy, never()).scanData(any(), any());
@@ -70,7 +74,7 @@ public class TestBackgroundContainerDataScanner extends
@Test
@Override
- public void testPreviouslyScannedContainerIsScanned() {
+ public void testPreviouslyScannedContainerIsScanned() throws Exception {
// If the last scan time is before than the configured gap, the container
// should be scanned.
setScannedTimestampOld(healthy);
@@ -80,7 +84,7 @@ public class TestBackgroundContainerDataScanner extends
@Test
@Override
- public void testUnscannedContainerIsScanned() {
+ public void testUnscannedContainerIsScanned() throws Exception {
// If there is no last scanned time, the container should be scanned.
Mockito.when(healthy.getContainerData().lastDataScanTime())
.thenReturn(Optional.empty());
@@ -209,4 +213,27 @@ public class TestBackgroundContainerDataScanner extends
Mockito.verify(corruptData, never()).scanData(any(), any());
Mockito.verify(openCorruptMetadata, never()).scanData(any(), any());
}
+
+
+ @Test
+ @Override
+ public void testShutdownDuringScan() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ // Make the data scan block until interrupt.
+ Mockito.when(healthy.scanData(any(), any())).then(i -> {
+ latch.countDown();
+ Thread.sleep(Duration.ofDays(1).toMillis());
+ return null;
+ });
+
+ scanner.start();
+ // Wait for the scanner to reach the healthy container.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ // Terminate the scanner while it is blocked scanning the healthy
container.
+ scanner.shutdown();
+ // The container should remain healthy.
+ verifyContainerMarkedUnhealthy(healthy, never());
+
+ }
}
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerMetadataScanner.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerMetadataScanner.java
index 712e89fde4..ff542d667a 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerMetadataScanner.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestBackgroundContainerMetadataScanner.java
@@ -31,7 +31,10 @@ import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
+import java.time.Duration;
import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto.State.UNHEALTHY;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -62,7 +65,7 @@ public class TestBackgroundContainerMetadataScanner extends
@Test
@Override
- public void testRecentlyScannedContainerIsSkipped() {
+ public void testRecentlyScannedContainerIsSkipped() throws Exception {
// If the last scan time is before than the configured gap, the container
// should be scanned.
setScannedTimestampRecent(healthy);
@@ -72,7 +75,7 @@ public class TestBackgroundContainerMetadataScanner extends
@Test
@Override
- public void testPreviouslyScannedContainerIsScanned() {
+ public void testPreviouslyScannedContainerIsScanned() throws Exception {
setScannedTimestampOld(healthy);
scanner.runIteration();
Mockito.verify(healthy, atLeastOnce()).scanMetaData();
@@ -200,4 +203,25 @@ public class TestBackgroundContainerMetadataScanner extends
Mockito.verify(corruptData, never()).scanMetaData();
Mockito.verify(openCorruptMetadata, never()).scanMetaData();
}
+
+ @Test
+ @Override
+ public void testShutdownDuringScan() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ // Make the metadata scan block until interrupt.
+ Mockito.when(healthy.scanMetaData()).then(i -> {
+ latch.countDown();
+ Thread.sleep(Duration.ofDays(1).toMillis());
+ return null;
+ });
+
+ scanner.start();
+ // Wait for the scanner to reach the healthy container.
+ assertTrue(latch.await(5, TimeUnit.SECONDS));
+ // Terminate the scanner while it is blocked scanning the healthy
container.
+ scanner.shutdown();
+ // The container should remain healthy.
+ verifyContainerMarkedUnhealthy(healthy, never());
+ }
}
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestContainerScannersAbstract.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestContainerScannersAbstract.java
index 504d4304f1..1298eb8cad 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestContainerScannersAbstract.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/ozoneimpl/TestContainerScannersAbstract.java
@@ -110,6 +110,9 @@ public abstract class TestContainerScannersAbstract {
@Test
public abstract void testWithVolumeFailure() throws Exception;
+ @Test
+ public abstract void testShutdownDuringScan() throws Exception;
+
@Test
public abstract void testUnhealthyContainerNotRescanned() throws Exception;
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/TestOnDemandContainerDataScanner.java
index e89cecfae0..50fb1355e2 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/TestOnDemandContainerDataScanner.java
@@ -33,6 +33,7 @@ import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
@@ -222,6 +223,24 @@ public class TestOnDemandContainerDataScanner extends
assertEquals(0, metrics.getNumUnHealthyContainers());
}
+ @Test
+ @Override
+ public void testShutdownDuringScan() throws Exception {
+ // Make the on demand scan block until interrupt.
+ Mockito.when(healthy.scanData(any(), any())).then(i -> {
+ Thread.sleep(Duration.ofDays(1).toMillis()); return null;
+ });
+
+ // Start the blocking scan.
+ OnDemandContainerDataScanner.init(conf, controller);
+ OnDemandContainerDataScanner.scanContainer(healthy);
+ // Shut down the on demand scanner. This will interrupt the blocked scan
+ // on the healthy container.
+ OnDemandContainerDataScanner.shutdown();
+ // Interrupting the healthy container's scan should not mark it unhealthy.
+ verifyContainerMarkedUnhealthy(healthy, never());
+ }
+
@Test
@Override
public void testUnhealthyContainerNotRescanned() throws Exception {
@@ -258,8 +277,7 @@ public class TestOnDemandContainerDataScanner extends
assertEquals(0, metrics.getNumUnHealthyContainers());
}
- private void scanContainer(Container<?> container)
- throws Exception {
+ private void scanContainer(Container<?> container) throws Exception {
OnDemandContainerDataScanner.init(conf, controller);
Optional<Future<?>> scanFuture =
OnDemandContainerDataScanner.scanContainer(container);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]