Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ 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;
Expand Down Expand Up @@ -87,23 +86,6 @@ public CacheBuilder<K, V> 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<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)}.
Expand Down Expand Up @@ -255,9 +237,6 @@ private LoadingCache<K, V> buildCaffeine(CacheLoader<K, V> loader) {
@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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -56,13 +52,10 @@ public abstract class ReaderCache<T> {
private final FastCache<T> fastCache;

/**
* The slower (LIRS) cache, exposed through the Oak Cache API.
* The slower (LIRS) cache.
*/
@NotNull
private final Cache<CacheKey, T> cache;

@NotNull
private final AbstractCacheStats cacheStats;
private final CacheLIRS<CacheKey, T> cache;

/**
* Create a new string cache.
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -137,7 +137,7 @@ public long estimateCurrentWeight() {

private static class Default<K> extends RecordCache<K> {
@NotNull
private final org.apache.jackrabbit.oak.cache.api.Cache<K, RecordId> cache;
private final org.apache.jackrabbit.guava.common.cache.Cache<K, RecordId> cache;

@NotNull
private final Weigher<K, RecordId> weigher;
Expand All @@ -148,28 +148,28 @@ private static class Default<K> extends RecordCache<K> {

@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 <K> Supplier<RecordCache<K>> defaultFactory(final int size, @NotNull final Weigher<K, RecordId> weigher) {
return () -> new Default<>(size, requireNonNull(weigher));
}

Default(final int size, @NotNull final Weigher<K, RecordId> weigher) {
this.weigher = weigher;
this.cache = CacheBuilder.<K, RecordId>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<K, RecordId>) removal -> {
int removedWeight = weigher.weigh(removal.getKey(), removal.getValue());
weight.add(-removedWeight);
})
.build();
this.weigher = weigher;
}

@Override
Expand All @@ -186,7 +186,7 @@ public RecordId get(@NotNull K key) {

@Override
public long size() {
return cache.estimatedSize();
return cache.size();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -131,55 +131,47 @@ private static class NonEmptyCache extends SegmentCache {
*/
private NonEmptyCache(long cacheSizeMB) {
long maximumWeight = cacheSizeMB * 1024 * 1024;
this.cache = CacheBuilder.<SegmentId, Segment>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<SegmentId, Segment> 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<Segment> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 0 additions & 2 deletions specs/guava-cache-removal/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,6 @@ 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;
Expand All @@ -317,7 +316,6 @@ 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() { ... }
Expand Down
5 changes: 1 addition & 4 deletions specs/guava-cache-removal/TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -265,8 +265,6 @@ 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
Expand Down Expand Up @@ -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 -> ...)`
Expand Down
Loading