Skip to content
Merged
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
@@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.cache;

import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.annotation.versioning.ProviderType;

/**
* A size-bounded, thread-safe cache.
*
* <p>Implementations may use different eviction strategies (LIRS, W-TinyLFU/Caffeine,
* etc.) but callers see only this interface. Obtain instances via OakCacheBuilder.</p>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* etc.) but callers see only this interface. Obtain instances via OakCacheBuilder.</p>
* etc.) but callers see only this interface. Obtain instances via OakCacheBuilder.

*
* <!-- TODO OAK-TASK2: replace plain-text OakCacheBuilder reference above with

Check warning on line 33 in oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/OakCache.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=org.apache.jackrabbit%3Ajackrabbit-oak&issues=AZ0esniKapyM9SrSN2Wl&open=AZ0esniKapyM9SrSN2Wl&pullRequest=2814
* {@link OakCacheBuilder} once OakCacheBuilder is introduced in TASK-2. -->
*
* <p>The {@link #get(Object, Function)} signature matches Caffeine's
* {@code Cache.get(K, Function)} contract exactly: the loader is key-aware and any
* exception thrown by the mapping function propagates as an unchecked
* {@code RuntimeException} (or {@code CompletionException}).</p>
*
* @param <K> the type of cache keys
* @param <V> the type of cache values
*/
@ProviderType
public interface OakCache<K, V> {

/**
* Returns the value associated with {@code key} if it is currently in the
* cache, otherwise {@code null}.
*
* @param key the key to look up (must not be null)
* @return the cached value, or {@code null} if not present
*/
@Nullable
V getIfPresent(@NotNull K key);

/**
* Returns the value associated with {@code key}, computing it via
* {@code mappingFunction} and caching the result if it was absent.
*
* <p>Matches Caffeine's {@code Cache.get(K, Function)} contract: any exception
* thrown by the mapping function propagates as an unchecked
* {@code RuntimeException} or {@code CompletionException}. Implementations
* backed by CacheLIRS bridge internally by wrapping any checked
* {@code ExecutionException} into {@code CompletionException}.</p>
*
* @param key the key whose associated value is to be returned (must not be null)
* @param mappingFunction the function to compute a value if the key is absent (must not be null)
* @return the current (existing or computed) value, or {@code null} if the
* mapping function returns {@code null}
*/
@Nullable
V get(@NotNull K key, @NotNull Function<? super K, ? extends V> mappingFunction);

/**
* Associates {@code value} with {@code key} in the cache. If the cache
* previously contained a value for {@code key} it is replaced.
*
* @param key the key (must not be null)
* @param value the value (must not be null)
*/
void put(@NotNull K key, @NotNull V value);

/**
* Discards any cached value for {@code key}.
*
* @param key the key to invalidate (must not be null)
*/
void invalidate(@NotNull K key);

/**
* Discards all entries in the cache.
*/
void invalidateAll();

/**
* Discards any cached values for the given keys.
*
* <p><em>Note: no Oak module currently calls this method. It may be removed
* from the interface in a future release if it remains unused.</em></p>
*
* @param keys the keys to invalidate (must not be null)
*/
void invalidateAll(@NotNull Iterable<? extends K> keys);

/**
* Returns the approximate number of entries in the cache.
*
* <p><em>Note: no Oak module currently calls this method directly;
* {@code asMap().size()} is used instead. It may be removed from the
* interface in a future release if it remains unused.</em></p>
*
* @return the approximate entry count
*/
long estimatedSize();

/**
* Returns a snapshot of this cache's cumulative statistics. If statistics
* collection was not enabled via {@code OakCacheBuilder.recordStats()}, all
* <!-- TODO OAK-TASK2: restore {@link OakCacheBuilder#recordStats()} once TASK-2 is merged. -->

Check warning on line 120 in oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/OakCache.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=org.apache.jackrabbit%3Ajackrabbit-oak&issues=AZ0esniKapyM9SrSN2Wm&open=AZ0esniKapyM9SrSN2Wm&pullRequest=2814
* counters will be zero.
*
* @return a stats snapshot (never null)
*/
@NotNull
OakCacheStats stats();

/**
* Returns a view of the entries stored in this cache as a thread-safe map.
* Modifications to the map directly affect the cache.
*
* @return a live concurrent map view of the cache entries (never null)
*/
@NotNull
ConcurrentMap<K, V> asMap();

/**
* Returns a map of the values currently present in the cache for the given
* keys. The returned map contains only keys that were present at the time
* of the call.
*
* <p><em>Note: no Oak module currently calls this method (CacheLIRS throws
* {@code UnsupportedOperationException} for it). It may be removed from the
* interface in a future release if it remains unused.</em></p>
*
* @param keys the keys to look up (must not be null)
* @return a map of keys to cached values for those keys that were present (never null)
*/
@NotNull
Map<K, V> getAllPresent(@NotNull Iterable<? extends K> keys);

/**
* Performs any pending maintenance operations needed by the cache.
*
* <p><em>Note: no Oak module currently calls this method; the CacheLIRS
* implementation is a no-op. It may be removed from the interface in a
* future release if it remains unused.</em></p>
*/
void cleanUp();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.cache;

import org.jetbrains.annotations.NotNull;

/**
* Computes or loads a value for a missing cache entry.
*
* <p>Used with {@code OakCacheBuilder.build(OakCacheLoader)} to create an
* {@link OakLoadingCache}. The loader is key-aware (receives the lookup key)
* and may throw a checked exception.</p>
*
* <!-- TODO OAK-TASK2: restore {@link OakCacheBuilder#build(OakCacheLoader)} once TASK-2 is merged. -->

Check warning on line 28 in oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/OakCacheLoader.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=org.apache.jackrabbit%3Ajackrabbit-oak&issues=AZ0esniiapyM9SrSN2Wq&open=AZ0esniiapyM9SrSN2Wq&pullRequest=2814
*
* @param <K> the type of cache keys
* @param <V> the type of cache values
*/
@FunctionalInterface
public interface OakCacheLoader<K, V> {

/**
* Computes the value for the given key.
*
* @param key the key whose value should be loaded (never null)
* @return the loaded value (never null)
* @throws Exception if the value cannot be loaded
*/
@NotNull
V load(@NotNull K key) throws Exception;

Check warning on line 44 in oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/OakCacheLoader.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace generic exceptions with specific library exceptions or a custom exception.

See more on https://sonarcloud.io/project/issues?id=org.apache.jackrabbit%3Ajackrabbit-oak&issues=AZ0esniiapyM9SrSN2Wp&open=AZ0esniiapyM9SrSN2Wp&pullRequest=2814
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.cache;

import org.jetbrains.annotations.NotNull;

/**
* An immutable snapshot of cache statistics at a point in time.
*
* <p>Returned by {@link OakCache#stats()}. All counters are cumulative since
* the cache was created. Use {@link #minus(OakCacheStats)} to compute a delta
* between two snapshots.</p>
*
* @param hitCount number of times a requested key was found in the cache
* @param missCount number of times a requested key was not found in the cache
* @param loadSuccessCount number of times a new value was successfully loaded
* @param loadFailureCount number of times a value load attempt threw an exception
* @param totalLoadTime total time spent loading new values, in nanoseconds
* @param evictionCount number of entries evicted from the cache
*/
public record OakCacheStats(
long hitCount,
long missCount,
long loadSuccessCount,
long loadFailureCount,
long totalLoadTime,
long evictionCount) {

/**
* Returns the total number of requests (hits + misses).
*
* @return request count
*/
public long requestCount() {
return hitCount + missCount;
}

/**
* Returns the ratio of cache requests that were hits, or {@code 1.0} if no
* requests have been made.
*
* @return hit rate between 0.0 and 1.0
*/
public double hitRate() {
long requests = requestCount();
return requests == 0 ? 1.0 : (double) hitCount / requests;
}

/**
* Returns the ratio of cache requests that were misses, or {@code 0.0} if
* no requests have been made.
*
* @return miss rate between 0.0 and 1.0
*/
public double missRate() {
long requests = requestCount();
return requests == 0 ? 0.0 : (double) missCount / requests;
}

/**
* Returns the difference between this snapshot and an earlier {@code other}
* snapshot, useful for computing per-interval deltas.
*
* @param other the earlier snapshot to subtract (must not be null)
* @return a new snapshot representing the delta
*/
@NotNull
public OakCacheStats minus(@NotNull OakCacheStats other) {
return new OakCacheStats(
Math.max(0, hitCount - other.hitCount),
Math.max(0, missCount - other.missCount),
Math.max(0, loadSuccessCount - other.loadSuccessCount),
Math.max(0, loadFailureCount - other.loadFailureCount),
Math.max(0, totalLoadTime - other.totalLoadTime),
Math.max(0, evictionCount - other.evictionCount)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.cache;

import java.util.concurrent.CompletionException;

import org.jetbrains.annotations.NotNull;
import org.osgi.annotation.versioning.ProviderType;

/**
* A cache that automatically loads absent entries from a pre-configured
* {@link OakCacheLoader}.
*
* <p>Obtain instances via {@code OakCacheBuilder.build(OakCacheLoader)}.
* <!-- TODO OAK-TASK2: restore {@link OakCacheBuilder#build(OakCacheLoader)} once TASK-2 is merged. -->

Check warning on line 29 in oak-core-spi/src/main/java/org/apache/jackrabbit/oak/cache/OakLoadingCache.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=org.apache.jackrabbit%3Ajackrabbit-oak&issues=AZ0esnddapyM9SrSN2Wk&open=AZ0esnddapyM9SrSN2Wk&pullRequest=2814
* Matches Caffeine's {@code LoadingCache} contract: {@link #get(Object)} throws
* an unchecked {@link CompletionException} on loader failure. Implementations
* backed by CacheLIRS bridge the checked {@code ExecutionException} by wrapping
* it into {@code CompletionException}.</p>
*
* @param <K> the type of cache keys
* @param <V> the type of cache values
*/
@ProviderType
public interface OakLoadingCache<K, V> extends OakCache<K, V> {

/**
* Returns the value associated with {@code key}, loading it via the
* pre-configured {@link OakCacheLoader} if absent.
*
* @param key the key whose value should be returned or loaded (must not be null)
* @return the current or newly loaded value (never null)
* @throws CompletionException wrapping the loader's exception if loading fails,
* matching Caffeine's {@code LoadingCache.get(K)} contract
*/
@NotNull
V get(@NotNull K key);

/**
* Triggers a reload of the value for {@code key}. The stale value remains
* available until the reload completes (Caffeine's {@code refreshAfterWrite}
* semantics; best-effort for the CacheLIRS implementation).
*
* @param key the key whose value should be refreshed (must not be null)
*/
void refresh(@NotNull K key);
}
Loading
Loading