You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by da...@apache.org on 2026/04/15 15:40:37 UTC

(jackrabbit-oak) 01/01: OAK-12157 : removed guava cache from oak-segment-tar

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

daim pushed a commit to branch OAK-12157
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit 1525ba3f39d206e10d66af3df7c55a97bc135b44
Author: rishabhdaim <ri...@gmail.com>
AuthorDate: Wed Apr 15 21:10:18 2026 +0530

    OAK-12157 : removed guava cache from oak-segment-tar
---
 .../jackrabbit/oak/cache/api/CacheBuilder.java     | 21 ++++++++
 .../jackrabbit/oak/segment/CacheWeights.java       |  2 +-
 .../apache/jackrabbit/oak/segment/ReaderCache.java |  7 +--
 .../apache/jackrabbit/oak/segment/RecordCache.java | 28 +++++-----
 .../jackrabbit/oak/segment/SegmentCache.java       | 62 ++++++++++++----------
 .../jackrabbit/oak/segment/file/PriorityCache.java |  2 +-
 .../oak/segment/file/PriorityCacheTest.java        |  2 +-
 specs/guava-cache-removal/PLAN.md                  |  2 +
 specs/guava-cache-removal/TASKS.md                 |  5 +-
 9 files changed, 83 insertions(+), 48 deletions(-)

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 1d70aa97b3..ace6e5fba9 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,6 +46,7 @@ public final class CacheBuilder<K, V> {
 
     private long maximumWeight = -1;
     private long maximumSize = -1;
+    private int initialCapacity = -1;
     private Weigher<? super K, ? super V> weigher;
     private EvictionListener<? super K, ? super V> evictionListener;
     private boolean recordStats;
@@ -86,6 +87,23 @@ public final class CacheBuilder<K, V> {
         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<K, V> 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)}.
@@ -237,6 +255,9 @@ public final class CacheBuilder<K, V> {
     @SuppressWarnings({"unchecked", "rawtypes"})
     private Caffeine<K, V> configureCaffeineBuilder() {
         Caffeine caffeineBuilder = Caffeine.newBuilder();
+        if (initialCapacity >= 0) {
+            caffeineBuilder = caffeineBuilder.initialCapacity(initialCapacity);
+        }
         if (weigher != null) {
             // validateConfiguration() guarantees maximumWeight >= 0 when weigher is set
             Weigher<? super K, ? super V> 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 38033a2148..fbf2342bd7 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 @@ package org.apache.jackrabbit.oak.segment;
 
 import static org.apache.jackrabbit.oak.commons.StringUtils.estimateMemoryUsage;
 
-import org.apache.jackrabbit.guava.common.cache.Weigher;
+import org.apache.jackrabbit.oak.cache.api.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 0acaa9d10e..ea87d1086c 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,7 +25,7 @@ import static org.apache.jackrabbit.oak.segment.CacheWeights.OBJECT_HEADER_SIZE;
 import java.util.Arrays;
 import java.util.function.Function;
 
-import org.apache.jackrabbit.guava.common.cache.Weigher;
+import org.apache.jackrabbit.oak.cache.api.Weigher;
 import org.apache.jackrabbit.oak.cache.CacheLIRS;
 import org.apache.jackrabbit.oak.cache.CacheStats;
 import org.jetbrains.annotations.NotNull;
@@ -74,13 +74,14 @@ public abstract class ReaderCache<T> {
                 .module(name)
                 .maximumWeight(maxWeight)
                 .averageWeight(averageWeight)
-                .weigher(weigher)
+                .weigher(weigher::weigh)
                 .build();
     }
 
     @NotNull
     public CacheStats getStats() {
-        return new CacheStats(cache, name, weigher, cache.getMaxMemory());
+        org.apache.jackrabbit.guava.common.cache.Weigher<CacheKey, T> guavaWeigher = weigher::weigh;
+        return new CacheStats(cache, name, guavaWeigher, 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 90e83a0161..61f0702935 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 static java.util.Objects.requireNonNull;
 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.guava.common.cache.RemovalListener;
-import org.apache.jackrabbit.guava.common.cache.Weigher;
+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.jetbrains.annotations.NotNull;
 
 /**
@@ -137,7 +137,7 @@ public abstract class RecordCache<K> implements Cache<K, RecordId> {
 
     private static class Default<K> extends RecordCache<K> {
         @NotNull
-        private final org.apache.jackrabbit.guava.common.cache.Cache<K, RecordId> cache;
+        private final org.apache.jackrabbit.oak.cache.api.Cache<K, RecordId> cache;
 
         @NotNull
         private final Weigher<K, RecordId> weigher;
@@ -148,10 +148,10 @@ public abstract class RecordCache<K> implements Cache<K, RecordId> {
 
         @Override
         public @NotNull CacheStats getStats() {
-            CacheStats internalStats = cache.stats();
+            org.apache.jackrabbit.oak.cache.api.CacheStatsSnapshot snapshot = cache.stats();
             // any addition to the cache counts as load by our definition
-            return new CacheStats(internalStats.hitCount(), internalStats.missCount(),
-                    loadCount.sum(), 0, 0,  internalStats.evictionCount());
+            return new CacheStats(snapshot.hitCount(), snapshot.missCount(),
+                    loadCount.sum(), 0, 0, snapshot.evictionCount());
         }
 
         static <K> Supplier<RecordCache<K>> defaultFactory(final int size, @NotNull final Weigher<K, RecordId> weigher) {
@@ -159,17 +159,17 @@ public abstract class RecordCache<K> implements Cache<K, RecordId> {
         }
 
         Default(final int size, @NotNull final Weigher<K, RecordId> weigher) {
-            this.cache = CacheBuilder.newBuilder()
+            this.weigher = weigher;
+            this.cache = CacheBuilder.<K, RecordId>newBuilder()
                     .maximumSize(size * 4L / 3)
                     .initialCapacity(size)
-                    .concurrencyLevel(4)
                     .recordStats()
-                    .removalListener((RemovalListener<K, RecordId>) removal -> {
-                        int removedWeight = weigher.weigh(removal.getKey(), removal.getValue());
-                        weight.add(-removedWeight);
+                    .evictionListener((k, v, cause) -> {
+                        if (v != null) {
+                            weight.add(-weigher.weigh(k, v));
+                        }
                     })
                     .build();
-            this.weigher = weigher;
         }
 
         @Override
@@ -186,7 +186,7 @@ public abstract class RecordCache<K> implements Cache<K, RecordId> {
 
         @Override
         public long size() {
-            return cache.size();
+            return cache.estimatedSize();
         }
 
         @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 41238c04b4..daf04a6bc2 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.AtomicInteger;
 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,47 +131,55 @@ public abstract class SegmentCache {
          */
         private NonEmptyCache(long cacheSizeMB) {
             long maximumWeight = cacheSizeMB * 1024 * 1024;
-            this.cache = CacheBuilder.newBuilder()
-                    .concurrencyLevel(16)
+            this.cache = CacheBuilder.<SegmentId, Segment>newBuilder()
                     .maximumWeight(maximumWeight)
                     .weigher(new SegmentCacheWeigher())
-                    .removalListener(this::onRemove)
+                    .evictionListener(this::onRemove)
                     .build();
-            this.stats = new Stats(NAME, maximumWeight, cache::size);
+            this.stats = new Stats(NAME, maximumWeight, cache::estimatedSize);
         }
 
         /**
          * Removal handler called whenever an item is evicted from the cache.
          */
-        private void onRemove(@NotNull RemovalNotification<SegmentId, Segment> notification) {
+        private void onRemove(@NotNull SegmentId key, Segment value, @NotNull EvictionCause cause) {
             stats.evictionCount.incrementAndGet();
-            if (notification.getValue() != null) {
-                stats.currentWeight.addAndGet(-segmentWeight(notification.getValue()));
-            }
-            if (notification.getKey() != null) {
-                notification.getKey().unloaded();
+            if (value != null) {
+                stats.currentWeight.addAndGet(-segmentWeight(value));
             }
+            key.unloaded();
         }
 
         @Override
         @NotNull
         public Segment getSegment(@NotNull SegmentId id, @NotNull Callable<Segment> loader) throws ExecutionException {
             if (id.isDataSegmentId()) {
-                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;
+                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);
                     }
-                });
+                    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 afc9716206..209d158029 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.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import org.apache.jackrabbit.guava.common.cache.CacheStats;
-import org.apache.jackrabbit.guava.common.cache.Weigher;
+import org.apache.jackrabbit.oak.cache.api.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 2eafe8e1d7..bfb8850ee9 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 static org.junit.Assume.assumeTrue;
 
 import java.util.Random;
 
-import org.apache.jackrabbit.guava.common.cache.Weigher;
+import org.apache.jackrabbit.oak.cache.api.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 75e328360b..dab7f82346 100644
--- a/specs/guava-cache-removal/PLAN.md
+++ b/specs/guava-cache-removal/PLAN.md
@@ -304,6 +304,7 @@ public final class CacheBuilder<K, V> {
     private String module;
     private long maximumWeight = -1;
     private long maximumSize = -1;
+    private int initialCapacity = -1;
     private Weigher<K, V> weigher;
     private EvictionListener<K, V> removalListener;
     private boolean recordStats;
@@ -315,6 +316,7 @@ public final class CacheBuilder<K, V> {
 
     public CacheBuilder<K, V> maximumWeight(long maximumWeight) { ... }
     public CacheBuilder<K, V> maximumSize(long maximumSize) { ... }
+    public CacheBuilder<K, V> initialCapacity(int initialCapacity) { ... }
     public CacheBuilder<K, V> weigher(Weigher<K, V> weigher) { ... }
     public CacheBuilder<K, V> removalListener(EvictionListener<K, V> listener) { ... }
     public CacheBuilder<K, V> recordStats() { ... }
diff --git a/specs/guava-cache-removal/TASKS.md b/specs/guava-cache-removal/TASKS.md
index 566de1959e..ed9458fb97 100644
--- a/specs/guava-cache-removal/TASKS.md
+++ b/specs/guava-cache-removal/TASKS.md
@@ -181,7 +181,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`, `weigher(Weigher)`, `evictionListener(EvictionListener)`, `recordStats`, `expireAfterAccess`, `expireAfterWrite`, `refreshAfterWrite`.
+  Builder fields: `maximumWeight`, `maximumSize`, `initialCapacity`, `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:
@@ -249,6 +249,8 @@ Remove all `<!-- TODO OAK-TASK2: ... -->` 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
@@ -476,6 +478,7 @@ 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 -> ...)`