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 -> ...)`