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 20dca25d393 HDDS-13648. Update NSSummary rebuilding implementation to 
queue based approach. (#9009)
20dca25d393 is described below

commit 20dca25d393d4257aaff41ed872aca4b4e02d761
Author: Devesh Kumar Singh <[email protected]>
AuthorDate: Tue Sep 9 20:49:19 2025 +0530

    HDDS-13648. Update NSSummary rebuilding implementation to queue based 
approach. (#9009)
---
 .../org/apache/hadoop/ozone/recon/ReconUtils.java  |  45 ------
 .../recon/spi/ReconNamespaceSummaryManager.java    |   3 -
 .../spi/impl/ReconNamespaceSummaryManagerImpl.java |   8 --
 .../hadoop/ozone/recon/tasks/NSSummaryTask.java    |  36 ++++-
 .../recon/tasks/NSSummaryTaskDbEventHandler.java   |   2 +-
 .../upgrade/NSSummaryAggregatedTotalsUpgrade.java  |  19 +--
 .../recon/api/TestNSSummaryEndpointWithFSO.java    |  38 -----
 .../recon/tasks/TestNSSummaryUnifiedControl.java   |  48 -------
 .../TestNSSummaryAggregatedTotalsUpgrade.java      | 160 +++++++++++++++++++++
 9 files changed, 201 insertions(+), 158 deletions(-)

diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconUtils.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconUtils.java
index 5a367a8baad..2758dfb34cc 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconUtils.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconUtils.java
@@ -49,8 +49,6 @@
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 import javax.ws.rs.core.Response;
 import org.apache.commons.io.FileUtils;
@@ -97,15 +95,6 @@ public class ReconUtils {
   private static Logger log = LoggerFactory.getLogger(
       ReconUtils.class);
 
-  // Use NSSummaryTask's unified rebuild control instead of separate tracking
-  private static final ExecutorService NSSUMMARY_REBUILD_EXECUTOR =
-      Executors.newSingleThreadExecutor(r -> {
-        Thread t = new Thread(r);
-        t.setName("RebuildNSSummaryThread");
-        t.setDaemon(true); // Optional: allows JVM to exit without waiting
-        return t;
-      });
-
   public ReconUtils() {
   }
 
@@ -119,33 +108,6 @@ public static 
org.apache.hadoop.ozone.recon.tasks.NSSummaryTask.RebuildState get
     return org.apache.hadoop.ozone.recon.tasks.NSSummaryTask.getRebuildState();
   }
 
-  /**
-   * Convenience method to trigger asynchronous NSSummary tree rebuild.
-   * Uses the unified control mechanism in NSSummaryTask.
-   * 
-   * @param reconNamespaceSummaryManager The namespace summary manager
-   * @param omMetadataManager The OM metadata manager
-   * @return true if rebuild was triggered successfully, false otherwise
-   */
-  public static boolean triggerAsyncNSSummaryRebuild(
-      ReconNamespaceSummaryManager reconNamespaceSummaryManager,
-      ReconOMMetadataManager omMetadataManager) {
-
-    // Submit rebuild task to single thread executor for async execution
-    NSSUMMARY_REBUILD_EXECUTOR.submit(() -> {
-      try {
-
-        // This will go through NSSummaryTask's unified control mechanism
-        reconNamespaceSummaryManager.rebuildNSSummaryTree(omMetadataManager);
-        log.info("Async NSSummary tree rebuild completed successfully.");
-      } catch (Exception e) {
-        log.error("Async NSSummary tree rebuild failed.", e);
-      }
-    });
-    
-    return true;
-  }
-
   public static File getReconScmDbDir(ConfigurationSource conf) {
     return new ReconUtils().getReconDbDir(conf, OZONE_RECON_SCM_DB_DIR);
   }
@@ -299,13 +261,6 @@ public static StringBuilder constructFullPathPrefix(long 
initialParentId, String
             "deletion, returning empty string for path construction.");
         throw new ServiceNotReadyException("Service is initializing. Please 
try again later.");
       }
-      if (nsSummary.getParentId() == -1) {
-        // Trigger async rebuild using unified control mechanism
-        triggerAsyncNSSummaryRebuild(reconNamespaceSummaryManager, 
omMetadataManager);
-        log.warn(
-            "NSSummary tree corruption detected, rebuild triggered. Returning 
empty string for path construction.");
-        throw new ServiceNotReadyException("Service is initializing. Please 
try again later.");
-      }
       // On the last pass, dir-name will be empty and parent will be zero, 
indicating the loop should end.
       if (!nsSummary.getDirName().isEmpty()) {
         pathSegments.add(nsSummary.getDirName());
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconNamespaceSummaryManager.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconNamespaceSummaryManager.java
index e166466cd56..0c59f0921b4 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconNamespaceSummaryManager.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconNamespaceSummaryManager.java
@@ -22,7 +22,6 @@
 import org.apache.hadoop.hdds.utils.db.BatchOperation;
 import org.apache.hadoop.hdds.utils.db.DBStore;
 import org.apache.hadoop.hdds.utils.db.RDBBatchOperation;
-import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.recon.api.types.NSSummary;
 import org.apache.hadoop.ozone.recon.spi.impl.ReconDBProvider;
 
@@ -52,6 +51,4 @@ void batchStoreNSSummaries(BatchOperation batch, long 
objectId,
 
   void commitBatchOperation(RDBBatchOperation rdbBatchOperation)
       throws IOException;
-
-  void rebuildNSSummaryTree(OMMetadataManager omMetadataManager);
 }
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconNamespaceSummaryManagerImpl.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconNamespaceSummaryManagerImpl.java
index 536fce1e8fe..1d0a7a0d617 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconNamespaceSummaryManagerImpl.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconNamespaceSummaryManagerImpl.java
@@ -26,7 +26,6 @@
 import org.apache.hadoop.hdds.utils.db.DBStore;
 import org.apache.hadoop.hdds.utils.db.RDBBatchOperation;
 import org.apache.hadoop.hdds.utils.db.Table;
-import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.recon.api.types.NSSummary;
 import org.apache.hadoop.ozone.recon.spi.ReconNamespaceSummaryManager;
 import org.apache.hadoop.ozone.recon.tasks.NSSummaryTask;
@@ -105,13 +104,6 @@ public void commitBatchOperation(RDBBatchOperation 
rdbBatchOperation)
     this.namespaceDbStore.commitBatchOperation(rdbBatchOperation);
   }
 
-  @Override
-  public void rebuildNSSummaryTree(OMMetadataManager omMetadataManager) {
-    // This method is called by the unified ReconUtils.triggerNSSummaryRebuild
-    // It should only handle the actual rebuild logic without state management
-    nsSummaryTask.reprocess(omMetadataManager);
-  }
-
   public Table getNSSummaryTable() {
     return nsSummaryTable;
   }
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTask.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTask.java
index 1291a6cc9e7..6b4a5cce0e7 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTask.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTask.java
@@ -205,9 +205,14 @@ public TaskResult reprocess(OMMetadataManager 
omMetadataManager) {
       LOG.info("NSSummary tree rebuild is already in progress, skipping 
duplicate request.");
       return buildTaskResult(false);
     }
-    
+
     if (!REBUILD_STATE.compareAndSet(currentState, RebuildState.RUNNING)) {
-      LOG.info("Failed to acquire rebuild lock, another thread may have 
started rebuild.");
+      // Check if another thread successfully started the rebuild
+      if (REBUILD_STATE.get() == RebuildState.RUNNING) {
+        LOG.info("Rebuild already in progress by another thread, returning 
success");
+        return buildTaskResult(true);
+      }
+      LOG.info("Failed to acquire rebuild lock, unknown state");
       return buildTaskResult(false);
     }
 
@@ -250,7 +255,7 @@ protected TaskResult executeReprocess(OMMetadataManager 
omMetadataManager, long
     ThreadFactory threadFactory = new ThreadFactoryBuilder()
         .setNameFormat("Recon-NSSummaryTask-%d")
         .build();
-    ExecutorService executorService = Executors.newFixedThreadPool(2,
+    ExecutorService executorService = Executors.newFixedThreadPool(3,
         threadFactory);
     boolean success = false;
     try {
@@ -263,14 +268,31 @@ protected TaskResult executeReprocess(OMMetadataManager 
omMetadataManager, long
         }
       }
       success = true;
-      
-    } catch (InterruptedException | ExecutionException ex) {
-      LOG.error("Error while reprocessing NSSummary table in Recon DB.", ex);
+
+    } catch (InterruptedException ex) {
+      Thread.currentThread().interrupt();
+      LOG.error("NSSummaryTask was interrupted.", ex);
+      REBUILD_STATE.set(RebuildState.FAILED);
+      return buildTaskResult(false);
+    } catch (ExecutionException ex) {
+      LOG.error("Error while reprocessing NSSummary table in Recon DB.", 
ex.getCause());
       REBUILD_STATE.set(RebuildState.FAILED);
       return buildTaskResult(false);
-      
     } finally {
       executorService.shutdown();
+      // Deterministic resource cleanup with timeout
+      try {
+        // get() ensures the work is done. awaitTermination ensures the 
workers are also verifiably gone.
+        // It turns an asynchronous shutdown into a synchronous, deterministic 
one
+        if (!executorService.awaitTermination(5, TimeUnit.MINUTES)) {
+          LOG.warn("Executor service for NSSummaryTask did not terminate in 
the specified time.");
+          executorService.shutdownNow();
+        }
+      } catch (InterruptedException ex) {
+        LOG.error("NSSummaryTask executor service termination was 
interrupted.", ex);
+        executorService.shutdownNow();
+        Thread.currentThread().interrupt();
+      }
 
       long endTime = System.nanoTime();
       // Convert to milliseconds
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTaskDbEventHandler.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTaskDbEventHandler.java
index a45930f5bb2..5d2f747f940 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTaskDbEventHandler.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTaskDbEventHandler.java
@@ -261,7 +261,7 @@ protected boolean flushAndCommitUpdatedNSToDB(Map<Long, 
NSSummary> nsSummaryMap,
     try {
       updateNSSummariesToDB(nsSummaryMap, objectIdsToBeDeleted);
     } catch (IOException e) {
-      LOG.error("Unable to write Namespace Summary data in Recon DB.", e);
+      LOG.error("Unable to write Namespace Summary data in Recon DB. 
batchSize={}", nsSummaryMap.size(), e);
       return false;
     } finally {
       nsSummaryMap.clear();
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/upgrade/NSSummaryAggregatedTotalsUpgrade.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/upgrade/NSSummaryAggregatedTotalsUpgrade.java
index 5f3817a2fd3..af69ea8b88c 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/upgrade/NSSummaryAggregatedTotalsUpgrade.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/upgrade/NSSummaryAggregatedTotalsUpgrade.java
@@ -22,9 +22,8 @@
 import com.google.inject.Injector;
 import javax.sql.DataSource;
 import org.apache.hadoop.ozone.recon.ReconGuiceServletContextListener;
-import org.apache.hadoop.ozone.recon.ReconUtils;
-import org.apache.hadoop.ozone.recon.recovery.ReconOMMetadataManager;
-import org.apache.hadoop.ozone.recon.spi.ReconNamespaceSummaryManager;
+import org.apache.hadoop.ozone.recon.tasks.ReconTaskController;
+import org.apache.hadoop.ozone.recon.tasks.ReconTaskReInitializationEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,12 +49,16 @@ public void execute(DataSource source) throws Exception {
           "Guice injector not initialized. NSSummary rebuild cannot proceed 
during upgrade.");
     }
 
-    ReconNamespaceSummaryManager nsMgr = 
injector.getInstance(ReconNamespaceSummaryManager.class);
-    ReconOMMetadataManager omMgr = 
injector.getInstance(ReconOMMetadataManager.class);
-
-    // Fire and forget: unified control using ReconUtils -> NSSummaryTask
+    ReconTaskController reconTaskController = 
injector.getInstance(ReconTaskController.class);
     LOG.info("Triggering asynchronous NSSummary tree rebuild for materialized 
totals (upgrade action).");
-    ReconUtils.triggerAsyncNSSummaryRebuild(nsMgr, omMgr);
+    ReconTaskController.ReInitializationResult result = 
reconTaskController.queueReInitializationEvent(
+        ReconTaskReInitializationEvent.ReInitializationReason.MANUAL_TRIGGER);
+    if (result != ReconTaskController.ReInitializationResult.SUCCESS) {
+      LOG.error(
+          "Failed to queue reinitialization event for manual trigger (result: 
{}), failing the reinitialization " +
+              "during NSSummaryAggregatedTotalsUpgrade action, will be retried 
as part of syncDataFromOM " +
+              "scheduler task.", result);
+    }
   }
 
   @Override
diff --git 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestNSSummaryEndpointWithFSO.java
 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestNSSummaryEndpointWithFSO.java
index b496f2225d1..227330c83f6 100644
--- 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestNSSummaryEndpointWithFSO.java
+++ 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestNSSummaryEndpointWithFSO.java
@@ -29,9 +29,7 @@
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.io.File;
@@ -88,8 +86,6 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
-import org.mockito.ArgumentCaptor;
-import org.slf4j.Logger;
 
 /**
  * Test for NSSummary REST APIs with FSO.
@@ -813,40 +809,6 @@ public void 
testConstructFullPathWithNegativeParentIdTriggersRebuild() throws IO
         ReconUtils.constructFullPath(keyInfo, mockSummaryManager, 
mockMetadataManager));
   }
 
-  @Test
-  public void testLoggingWhenParentIdIsNegative() throws IOException {
-    ReconNamespaceSummaryManager mockManager =
-        mock(ReconNamespaceSummaryManager.class);
-    Logger mockLogger = mock(Logger.class);
-    ReconUtils.setLogger(mockLogger);
-
-    NSSummary mockSummary = new NSSummary();
-    mockSummary.setParentId(-1);
-    when(mockManager.getNSSummary(anyLong())).thenReturn(mockSummary);
-
-    OmKeyInfo keyInfo = new OmKeyInfo.Builder()
-        .setKeyName("testKey")
-        .setVolumeName("vol")
-        .setBucketName("bucket")
-        .setObjectID(1L)
-        .setParentObjectID(1L)
-        .build();
-
-    assertThrows(ServiceNotReadyException.class, () ->
-        ReconUtils.constructFullPath(keyInfo, mockManager, null));
-
-    // Assert
-    ArgumentCaptor<String> logCaptor = ArgumentCaptor.forClass(String.class);
-    verify(mockLogger).warn(logCaptor.capture());
-    String loggedMessage = logCaptor.getValue();
-
-    // Here we can assert the exact message we expect to see in the logs.
-    // Since we set parentId = -1, this triggers the corruption detection path
-    assertEquals(
-        "NSSummary tree corruption detected, rebuild triggered. Returning 
empty string " +
-            "for path construction.", loggedMessage);
-  }
-
   /**
    * Write directories and keys info into OM DB.
    * @throws Exception
diff --git 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestNSSummaryUnifiedControl.java
 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestNSSummaryUnifiedControl.java
index d6016e3d2df..c1d0a49ee99 100644
--- 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestNSSummaryUnifiedControl.java
+++ 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestNSSummaryUnifiedControl.java
@@ -434,54 +434,6 @@ void testReconUtilsIntegration() throws Exception {
         "State should return to IDLE after completion");
   }
 
-  /**
-   * Test that ReconUtils async trigger respects unified control.
-   */
-  @Test
-  void testReconUtilsRespectsUnifiedControl() throws Exception {
-    CountDownLatch firstRebuildStarted = new CountDownLatch(1);
-    CountDownLatch firstRebuildCanFinish = new CountDownLatch(1);
-
-    // Setup first rebuild to block
-    doAnswer(invocation -> {
-      firstRebuildStarted.countDown();
-      boolean awaitSuccess = firstRebuildCanFinish.await(5, TimeUnit.SECONDS);
-      if (!awaitSuccess) {
-        LOG.warn("firstRebuildCanFinish.await() timed out");
-      }
-      return null;
-    }).when(mockNamespaceSummaryManager).clearNSSummaryTable();
-
-    // Start first rebuild via NSSummaryTask directly
-    CompletableFuture<TaskResult> directRebuild = 
CompletableFuture.supplyAsync(() -> 
-        nsSummaryTask.reprocess(mockOMMetadataManager));
-
-    // Wait for first rebuild to start
-    assertTrue(firstRebuildStarted.await(5, TimeUnit.SECONDS),
-        "First rebuild should start");
-    assertEquals(RebuildState.RUNNING, NSSummaryTask.getRebuildState(),
-        "State should be RUNNING");
-
-    // Try to trigger via ReconUtils - should still respect the running state
-    // (The async execution will be queued but the actual rebuild will be 
rejected)
-    boolean triggered = ReconUtils.triggerAsyncNSSummaryRebuild(
-        mockNamespaceSummaryManager, mockReconOMMetadataManager);
-    assertTrue(triggered, "ReconUtils should queue the async request");
-
-    // State should still be RUNNING from the first rebuild
-    assertEquals(RebuildState.RUNNING, NSSummaryTask.getRebuildState(),
-        "State should still be RUNNING from first rebuild");
-
-    // Complete first rebuild
-    firstRebuildCanFinish.countDown();
-    TaskResult result = directRebuild.get(5, TimeUnit.SECONDS);
-    assertTrue(result.isTaskSuccess(), "First rebuild should succeed");
-
-    // Final state should be IDLE
-    assertEquals(RebuildState.IDLE, NSSummaryTask.getRebuildState(),
-        "Final state should be IDLE");
-  }
-
   /**
    * Test state transitions during exception scenarios.
    */
diff --git 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/upgrade/TestNSSummaryAggregatedTotalsUpgrade.java
 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/upgrade/TestNSSummaryAggregatedTotalsUpgrade.java
new file mode 100644
index 00000000000..87b5d2b2f71
--- /dev/null
+++ 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/upgrade/TestNSSummaryAggregatedTotalsUpgrade.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.ozone.recon.upgrade;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.inject.Injector;
+import javax.sql.DataSource;
+import org.apache.hadoop.ozone.recon.ReconGuiceServletContextListener;
+import org.apache.hadoop.ozone.recon.tasks.ReconTaskController;
+import org.apache.hadoop.ozone.recon.tasks.ReconTaskReInitializationEvent;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+/**
+ * Test class for NSSummaryAggregatedTotalsUpgrade.
+ */
+public class TestNSSummaryAggregatedTotalsUpgrade {
+
+  private NSSummaryAggregatedTotalsUpgrade upgradeAction;
+  private DataSource mockDataSource;
+  private Injector mockInjector;
+  private ReconTaskController mockReconTaskController;
+
+  @BeforeEach
+  public void setUp() {
+    upgradeAction = new NSSummaryAggregatedTotalsUpgrade();
+    mockDataSource = mock(DataSource.class);
+    mockInjector = mock(Injector.class);
+    mockReconTaskController = mock(ReconTaskController.class);
+  }
+
+  @Test
+  public void testExecuteSuccessfulReinitialization() throws Exception {
+    try (MockedStatic<ReconGuiceServletContextListener> mockedListener = 
+         Mockito.mockStatic(ReconGuiceServletContextListener.class)) {
+      
+      mockedListener.when(ReconGuiceServletContextListener::getGlobalInjector)
+          .thenReturn(mockInjector);
+      
+      when(mockInjector.getInstance(ReconTaskController.class))
+          .thenReturn(mockReconTaskController);
+      
+      when(mockReconTaskController.queueReInitializationEvent(
+          
ReconTaskReInitializationEvent.ReInitializationReason.MANUAL_TRIGGER))
+          .thenReturn(ReconTaskController.ReInitializationResult.SUCCESS);
+
+      assertDoesNotThrow(() -> upgradeAction.execute(mockDataSource));
+
+      verify(mockInjector).getInstance(ReconTaskController.class);
+      verify(mockReconTaskController).queueReInitializationEvent(
+          
ReconTaskReInitializationEvent.ReInitializationReason.MANUAL_TRIGGER);
+    }
+  }
+
+  @Test
+  public void testExecuteFailedReinitializationRetryLater() throws Exception {
+    try (MockedStatic<ReconGuiceServletContextListener> mockedListener = 
+         Mockito.mockStatic(ReconGuiceServletContextListener.class)) {
+      
+      mockedListener.when(ReconGuiceServletContextListener::getGlobalInjector)
+          .thenReturn(mockInjector);
+      
+      when(mockInjector.getInstance(ReconTaskController.class))
+          .thenReturn(mockReconTaskController);
+      
+      when(mockReconTaskController.queueReInitializationEvent(
+          
ReconTaskReInitializationEvent.ReInitializationReason.MANUAL_TRIGGER))
+          .thenReturn(ReconTaskController.ReInitializationResult.RETRY_LATER);
+
+      assertDoesNotThrow(() -> upgradeAction.execute(mockDataSource));
+
+      verify(mockInjector).getInstance(ReconTaskController.class);
+      verify(mockReconTaskController).queueReInitializationEvent(
+          
ReconTaskReInitializationEvent.ReInitializationReason.MANUAL_TRIGGER);
+    }
+  }
+
+  @Test
+  public void testExecuteFailedReinitializationMaxRetriesExceeded() throws 
Exception {
+    try (MockedStatic<ReconGuiceServletContextListener> mockedListener = 
+         Mockito.mockStatic(ReconGuiceServletContextListener.class)) {
+      
+      mockedListener.when(ReconGuiceServletContextListener::getGlobalInjector)
+          .thenReturn(mockInjector);
+      
+      when(mockInjector.getInstance(ReconTaskController.class))
+          .thenReturn(mockReconTaskController);
+      
+      when(mockReconTaskController.queueReInitializationEvent(
+          
ReconTaskReInitializationEvent.ReInitializationReason.MANUAL_TRIGGER))
+          
.thenReturn(ReconTaskController.ReInitializationResult.MAX_RETRIES_EXCEEDED);
+
+      assertDoesNotThrow(() -> upgradeAction.execute(mockDataSource));
+
+      verify(mockInjector).getInstance(ReconTaskController.class);
+      verify(mockReconTaskController).queueReInitializationEvent(
+          
ReconTaskReInitializationEvent.ReInitializationReason.MANUAL_TRIGGER);
+    }
+  }
+
+  @Test
+  public void testExecuteWithNullInjector() {
+    try (MockedStatic<ReconGuiceServletContextListener> mockedListener = 
+         Mockito.mockStatic(ReconGuiceServletContextListener.class)) {
+      
+      mockedListener.when(ReconGuiceServletContextListener::getGlobalInjector)
+          .thenReturn(null);
+
+      IllegalStateException exception = 
assertThrows(IllegalStateException.class, 
+          () -> upgradeAction.execute(mockDataSource));
+
+      assert exception.getMessage().contains(
+          "Guice injector not initialized. NSSummary rebuild cannot proceed 
during upgrade.");
+    }
+  }
+
+  @Test
+  public void testExecuteWithInjectorException() throws Exception {
+    try (MockedStatic<ReconGuiceServletContextListener> mockedListener = 
+         Mockito.mockStatic(ReconGuiceServletContextListener.class)) {
+      
+      mockedListener.when(ReconGuiceServletContextListener::getGlobalInjector)
+          .thenReturn(mockInjector);
+      
+      when(mockInjector.getInstance(ReconTaskController.class))
+          .thenThrow(new RuntimeException("Injector failed to provide 
instance"));
+
+      assertThrows(RuntimeException.class, () -> 
upgradeAction.execute(mockDataSource));
+
+      verify(mockInjector).getInstance(ReconTaskController.class);
+    }
+  }
+
+  @Test
+  public void testGetType() {
+    assert upgradeAction.getType() == 
ReconUpgradeAction.UpgradeActionType.FINALIZE;
+  }
+}


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

Reply via email to