void-ptr974 commented on code in PR #26146:
URL: https://github.com/apache/pulsar/pull/26146#discussion_r3566280698


##########
pulsar-broker/src/test/java/org/apache/pulsar/broker/loadbalance/extensions/channel/ServiceUnitStateChannelTest.java:
##########
@@ -280,6 +281,146 @@ public void channelOwnerTest() throws Exception {
         }
     }
 
+    @Test(priority = 1)
+    public void testCompletedGetOwnerRequestDoesNotRemoveNewRequest() throws 
Exception {
+        ServiceUnitStateChannelImpl channel = (ServiceUnitStateChannelImpl) 
channel1;
+        String serviceUnit = namespaceName + "/0x10000000_0x10000001";
+        var getOwnerRequests = channel.getOwnerRequests();
+        getOwnerRequests.remove(serviceUnit);
+        CompletableFuture<String> oldRequest = 
channel.dedupeGetOwnerRequest(serviceUnit);
+        assertEquals(getOwnerRequests.get(serviceUnit), oldRequest);
+
+        CountDownLatch staleCallbackRunning = new CountDownLatch(1);
+        CountDownLatch releaseStaleCallback = new CountDownLatch(1);
+        oldRequest.whenComplete((__, ___) -> {

Review Comment:
   Updated in 12d970b138b. The test now installs the replacement request before 
completing the old one, so it deterministically exercises the stale cleanup 
without relying on dependent-action ordering or an executor.



##########
pulsar-broker/src/test/java/org/apache/pulsar/broker/loadbalance/extensions/channel/ServiceUnitStateChannelTest.java:
##########
@@ -280,6 +281,146 @@ public void channelOwnerTest() throws Exception {
         }
     }
 
+    @Test(priority = 1)
+    public void testCompletedGetOwnerRequestDoesNotRemoveNewRequest() throws 
Exception {
+        ServiceUnitStateChannelImpl channel = (ServiceUnitStateChannelImpl) 
channel1;
+        String serviceUnit = namespaceName + "/0x10000000_0x10000001";
+        var getOwnerRequests = channel.getOwnerRequests();
+        getOwnerRequests.remove(serviceUnit);
+        CompletableFuture<String> oldRequest = 
channel.dedupeGetOwnerRequest(serviceUnit);
+        assertEquals(getOwnerRequests.get(serviceUnit), oldRequest);
+
+        CountDownLatch staleCallbackRunning = new CountDownLatch(1);
+        CountDownLatch releaseStaleCallback = new CountDownLatch(1);
+        oldRequest.whenComplete((__, ___) -> {
+            staleCallbackRunning.countDown();
+            try {
+                assertTrue(releaseStaleCallback.await(5, TimeUnit.SECONDS));
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                throw new CompletionException(e);
+            }
+        });
+
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        try {
+            Future<?> completeOldRequest = executor.submit(() -> 
oldRequest.complete(brokerId1));
+            assertTrue(staleCallbackRunning.await(5, TimeUnit.SECONDS),
+                    "The stale get-owner completion callback did not start");
+            assertTrue(getOwnerRequests.get(serviceUnit) == oldRequest,
+                    "The stale cleanup must not run before the new request is 
installed");
+
+            // Replace the map entry before releasing the old callback.
+            CompletableFuture<String> newRequest = new CompletableFuture<>();
+            getOwnerRequests.put(serviceUnit, newRequest);
+            releaseStaleCallback.countDown();
+            completeOldRequest.get(5, TimeUnit.SECONDS);
+
+            assertTrue(getOwnerRequests.get(serviceUnit) == newRequest,
+                    "A stale get-owner cleanup must not remove a newer request 
future");
+        } finally {
+            releaseStaleCallback.countDown();
+            executor.shutdownNow();
+            getOwnerRequests.remove(serviceUnit);
+        }
+    }
+
+    @Test(priority = 1)
+    public void testSkippedEventDoesNotRemoveNewGetOwnerRequest() throws 
Exception {
+        ServiceUnitStateChannelImpl channel = (ServiceUnitStateChannelImpl) 
channel1;
+        String serviceUnit = namespaceName + "/0x10000002_0x10000003";
+        var getOwnerRequests = channel.getOwnerRequests();
+        CompletableFuture<String> oldRequest = new CompletableFuture<>();
+        try {
+            overrideTableView(channel, serviceUnit, new 
ServiceUnitStateData(Owned, brokerId1, 1));
+            getOwnerRequests.put(serviceUnit, oldRequest);
+
+            CountDownLatch staleCallbackRunning = new CountDownLatch(1);
+            CountDownLatch releaseStaleCallback = new CountDownLatch(1);
+            oldRequest.whenComplete((__, ___) -> {
+                staleCallbackRunning.countDown();
+                try {
+                    assertTrue(releaseStaleCallback.await(5, 
TimeUnit.SECONDS));
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                    throw new CompletionException(e);
+                }
+            });
+
+            ExecutorService executor = Executors.newSingleThreadExecutor();
+            try {
+                Future<?> handleSkippedEvent = executor.submit(() -> 
channel.handleSkippedEvent(serviceUnit));
+                assertTrue(staleCallbackRunning.await(5, TimeUnit.SECONDS),
+                        "The stale skipped-event callback did not start");
+                assertTrue(getOwnerRequests.get(serviceUnit) == oldRequest,
+                        "The skipped-event cleanup must not run before the new 
request is installed");
+
+                // Replace the map entry before releasing the old callback.
+                CompletableFuture<String> newRequest = new 
CompletableFuture<>();
+                getOwnerRequests.put(serviceUnit, newRequest);
+                releaseStaleCallback.countDown();
+                handleSkippedEvent.get(5, TimeUnit.SECONDS);
+
+                assertEquals(oldRequest.getNow(null), brokerId1);
+                assertTrue(getOwnerRequests.get(serviceUnit) == newRequest,
+                        "A stale skipped-event cleanup must not remove a newer 
request future");
+            } finally {
+                releaseStaleCallback.countDown();
+                executor.shutdownNow();
+            }
+        } finally {
+            getOwnerRequests.remove(serviceUnit);
+            oldRequest.cancel(false);
+            overrideTableView(channel, serviceUnit, null);
+        }
+    }
+
+    @Test(priority = 1)
+    public void testCompletedCleanupJobDoesNotRemoveNewCleanupJob() throws 
Exception {
+        ServiceUnitStateChannelImpl channel = (ServiceUnitStateChannelImpl) 
channel1;
+        String broker = brokerId3;
+        var cleanupJobs = channel.getCleanupJobs();
+        cleanupJobs.remove(broker);
+        channel.scheduleCleanup(broker, 60L);
+        CompletableFuture<Void> oldJob = cleanupJobs.get(broker);
+        assertNotNull(oldJob);
+
+        CountDownLatch staleCallbackRunning = new CountDownLatch(1);
+        CountDownLatch releaseStaleCallback = new CountDownLatch(1);
+        oldJob.whenComplete((__, ___) -> {

Review Comment:
   Updated in 12d970b138b. This test now replaces the cleanup job before 
completing the old job, making the stale removal deterministic and eliminating 
the latches and executor.



-- 
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]

Reply via email to