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