You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by lh...@apache.org on 2026/08/18 20:19:56 UTC

(pulsar) branch master updated: [fix][offload] Normalize offloader cache directory path (#26372)

This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new ae07c33d6cbf [fix][offload] Normalize offloader cache directory path (#26372)
ae07c33d6cbf is described below

commit ae07c33d6cbfe37cd9111e02485797cacd9aef35
Author: Cong Zhao <zh...@apache.org>
AuthorDate: Wed Aug 19 04:19:49 2026 +0800

    [fix][offload] Normalize offloader cache directory path (#26372)
---
 .../mledger/offload/OffloadersCache.java           |  4 ++-
 .../mledger/offload/OffloadersCacheTest.java       | 32 +++++++++++++++++++++-
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadersCache.java b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadersCache.java
index 26a5f803ebb7..0d31a1659e08 100644
--- a/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadersCache.java
+++ b/managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloadersCache.java
@@ -19,6 +19,7 @@
 package org.apache.bookkeeper.mledger.offload;
 
 import java.io.IOException;
+import java.nio.file.Paths;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import lombok.CustomLog;
@@ -41,7 +42,8 @@ public class OffloadersCache implements AutoCloseable {
      * @throws IOException when fail to retrieve the pulsar offloader class
      */
     public Offloaders getOrLoadOffloaders(String offloadersPath, String narExtractionDirectory) {
-        return loadedOffloaders.computeIfAbsent(offloadersPath,
+        String normalizedOffloadersPath = Paths.get(offloadersPath).toAbsolutePath().normalize().toString();
+        return loadedOffloaders.computeIfAbsent(normalizedOffloadersPath,
                 (directory) -> {
                     try {
                         return OffloaderUtils.searchForOffloaders(directory, narExtractionDirectory);
diff --git a/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/offload/OffloadersCacheTest.java b/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/offload/OffloadersCacheTest.java
index f20b9b9cbbf2..987901947156 100644
--- a/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/offload/OffloadersCacheTest.java
+++ b/managed-ledger/src/test/java/org/apache/bookkeeper/mledger/offload/OffloadersCacheTest.java
@@ -19,7 +19,10 @@
 package org.apache.bookkeeper.mledger.offload;
 
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.testng.Assert.assertSame;
+import java.nio.file.Paths;
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 import org.testng.annotations.Test;
@@ -29,9 +32,10 @@ public class OffloadersCacheTest {
     @Test
     public void testLoadsOnlyOnce() throws Exception {
         Offloaders expectedOffloaders = new Offloaders();
+        String normalizedPath = Paths.get("./offloaders").toAbsolutePath().normalize().toString();
 
         try (MockedStatic<OffloaderUtils> offloaderUtils = Mockito.mockStatic(OffloaderUtils.class)) {
-            offloaderUtils.when(() -> OffloaderUtils.searchForOffloaders(eq("./offloaders"), eq("/tmp")))
+            offloaderUtils.when(() -> OffloaderUtils.searchForOffloaders(eq(normalizedPath), eq("/tmp")))
                     .thenReturn(expectedOffloaders);
 
             OffloadersCache cache = new OffloadersCache();
@@ -47,4 +51,30 @@ public class OffloadersCacheTest {
             assertSame(offloaders2, expectedOffloaders, "The offloaders should be the mocked one.");
         }
     }
+
+    @Test
+    public void testEquivalentPathsLoadOnlyOnce() throws Exception {
+        String relativePath = "./offloaders";
+        String normalizedPath = Paths.get(relativePath).toAbsolutePath().normalize().toString();
+        Offloaders expectedOffloaders = new Offloaders();
+
+        try (MockedStatic<OffloaderUtils> offloaderUtils = Mockito.mockStatic(OffloaderUtils.class)) {
+            offloaderUtils.when(() -> OffloaderUtils.searchForOffloaders(eq(relativePath), eq("/tmp")))
+                    .thenReturn(expectedOffloaders);
+            offloaderUtils.when(() -> OffloaderUtils.searchForOffloaders(eq(normalizedPath), eq("/tmp")))
+                    .thenReturn(expectedOffloaders);
+
+            OffloadersCache cache = new OffloadersCache();
+
+            Offloaders offloaders1 = cache.getOrLoadOffloaders(relativePath, "/tmp");
+            Offloaders offloaders2 = cache.getOrLoadOffloaders(normalizedPath, "/tmp");
+
+            assertSame(offloaders1, expectedOffloaders, "The relative path should load the mocked offloaders.");
+            assertSame(offloaders2, expectedOffloaders, "The absolute path should reuse the cached offloaders.");
+            offloaderUtils.verify(
+                    () -> OffloaderUtils.searchForOffloaders(eq(normalizedPath), eq("/tmp")), times(1));
+            offloaderUtils.verify(
+                    () -> OffloaderUtils.searchForOffloaders(eq(relativePath), eq("/tmp")), never());
+        }
+    }
 }