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

reschke pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git


The following commit(s) were added to refs/heads/master by this push:
     new edb629c6 SLING-12700: add test coverage for vanity path cache metrics 
(#158)
edb629c6 is described below

commit edb629c696c797d345f91ae5006ba66352fa70a7
Author: Julian Reschke <[email protected]>
AuthorDate: Thu Mar 6 17:32:08 2025 +0100

    SLING-12700: add test coverage for vanity path cache metrics (#158)
    
    * SLING-12700: add test coverage for vanity path cache metrics
    
    * SLING-12700: add test coverage for vanity path cache metrics - fix race 
condition for cache init for background vp init
    
    * SLING-12700: add test coverage for vanity path cache metrics - typo
---
 .../impl/mapping/VanityPathHandler.java            | 11 ++--
 .../impl/mapping/VanityPathMapEntriesTest.java     | 69 +++++++++++++++-------
 2 files changed, 54 insertions(+), 26 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java
index 463b14e8..c19bc7b3 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java
@@ -72,8 +72,8 @@ public class VanityPathHandler {
     final AtomicLong vanityPathBloomNegatives = new AtomicLong(0);
     final AtomicLong vanityPathBloomFalsePositives = new AtomicLong(0);
 
-    private final AtomicLong temporaryResolveMapsMapHits = new AtomicLong();
-    private final AtomicLong temporaryResolveMapsMapMisses = new AtomicLong();
+    final AtomicLong temporaryResolveMapsMapHits = new AtomicLong();
+    final AtomicLong temporaryResolveMapsMapMisses = new AtomicLong();
     private final AtomicBoolean vanityPathsProcessed = new 
AtomicBoolean(false);
 
     private final Logger log = 
LoggerFactory.getLogger(VanityPathHandler.class);
@@ -88,6 +88,7 @@ public class VanityPathHandler {
     private final List<MapEntry> noMapEntries = Collections.emptyList();
 
     // Temporary cache for use while doing async vanity path query
+    private static int TEMPORARY_CACHE_SIZE_LIMIT = 10000;
     private Map<String, List<MapEntry>> temporaryResolveMapsMap;
 
     private final ReentrantLock initializing;
@@ -121,6 +122,7 @@ public class VanityPathHandler {
             if (this.factory.isVanityPathEnabled()) {
                 vanityPathsProcessed.set(false);
                 this.vanityBloomFilter = createVanityBloomFilter();
+                this.temporaryResolveMapsMap = Collections.synchronizedMap(new 
LRUMap<>(TEMPORARY_CACHE_SIZE_LIMIT));
                 VanityPathInitializer vpi = new 
VanityPathInitializer(this.factory);
 
                 if (this.factory.isVanityPathCacheInitInBackground()) {
@@ -147,8 +149,6 @@ public class VanityPathHandler {
 
     private class VanityPathInitializer implements Runnable {
 
-        private int SIZELIMIT = 10000;
-
         private final MapConfigurationProvider factory;
 
         public VanityPathInitializer(MapConfigurationProvider factory) {
@@ -158,7 +158,6 @@ public class VanityPathHandler {
         @Override
         public void run() {
             try {
-                temporaryResolveMapsMap = Collections.synchronizedMap(new 
LRUMap<>(SIZELIMIT));
                 execute();
             } catch (Exception ex) {
                 log.error("vanity path initializer thread terminated with an 
exception", ex);
@@ -192,7 +191,7 @@ public class VanityPathHandler {
                 log.error("Vanity path init failed", ex);
             } finally {
                 log.debug("dropping temporary resolver map - {}/{} entries, {} 
hits, {} misses", temporaryResolveMapsMap.size(),
-                        SIZELIMIT, temporaryResolveMapsMapHits.get(), 
temporaryResolveMapsMapMisses.get());
+                        TEMPORARY_CACHE_SIZE_LIMIT, 
temporaryResolveMapsMapHits.get(), temporaryResolveMapsMapMisses.get());
                 temporaryResolveMapsMap = null;
             }
         }
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java
index be2d31b9..af6fb4ce 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java
@@ -1251,33 +1251,53 @@ public class VanityPathMapEntriesTest extends 
AbstractMappingMapEntriesTest {
             }
         });
 
-        assertEquals("test not started, query counter should be 0, queries 
were: " +
-                dumpQueries(queries), 0, queries.size());
+        Iterator<MapEntry> mit;
+        int expectedQueryCount = 0;
+        int expectedCacheHits = 0;
+        int expectedCacheMisses = 0;
 
-        mapEntries.vph.initializeVanityPaths();
-
-        assertEquals("test started, but query is blocked, query counter should 
still be 0, queries were: " +
-                dumpQueries(queries), 0, queries.size());
+        checkCounters("before tests",
+                queries, expectedQueryCount, expectedCacheHits, 
expectedCacheMisses);
 
-        // should not be finished until unlocked
-        assertFalse("VPH should not be ready as it is locked", 
mapEntries.vph.isReady());
+        mapEntries.vph.initializeVanityPaths();
+        assertFalse("VPH should not be ready until unblocked", 
mapEntries.vph.isReady());
+        checkCounters("after launch of background init",
+                queries, expectedQueryCount, expectedCacheHits, 
expectedCacheMisses);
 
-        // do a forced lookup while init runs
-        Iterator<MapEntry> mit = 
mapEntries.vph.getCurrentMapEntryForVanityPath(targetPath);
-        assertNotNull(mit);
-        assertEquals("after lookup bypassing background init, exactly 1 query 
should have happened, but queries were: " +
-                dumpQueries(queries), 1, queries.size());
+        // do a forced lookup while background init runs, but is blocked
+        mit = mapEntries.vph.getCurrentMapEntryForVanityPath(targetPath);
+        expectedQueryCount += 1;
+        expectedCacheMisses += 1;
+        assertNotNull("map entry expected from query to repository", mit);
+        checkCounters("after first forced lookup",
+                queries, expectedQueryCount, expectedCacheHits, 
expectedCacheMisses);
 
-        // intermediate map does not contain vp
+        // intermediate map does not contain vanity path
         Map<String, List<String>> intermediateVanityMap = 
mapEntries.getVanityPathMappings();
         assertFalse("while bg init is running, vanity map should not be 
updated yet",
                 intermediateVanityMap.containsKey("/simpleVanityPath"));
 
+        // do a forced lookup for a non-existing resource while init runs
+        mit = mapEntries.vph.getCurrentMapEntryForVanityPath(targetPath + 
"-notfound");
+        expectedQueryCount += 1;
+        expectedCacheMisses += 1;
+        assertNull("should be null for non-existing resource", mit);
+        checkCounters("after first forced lookup for non-existing resource",
+                queries, expectedQueryCount, expectedCacheHits, 
expectedCacheMisses);
+
+        // do a second forced lookup for a non-existing resource while init 
runs
+        mit = mapEntries.vph.getCurrentMapEntryForVanityPath(targetPath + 
"-notfound");
+        expectedCacheHits += 1;
+        assertNull("should be null for non-existing resource", mit);
+        checkCounters("after second forced lookup for the same non-existing 
resource",
+                queries, expectedQueryCount, expectedCacheHits, 
expectedCacheMisses);
+
         // try another forced lookup, should be cached
         mit = mapEntries.vph.getCurrentMapEntryForVanityPath(targetPath);
+        expectedCacheHits += 1;
         assertNotNull(mit);
-        assertEquals("second lookup for the same path during background init 
should be cached, so no query should have happened, queries were: " +
-                dumpQueries(queries), 1, queries.size());
+        checkCounters("after second forced lookup for existing resource",
+                queries, expectedQueryCount, expectedCacheHits, 
expectedCacheMisses);
 
         // send a change event for a resource with vanity path;
         // this will be queued while init is running and then processed later 
on
@@ -1301,10 +1321,11 @@ public class VanityPathMapEntriesTest extends 
AbstractMappingMapEntriesTest {
         // let initializer run, then wait until finished
         lock.unlock();
         waitForBgInit();
+        expectedQueryCount += 1;
 
         // now one more query should have happened
-        assertEquals("query done by background init, total number should now 
be 2, but queries were: " +
-                dumpQueries(queries), 2, queries.size());
+        checkCounters("after initializer run",
+                queries, expectedQueryCount, expectedCacheHits, 
expectedCacheMisses);
 
         // final map contains both vps (from direct lookup and from event)
         Map<String, List<String>> finalVanityMap = 
mapEntries.getVanityPathMappings();
@@ -1312,8 +1333,16 @@ public class VanityPathMapEntriesTest extends 
AbstractMappingMapEntriesTest {
         assertTrue(finalVanityMap.get("/eventTest").contains("/baa"));
     }
 
+    // checks for the expected list of queries and the cache statistics
+    private void checkCounters(String testName, List<String> queries,
+                               int expectedQueries, int expectedCacheHits, int 
expectedCacheMisses) {
+        assertEquals(testName + " - queries: " + dumpQueries(queries), 
expectedQueries, queries.size());
+        assertEquals(testName + " - cache hits", expectedCacheHits, 
mapEntries.vph.temporaryResolveMapsMapHits.get());
+        assertEquals(testName + " - cache misses", expectedCacheMisses, 
mapEntries.vph.temporaryResolveMapsMapMisses.get());
+    }
+
     private static String dumpQueries(List<String> queries) {
-        return queries.size() + " (" + queries + ")";
+        return queries.size() + " queries (" + queries + ")";
     }
 
     @Test
@@ -1323,7 +1352,7 @@ public class VanityPathMapEntriesTest extends 
AbstractMappingMapEntriesTest {
         // should not fail
         mapEntries.vph.initializeVanityPaths();
         // but state should not change to "ready"
-        assertThrows("init should not be reported to be done", 
AssertionError.class, () -> waitForBgInit());
+        assertThrows("init should not be reported to be done", 
AssertionError.class, this::waitForBgInit);
     }
 
     // utilities for testing vanity path queries

Reply via email to