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 20f7097f3fc HDDS-15269. Avoid 30s shutdown wait in 
ReconTaskControllerImpl (#10402)
20f7097f3fc is described below

commit 20f7097f3fca45195c63a6a3ce6d0fb7efb0011c
Author: Chi-Hsuan Huang <[email protected]>
AuthorDate: Tue Jun 2 03:04:05 2026 +0800

    HDDS-15269. Avoid 30s shutdown wait in ReconTaskControllerImpl (#10402)
---
 .../ozone/recon/tasks/ReconTaskControllerImpl.java |  7 ++++-
 .../recon/tasks/TestReconTaskControllerImpl.java   | 30 +++++++++++++++++-----
 2 files changed, 30 insertions(+), 7 deletions(-)

diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/ReconTaskControllerImpl.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/ReconTaskControllerImpl.java
index 9ecc2aa2c13..b3f06a61ea3 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/ReconTaskControllerImpl.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/ReconTaskControllerImpl.java
@@ -88,6 +88,7 @@ public class ReconTaskControllerImpl implements 
ReconTaskController {
   private final ReconTaskStatusUpdaterManager taskStatusUpdaterManager;
   private final OMUpdateEventBuffer eventBuffer;
   private ExecutorService eventProcessingExecutor;
+  private volatile boolean running = false;
   private final AtomicBoolean tasksFailed = new AtomicBoolean(false);
   private volatile ReconOMMetadataManager currentOMMetadataManager;
   private final OzoneConfiguration configuration;
@@ -359,6 +360,7 @@ public synchronized void start() {
             .build());
     
     // Start async event processing thread
+    running = true;
     eventProcessingExecutor = Executors.newSingleThreadExecutor(
         new ThreadFactoryBuilder().setNameFormat("ReconEventProcessor-%d")
             .build());
@@ -369,6 +371,9 @@ public synchronized void start() {
   @Override
   public synchronized void stop() {
     LOG.info("Stopping Recon Task Controller.");
+    // Signal the event processing loop to exit on its next poll cycle so the
+    // graceful shutdown below can complete without waiting out the timeout.
+    running = false;
     shutdownExecutorGracefully(this.executorService, "main task executor");
     shutdownExecutorGracefully(this.eventProcessingExecutor, "event processing 
executor");
   }
@@ -481,7 +486,7 @@ private void processTasks(
   private void processBufferedEventsAsync() {
     LOG.info("Started async buffered event processing thread");
     
-    while (!Thread.currentThread().isInterrupted()) {
+    while (running && !Thread.currentThread().isInterrupted()) {
       try {
         ReconEvent event = eventBuffer.poll(1000); // 1 second timeout
         if (event != null) {
diff --git 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestReconTaskControllerImpl.java
 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestReconTaskControllerImpl.java
index da1f790ccdb..c2636701bf3 100644
--- 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestReconTaskControllerImpl.java
+++ 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestReconTaskControllerImpl.java
@@ -54,6 +54,7 @@
 import org.apache.hadoop.ozone.recon.spi.impl.ReconDBProvider;
 import org.apache.hadoop.ozone.recon.tasks.updater.ReconTaskStatusUpdater;
 import 
org.apache.hadoop.ozone.recon.tasks.updater.ReconTaskStatusUpdaterManager;
+import org.apache.hadoop.util.Time;
 import org.apache.ozone.recon.schema.generated.tables.daos.ReconTaskStatusDao;
 import org.apache.ozone.recon.schema.generated.tables.pojos.ReconTaskStatus;
 import org.apache.ozone.test.GenericTestUtils;
@@ -95,6 +96,19 @@ public void setUp() throws IOException {
     reconTaskController.start();
   }
 
+  @Test
+  public void testStopCompletesPromptly() {
+    // stop() must not block on the graceful shutdown timeout. The event
+    // processing loop only exits on interrupt, so a plain shutdown() can never
+    // drain it and awaitTermination would otherwise burn the full 30s timeout.
+    long start = Time.monotonicNow();
+    reconTaskController.stop();
+    long elapsed = Time.monotonicNow() - start;
+    assertThat(elapsed)
+        .as("stop() should return promptly, not wait out the shutdown timeout")
+        .isLessThan(5000L);
+  }
+
   @Test
   public void testRegisterTask() {
     String taskName = "Dummy_" + System.currentTimeMillis();
@@ -596,9 +610,11 @@ public void 
testProcessReInitializationEventWithTaskFailuresAndRetry() throws Ex
         .thenReturn(false)  // First call fails
         .thenReturn(true);  // Second call succeeds
     
-    // Stop async processing to control event processing manually
-    controllerSpy.stop();
-    
+    // Stop async processing on the real controller so we can drive event
+    // processing manually. Stopping controllerSpy would only flip the flag on
+    // the Mockito copy, not the live event-processing thread.
+    controllerImpl.stop();
+
     // Create and manually process a reinitialization event
     ReconTaskReInitializationEvent reinitEvent = new 
ReconTaskReInitializationEvent(
         ReconTaskReInitializationEvent.ReInitializationReason.TASK_FAILURES,
@@ -732,9 +748,11 @@ public void 
testProcessReInitializationEventWithCheckpointedManager() throws Exc
     when(controllerSpy.reInitializeTasks(any(ReconOMMetadataManager.class), 
any()))
         .thenReturn(true);  // Succeed
     
-    // Stop async processing to control event processing manually
-    controllerSpy.stop();
-    
+    // Stop async processing on the real controller so we can drive event
+    // processing manually. Stopping controllerSpy would only flip the flag on
+    // the Mockito copy, not the live event-processing thread.
+    controllerImpl.stop();
+
     // Create reinitialization event with checkpointed manager
     ReconTaskReInitializationEvent reinitEvent = new 
ReconTaskReInitializationEvent(
         ReconTaskReInitializationEvent.ReInitializationReason.BUFFER_OVERFLOW,


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

Reply via email to