diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/api/CacheBuilder.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/api/CacheBuilder.java index ace6e5fba90..1d70aa97b33 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/api/CacheBuilder.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/api/CacheBuilder.java @@ -46,7 +46,6 @@ public final class CacheBuilder { private long maximumWeight = -1; private long maximumSize = -1; - private int initialCapacity = -1; private Weigher weigher; private EvictionListener evictionListener; private boolean recordStats; @@ -87,23 +86,6 @@ public CacheBuilder maximumWeight(long maximumWeight) { return this; } - /** - * Sets the minimum number of entries the cache's internal hash table should be - * pre-sized to hold. Passing this hint avoids rehashing when the cache fills - * gradually from an empty state. - * - * @param initialCapacity the minimum initial capacity (must be non-negative) - * @return this builder - */ - @NotNull - public CacheBuilder initialCapacity(int initialCapacity) { - if (initialCapacity < 0) { - throw new IllegalArgumentException("initialCapacity must be non-negative, got: " + initialCapacity); - } - this.initialCapacity = initialCapacity; - return this; - } - /** * Sets the maximum number of entries the cache may hold. * May not be combined with {@link #maximumWeight(long)}. @@ -255,9 +237,6 @@ private LoadingCache buildCaffeine(CacheLoader loader) { @SuppressWarnings({"unchecked", "rawtypes"}) private Caffeine configureCaffeineBuilder() { Caffeine caffeineBuilder = Caffeine.newBuilder(); - if (initialCapacity >= 0) { - caffeineBuilder = caffeineBuilder.initialCapacity(initialCapacity); - } if (weigher != null) { // validateConfiguration() guarantees maximumWeight >= 0 when weigher is set Weigher w = weigher; diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/CacheWeights.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/CacheWeights.java index fbf2342bd70..38033a2148d 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/CacheWeights.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/CacheWeights.java @@ -20,7 +20,7 @@ import static org.apache.jackrabbit.oak.commons.StringUtils.estimateMemoryUsage; -import org.apache.jackrabbit.oak.cache.api.Weigher; +import org.apache.jackrabbit.guava.common.cache.Weigher; import org.apache.jackrabbit.oak.segment.ReaderCache.CacheKey; import org.jetbrains.annotations.NotNull; diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ReaderCache.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ReaderCache.java index 9d52fce3148..0acaa9d10e5 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ReaderCache.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/ReaderCache.java @@ -25,13 +25,9 @@ import java.util.Arrays; import java.util.function.Function; -import org.apache.jackrabbit.oak.cache.api.Weigher; -import org.apache.jackrabbit.guava.common.cache.CacheStats; -import org.apache.jackrabbit.oak.cache.AbstractCacheStats; +import org.apache.jackrabbit.guava.common.cache.Weigher; import org.apache.jackrabbit.oak.cache.CacheLIRS; -import org.apache.jackrabbit.oak.cache.api.Cache; -import org.apache.jackrabbit.oak.cache.api.CacheStatsAdapter; -import org.apache.jackrabbit.oak.cache.api.Weigher; +import org.apache.jackrabbit.oak.cache.CacheStats; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,13 +52,10 @@ public abstract class ReaderCache { private final FastCache fastCache; /** - * The slower (LIRS) cache, exposed through the Oak Cache API. + * The slower (LIRS) cache. */ @NotNull - private final Cache cache; - - @NotNull - private final AbstractCacheStats cacheStats; + private final CacheLIRS cache; /** * Create a new string cache. @@ -81,15 +74,13 @@ protected ReaderCache(long maxWeight, int averageWeight, .module(name) .maximumWeight(maxWeight) .averageWeight(averageWeight) - .weigher(weigher::weigh) - .build() - .asOakCache(); - cacheStats = new CacheStatsAdapter(cache, name, weigher, maxWeight); + .weigher(weigher) + .build(); } @NotNull - public AbstractCacheStats getStats() { - return cacheStats; + public CacheStats getStats() { + return new CacheStats(cache, name, weigher, cache.getMaxMemory()); } private static int getEntryHash(long lsb, long msb, int offset) { diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordCache.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordCache.java index 61f07029358..90e83a0161c 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordCache.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/RecordCache.java @@ -23,10 +23,10 @@ import java.util.concurrent.atomic.LongAdder; import java.util.function.Supplier; +import org.apache.jackrabbit.guava.common.cache.CacheBuilder; import org.apache.jackrabbit.guava.common.cache.CacheStats; -import org.apache.jackrabbit.oak.cache.api.CacheBuilder; -import org.apache.jackrabbit.oak.cache.api.EvictionListener; -import org.apache.jackrabbit.oak.cache.api.Weigher; +import org.apache.jackrabbit.guava.common.cache.RemovalListener; +import org.apache.jackrabbit.guava.common.cache.Weigher; import org.jetbrains.annotations.NotNull; /** @@ -137,7 +137,7 @@ public long estimateCurrentWeight() { private static class Default extends RecordCache { @NotNull - private final org.apache.jackrabbit.oak.cache.api.Cache cache; + private final org.apache.jackrabbit.guava.common.cache.Cache cache; @NotNull private final Weigher weigher; @@ -148,10 +148,10 @@ private static class Default extends RecordCache { @Override public @NotNull CacheStats getStats() { - org.apache.jackrabbit.oak.cache.api.CacheStatsSnapshot snapshot = cache.stats(); + CacheStats internalStats = cache.stats(); // any addition to the cache counts as load by our definition - return new CacheStats(snapshot.hitCount(), snapshot.missCount(), - loadCount.sum(), 0, 0, snapshot.evictionCount()); + return new CacheStats(internalStats.hitCount(), internalStats.missCount(), + loadCount.sum(), 0, 0, internalStats.evictionCount()); } static Supplier> defaultFactory(final int size, @NotNull final Weigher weigher) { @@ -159,17 +159,17 @@ static Supplier> defaultFactory(final int size, @NotNull fina } Default(final int size, @NotNull final Weigher weigher) { - this.weigher = weigher; - this.cache = CacheBuilder.newBuilder() + this.cache = CacheBuilder.newBuilder() .maximumSize(size * 4L / 3) .initialCapacity(size) + .concurrencyLevel(4) .recordStats() - .evictionListener((k, v, cause) -> { - if (v != null) { - weight.add(-weigher.weigh(k, v)); - } + .removalListener((RemovalListener) removal -> { + int removedWeight = weigher.weigh(removal.getKey(), removal.getValue()); + weight.add(-removedWeight); }) .build(); + this.weigher = weigher; } @Override @@ -186,7 +186,7 @@ public RecordId get(@NotNull K key) { @Override public long size() { - return cache.estimatedSize(); + return cache.size(); } @Override diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentCache.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentCache.java index daf04a6bc2a..41238c04b4d 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentCache.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentCache.java @@ -28,11 +28,11 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; +import org.apache.jackrabbit.guava.common.cache.Cache; +import org.apache.jackrabbit.guava.common.cache.CacheBuilder; import org.apache.jackrabbit.guava.common.cache.CacheStats; +import org.apache.jackrabbit.guava.common.cache.RemovalNotification; import org.apache.jackrabbit.oak.cache.AbstractCacheStats; -import org.apache.jackrabbit.oak.cache.api.Cache; -import org.apache.jackrabbit.oak.cache.api.CacheBuilder; -import org.apache.jackrabbit.oak.cache.api.EvictionCause; import org.apache.jackrabbit.oak.segment.CacheWeights.SegmentCacheWeigher; import org.jetbrains.annotations.NotNull; @@ -131,55 +131,47 @@ private static class NonEmptyCache extends SegmentCache { */ private NonEmptyCache(long cacheSizeMB) { long maximumWeight = cacheSizeMB * 1024 * 1024; - this.cache = CacheBuilder.newBuilder() + this.cache = CacheBuilder.newBuilder() + .concurrencyLevel(16) .maximumWeight(maximumWeight) .weigher(new SegmentCacheWeigher()) - .evictionListener(this::onRemove) + .removalListener(this::onRemove) .build(); - this.stats = new Stats(NAME, maximumWeight, cache::estimatedSize); + this.stats = new Stats(NAME, maximumWeight, cache::size); } /** * Removal handler called whenever an item is evicted from the cache. */ - private void onRemove(@NotNull SegmentId key, Segment value, @NotNull EvictionCause cause) { + private void onRemove(@NotNull RemovalNotification notification) { stats.evictionCount.incrementAndGet(); - if (value != null) { - stats.currentWeight.addAndGet(-segmentWeight(value)); + if (notification.getValue() != null) { + stats.currentWeight.addAndGet(-segmentWeight(notification.getValue())); + } + if (notification.getKey() != null) { + notification.getKey().unloaded(); } - key.unloaded(); } @Override @NotNull public Segment getSegment(@NotNull SegmentId id, @NotNull Callable loader) throws ExecutionException { if (id.isDataSegmentId()) { - try { - return cache.get(id, k -> { - try { - long t0 = System.nanoTime(); - Segment segment = loader.call(); - stats.loadSuccessCount.incrementAndGet(); - stats.loadTime.addAndGet(System.nanoTime() - t0); - stats.missCount.incrementAndGet(); - stats.currentWeight.addAndGet(segmentWeight(segment)); - id.loaded(segment); - return segment; - } catch (Exception e) { - stats.loadExceptionCount.incrementAndGet(); - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } - throw new RuntimeException(e); - } - }); - } catch (RuntimeException e) { - Throwable cause = e.getCause(); - if (cause instanceof Exception && !(cause instanceof RuntimeException)) { - throw new ExecutionException(cause); + return cache.get(id, () -> { + try { + long t0 = System.nanoTime(); + Segment segment = loader.call(); + stats.loadSuccessCount.incrementAndGet(); + stats.loadTime.addAndGet(System.nanoTime() - t0); + stats.missCount.incrementAndGet(); + stats.currentWeight.addAndGet(segmentWeight(segment)); + id.loaded(segment); + return segment; + } catch (Exception e) { + stats.loadExceptionCount.incrementAndGet(); + throw e; } - throw e; - } + }); } else { try { return loader.call(); diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java index 209d1580290..afc97162064 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/PriorityCache.java @@ -38,7 +38,7 @@ import org.jetbrains.annotations.Nullable; import org.apache.jackrabbit.guava.common.cache.CacheStats; -import org.apache.jackrabbit.oak.cache.api.Weigher; +import org.apache.jackrabbit.guava.common.cache.Weigher; /** * {@code PriorityCache} implements a partial mapping from keys of type {@code K} to values diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java index bfb8850ee91..2eafe8e1d72 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/PriorityCacheTest.java @@ -27,7 +27,7 @@ import java.util.Random; -import org.apache.jackrabbit.oak.cache.api.Weigher; +import org.apache.jackrabbit.guava.common.cache.Weigher; import org.apache.jackrabbit.oak.segment.CacheWeights; import org.junit.Test; diff --git a/specs/guava-cache-removal/PLAN.md b/specs/guava-cache-removal/PLAN.md index 055f8e9be73..756f4f9687b 100644 --- a/specs/guava-cache-removal/PLAN.md +++ b/specs/guava-cache-removal/PLAN.md @@ -305,7 +305,6 @@ public final class CacheBuilder { private String module; private long maximumWeight = -1; private long maximumSize = -1; - private int initialCapacity = -1; private Weigher weigher; private EvictionListener removalListener; private boolean recordStats; @@ -317,7 +316,6 @@ public final class CacheBuilder { public CacheBuilder maximumWeight(long maximumWeight) { ... } public CacheBuilder maximumSize(long maximumSize) { ... } - public CacheBuilder initialCapacity(int initialCapacity) { ... } public CacheBuilder weigher(Weigher weigher) { ... } public CacheBuilder removalListener(EvictionListener listener) { ... } public CacheBuilder recordStats() { ... } diff --git a/specs/guava-cache-removal/TASKS.md b/specs/guava-cache-removal/TASKS.md index 303b5256429..b9c96a23b02 100644 --- a/specs/guava-cache-removal/TASKS.md +++ b/specs/guava-cache-removal/TASKS.md @@ -197,7 +197,7 @@ The version bump must be included in the same PR as the new method. ### What changes - `oak-core-spi/.../cache/api/CacheBuilder.java` — **new** public final class for creating Caffeine-backed Oak caches only. - Builder fields: `maximumWeight`, `maximumSize`, `initialCapacity`, `weigher(Weigher)`, `evictionListener(EvictionListener)`, `recordStats`, `expireAfterAccess`, `expireAfterWrite`, `refreshAfterWrite`. + Builder fields: `maximumWeight`, `maximumSize`, `weigher(Weigher)`, `evictionListener(EvictionListener)`, `recordStats`, `expireAfterAccess`, `expireAfterWrite`, `refreshAfterWrite`. Methods: `build()` → `Cache`, `build(CacheLoader)` → `LoadingCache`. `build()` must always return a manual-cache adapter that does not implement `LoadingCache`; `build(CacheLoader)` must always return a loading-cache adapter. Validation rules are enforced in the builder before cache construction: @@ -265,8 +265,6 @@ Remove all `` HTML comments after restoring the link - runtime loader failures propagate directly - `Cache.get(key, mappingFunction)` uses Caffeine's `Function` contract and propagates runtime failures directly - `LoadingCache.refresh(key)` returns a non-null `CompletableFuture` - - `initialCapacity` is accepted and wired through to Caffeine without error - - negative `initialCapacity` is rejected before cache construction - invalid builder combinations are rejected consistently before cache construction - `CacheStatsAdapter` bridges `CacheStatsSnapshot` back to Guava shim `CacheStats` - `stats()` returns non-null `CacheStatsSnapshot` with correct counts @@ -494,7 +492,6 @@ the `org.apache.jackrabbit.oak.cache` package version must be bumped in `package - `RecordCache.java` — `CacheBuilder.newBuilder()` (Guava shim) to `CacheBuilder`; `Cache` to `Cache`; Guava `Weigher` to `Weigher` - `CacheWeights.java` — `Weigher` to `Weigher` - `CachingSegmentReader.java` — update cache type references -- `PriorityCacheTest.java` — `Weigher` import changed from Guava shim to Oak API (cascade from `CacheWeights.java`; full `PriorityCache` migration deferred to OAK-12158) ### Exception handling migration - Callers of `cache.get(key, callable)` must switch to `cache.get(key, k -> ...)`