sadanand48 commented on code in PR #9142:
URL: https://github.com/apache/ozone/pull/9142#discussion_r2428061946


##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServletInodeBasedXfer.java:
##########
@@ -438,6 +449,143 @@ public void testWriteDBToArchive(boolean 
expectOnlySstFiles) throws Exception {
     }
   }
 
+  @Test
+  public void testBootstrapLockCoordination() throws Exception {
+    // Create mocks for all background services
+    KeyDeletingService mockDeletingService = mock(KeyDeletingService.class);
+    DirectoryDeletingService mockDirDeletingService = 
mock(DirectoryDeletingService.class);
+    SstFilteringService mockFilteringService = mock(SstFilteringService.class);
+    SnapshotDeletingService mockSnapshotDeletingService = 
mock(SnapshotDeletingService.class);
+    RocksDBCheckpointDiffer mockCheckpointDiffer = 
mock(RocksDBCheckpointDiffer.class);
+    // Create mock locks for each service
+    BootstrapStateHandler.Lock mockDeletingLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockDirDeletingLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockFilteringLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockSnapshotDeletingLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockCheckpointDifferLock = 
mock(BootstrapStateHandler.Lock.class);
+    // Configure service mocks to return their respective locks
+    
when(mockDeletingService.getBootstrapStateLock()).thenReturn(mockDeletingLock);
+    
when(mockDirDeletingService.getBootstrapStateLock()).thenReturn(mockDirDeletingLock);
+    
when(mockFilteringService.getBootstrapStateLock()).thenReturn(mockFilteringLock);
+    
when(mockSnapshotDeletingService.getBootstrapStateLock()).thenReturn(mockSnapshotDeletingLock);
+    
when(mockCheckpointDiffer.getBootstrapStateLock()).thenReturn(mockCheckpointDifferLock);
+    // Mock KeyManager and its services
+    KeyManager mockKeyManager = mock(KeyManager.class);
+    when(mockKeyManager.getDeletingService()).thenReturn(mockDeletingService);
+    
when(mockKeyManager.getDirDeletingService()).thenReturn(mockDirDeletingService);
+    
when(mockKeyManager.getSnapshotSstFilteringService()).thenReturn(mockFilteringService);
+    
when(mockKeyManager.getSnapshotDeletingService()).thenReturn(mockSnapshotDeletingService);
+    // Mock OMMetadataManager and Store
+    OMMetadataManager mockMetadataManager = mock(OMMetadataManager.class);
+    DBStore mockStore = mock(DBStore.class);
+    when(mockMetadataManager.getStore()).thenReturn(mockStore);
+    
when(mockStore.getRocksDBCheckpointDiffer()).thenReturn(mockCheckpointDiffer);
+    // Mock OzoneManager
+    OzoneManager mockOM = mock(OzoneManager.class);
+    when(mockOM.getKeyManager()).thenReturn(mockKeyManager);
+    when(mockOM.getMetadataManager()).thenReturn(mockMetadataManager);
+    // Create the actual Lock instance (this tests the real implementation)
+    OMDBCheckpointServlet.Lock bootstrapLock = new 
OMDBCheckpointServlet.Lock(mockOM);
+    // Test successful lock acquisition
+    BootstrapStateHandler.Lock result = bootstrapLock.lock();
+    // Verify all service locks were acquired
+    verify(mockDeletingLock).lock();
+    verify(mockDirDeletingLock).lock();
+    verify(mockFilteringLock).lock();
+    verify(mockSnapshotDeletingLock).lock();
+    verify(mockCheckpointDifferLock).lock();
+    // Verify double buffer flush was called
+    verify(mockOM).awaitDoubleBufferFlush();
+    // Verify the lock returns itself
+    assertEquals(bootstrapLock, result);
+    // Test unlock
+    bootstrapLock.unlock();
+    // Verify all service locks were released
+    verify(mockDeletingLock).unlock();
+    verify(mockDirDeletingLock).unlock();
+    verify(mockFilteringLock).unlock();
+    verify(mockSnapshotDeletingLock).unlock();
+    verify(mockCheckpointDifferLock).unlock();
+  }
+
+  /**
+   * Verifies that bootstrap lock acquisition blocks background services 
during checkpoint creation,
+   * preventing race conditions between checkpoint and service operations.
+   */
+  @Test
+  public void testBootstrapLockBlocksMultipleServices() throws Exception {
+    setupCluster();
+    // Initialize servlet
+    OMDBCheckpointServletInodeBasedXfer servlet = new 
OMDBCheckpointServletInodeBasedXfer();
+    ServletConfig servletConfig = mock(ServletConfig.class);
+    ServletContext servletContext = mock(ServletContext.class);
+    when(servletConfig.getServletContext()).thenReturn(servletContext);
+    
when(servletContext.getAttribute(OzoneConsts.OM_CONTEXT_ATTRIBUTE)).thenReturn(om);
+    servlet.init(servletConfig);
+
+    BootstrapStateHandler.Lock bootstrapLock = servlet.getBootstrapStateLock();
+    // Test multiple services being blocked
+    CountDownLatch bootstrapAcquired = new CountDownLatch(1);
+    CountDownLatch allServicesCompleted = new CountDownLatch(3); // 3 
background services
+    AtomicInteger servicesBlocked = new AtomicInteger(0);
+    AtomicInteger servicesSucceeded = new AtomicInteger(0);
+    // Checkpoint thread holds bootstrap lock
+    Thread checkpointThread = new Thread(() -> {
+      try {
+        LOG.info("Acquiring bootstrap lock for checkpoint...");
+        BootstrapStateHandler.Lock acquired = bootstrapLock.lock();
+        bootstrapAcquired.countDown();
+        Thread.sleep(3000); // Hold for 3 seconds
+        LOG.info("Releasing bootstrap lock...");
+        acquired.unlock();
+      } catch (Exception e) {
+        fail("Checkpoint failed: " + e.getMessage());
+      }
+    });
+
+    BiFunction<String, BootstrapStateHandler, Thread> createServiceThread =
+        (serviceName, service) -> new Thread(() -> {
+          try {
+            bootstrapAcquired.await();
+            if (service != null) {
+              LOG.info("{} : Trying to acquire lock...", serviceName);
+              servicesBlocked.incrementAndGet();
+              BootstrapStateHandler.Lock serviceLock = 
service.getBootstrapStateLock();
+              serviceLock.lock(); // Should block!
+              servicesBlocked.decrementAndGet();
+              servicesSucceeded.incrementAndGet();
+              LOG.info(" {} : Lock acquired!", serviceName);
+              serviceLock.unlock();
+            }
+            allServicesCompleted.countDown();
+          } catch (Exception e) {
+            LOG.error("{}  failed", serviceName, e);
+            allServicesCompleted.countDown();
+          }
+        });
+    // Start all threads
+    checkpointThread.start();
+    Thread keyDeletingThread = createServiceThread.apply("KeyDeletingService",
+        om.getKeyManager().getDeletingService());
+    Thread dirDeletingThread = 
createServiceThread.apply("DirectoryDeletingService",
+        om.getKeyManager().getDirDeletingService());
+    Thread snapshotDeletingThread = 
createServiceThread.apply("SnapshotDeletingService",
+        om.getKeyManager().getSnapshotDeletingService());
+    keyDeletingThread.start();
+    dirDeletingThread.start();
+    snapshotDeletingThread.start();
+    // Wait a bit, then verify multiple services are blocked
+    Thread.sleep(1000);

Review Comment:
   I feel this should be okay for a test



##########
hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMDbCheckpointServletInodeBasedXfer.java:
##########
@@ -438,6 +449,143 @@ public void testWriteDBToArchive(boolean 
expectOnlySstFiles) throws Exception {
     }
   }
 
+  @Test
+  public void testBootstrapLockCoordination() throws Exception {
+    // Create mocks for all background services
+    KeyDeletingService mockDeletingService = mock(KeyDeletingService.class);
+    DirectoryDeletingService mockDirDeletingService = 
mock(DirectoryDeletingService.class);
+    SstFilteringService mockFilteringService = mock(SstFilteringService.class);
+    SnapshotDeletingService mockSnapshotDeletingService = 
mock(SnapshotDeletingService.class);
+    RocksDBCheckpointDiffer mockCheckpointDiffer = 
mock(RocksDBCheckpointDiffer.class);
+    // Create mock locks for each service
+    BootstrapStateHandler.Lock mockDeletingLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockDirDeletingLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockFilteringLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockSnapshotDeletingLock = 
mock(BootstrapStateHandler.Lock.class);
+    BootstrapStateHandler.Lock mockCheckpointDifferLock = 
mock(BootstrapStateHandler.Lock.class);
+    // Configure service mocks to return their respective locks
+    
when(mockDeletingService.getBootstrapStateLock()).thenReturn(mockDeletingLock);
+    
when(mockDirDeletingService.getBootstrapStateLock()).thenReturn(mockDirDeletingLock);
+    
when(mockFilteringService.getBootstrapStateLock()).thenReturn(mockFilteringLock);
+    
when(mockSnapshotDeletingService.getBootstrapStateLock()).thenReturn(mockSnapshotDeletingLock);
+    
when(mockCheckpointDiffer.getBootstrapStateLock()).thenReturn(mockCheckpointDifferLock);
+    // Mock KeyManager and its services
+    KeyManager mockKeyManager = mock(KeyManager.class);
+    when(mockKeyManager.getDeletingService()).thenReturn(mockDeletingService);
+    
when(mockKeyManager.getDirDeletingService()).thenReturn(mockDirDeletingService);
+    
when(mockKeyManager.getSnapshotSstFilteringService()).thenReturn(mockFilteringService);
+    
when(mockKeyManager.getSnapshotDeletingService()).thenReturn(mockSnapshotDeletingService);
+    // Mock OMMetadataManager and Store
+    OMMetadataManager mockMetadataManager = mock(OMMetadataManager.class);
+    DBStore mockStore = mock(DBStore.class);
+    when(mockMetadataManager.getStore()).thenReturn(mockStore);
+    
when(mockStore.getRocksDBCheckpointDiffer()).thenReturn(mockCheckpointDiffer);
+    // Mock OzoneManager
+    OzoneManager mockOM = mock(OzoneManager.class);
+    when(mockOM.getKeyManager()).thenReturn(mockKeyManager);
+    when(mockOM.getMetadataManager()).thenReturn(mockMetadataManager);
+    // Create the actual Lock instance (this tests the real implementation)
+    OMDBCheckpointServlet.Lock bootstrapLock = new 
OMDBCheckpointServlet.Lock(mockOM);
+    // Test successful lock acquisition
+    BootstrapStateHandler.Lock result = bootstrapLock.lock();
+    // Verify all service locks were acquired
+    verify(mockDeletingLock).lock();
+    verify(mockDirDeletingLock).lock();
+    verify(mockFilteringLock).lock();
+    verify(mockSnapshotDeletingLock).lock();
+    verify(mockCheckpointDifferLock).lock();
+    // Verify double buffer flush was called
+    verify(mockOM).awaitDoubleBufferFlush();
+    // Verify the lock returns itself
+    assertEquals(bootstrapLock, result);
+    // Test unlock
+    bootstrapLock.unlock();
+    // Verify all service locks were released
+    verify(mockDeletingLock).unlock();
+    verify(mockDirDeletingLock).unlock();
+    verify(mockFilteringLock).unlock();
+    verify(mockSnapshotDeletingLock).unlock();
+    verify(mockCheckpointDifferLock).unlock();
+  }
+
+  /**
+   * Verifies that bootstrap lock acquisition blocks background services 
during checkpoint creation,
+   * preventing race conditions between checkpoint and service operations.
+   */
+  @Test
+  public void testBootstrapLockBlocksMultipleServices() throws Exception {
+    setupCluster();
+    // Initialize servlet
+    OMDBCheckpointServletInodeBasedXfer servlet = new 
OMDBCheckpointServletInodeBasedXfer();
+    ServletConfig servletConfig = mock(ServletConfig.class);
+    ServletContext servletContext = mock(ServletContext.class);
+    when(servletConfig.getServletContext()).thenReturn(servletContext);
+    
when(servletContext.getAttribute(OzoneConsts.OM_CONTEXT_ATTRIBUTE)).thenReturn(om);
+    servlet.init(servletConfig);
+
+    BootstrapStateHandler.Lock bootstrapLock = servlet.getBootstrapStateLock();
+    // Test multiple services being blocked
+    CountDownLatch bootstrapAcquired = new CountDownLatch(1);
+    CountDownLatch allServicesCompleted = new CountDownLatch(3); // 3 
background services
+    AtomicInteger servicesBlocked = new AtomicInteger(0);
+    AtomicInteger servicesSucceeded = new AtomicInteger(0);
+    // Checkpoint thread holds bootstrap lock
+    Thread checkpointThread = new Thread(() -> {
+      try {
+        LOG.info("Acquiring bootstrap lock for checkpoint...");
+        BootstrapStateHandler.Lock acquired = bootstrapLock.lock();
+        bootstrapAcquired.countDown();
+        Thread.sleep(3000); // Hold for 3 seconds

Review Comment:
   I feel this should be okay for a test



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to