Skip to content

Commit

Permalink
Fix get in evictable cache, as it did not update last accessed timest…
Browse files Browse the repository at this point in the history
…amp (helidon-io#7422)

(cherry picked from commit 763e4f6)
  • Loading branch information
tomas-langer committed Aug 25, 2023
1 parent 832d35e commit a8e4ebf
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021 Oracle and/or its affiliates.
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.security.providers.common;

import java.util.Optional;
Expand All @@ -29,15 +30,16 @@
* Default implementation of {@link EvictableCache}.
*/
class EvictableCacheImpl<K, V> implements EvictableCache<K, V> {
/**
* An implementation that does no caching.
*/
static final EvictableCache<?, ?> NO_CACHE = new EvictableCache<>() { };

/**
* Number of threads in the scheduled thread pool to evict records.
*/
private static final int EVICT_THREAD_COUNT = 1;
private static final ScheduledThreadPoolExecutor EXECUTOR;
/**
* An implementation that does no caching.
*/
static final EvictableCache<?, ?> NO_CACHE = new EvictableCache() { };

static {
ThreadFactory jf = new ThreadFactory() {
Expand Down Expand Up @@ -79,7 +81,7 @@ public Thread newThread(Runnable r) {
@Override
public Optional<V> remove(K key) {
CacheRecord<K, V> removed = cacheMap.remove(key);
if (null == removed) {
if (removed == null) {
return Optional.empty();
}

Expand All @@ -88,7 +90,10 @@ public Optional<V> remove(K key) {

@Override
public Optional<V> get(K key) {
return getRecord(key).flatMap(this::validate).map(CacheRecord::getValue);
return getRecord(key)
.flatMap(this::validate)
.map(this::accessed)
.map(CacheRecord::getValue);
}

@Override
Expand All @@ -113,7 +118,7 @@ public void close() {

void evict() {
cacheMap.forEachKey(evictParallelismThreshold, key -> cacheMap.compute(key, (key1, cacheRecord) -> {
if ((null == cacheRecord) || evictor.apply(cacheRecord.getKey(), cacheRecord.getValue())) {
if ((cacheRecord == null) || evictor.apply(cacheRecord.getKey(), cacheRecord.getValue())) {
return null;
} else {
if (cacheRecord.isValid(cacheTimeoutNanos, overallTimeoutNanos)) {
Expand All @@ -125,6 +130,11 @@ void evict() {
}));
}

private CacheRecord<K, V> accessed(CacheRecord<K, V> record) {
record.accessed();
return record;
}

private Optional<CacheRecord<K, V>> validate(CacheRecord<K, V> record) {
if (record.isValid(cacheTimeoutNanos, overallTimeoutNanos) && !evictor.apply(record.getKey(), record.getValue())) {
return Optional.of(record);
Expand All @@ -135,9 +145,8 @@ private Optional<CacheRecord<K, V>> validate(CacheRecord<K, V> record) {

private Optional<V> doComputeValue(K key, Supplier<Optional<V>> valueSupplier) {
CacheRecord<K, V> record = cacheMap.compute(key, (s, cacheRecord) -> {
if ((null != cacheRecord) && cacheRecord.isValid(cacheTimeoutNanos, overallTimeoutNanos)) {
cacheRecord.accessed();
return cacheRecord;
if ((cacheRecord != null) && cacheRecord.isValid(cacheTimeoutNanos, overallTimeoutNanos)) {
return accessed(cacheRecord);
}

if (cacheMap.size() >= cacheMaxSize) {
Expand All @@ -149,7 +158,7 @@ private Optional<V> doComputeValue(K key, Supplier<Optional<V>> valueSupplier) {
.orElse(null);
});

if (null == record) {
if (record == null) {
return Optional.empty();
} else {
return Optional.of(record.value);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021 Oracle and/or its affiliates.
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -63,6 +63,27 @@ void testEviction() throws InterruptedException {
cache.close();
}

@Test
void testGetUpdatesAccessed() throws InterruptedException {
EvictableCache<String, String> cache = EvictableCache.<String, String>builder()
.evictSchedule(100, 100, TimeUnit.MILLISECONDS)
.timeout(50, TimeUnit.MILLISECONDS)
.build();

assertThat(cache.computeValue("one", () -> Optional.of("1")), is(Optional.of("1")));

for (int i = 0; i < 10; i++) {
assertThat(cache.get("one"), is(Optional.of("1")));
// this should amount to more than 200 millis, which would time out if the record is not marked as accessed
Thread.sleep(20);
}
// now let's make sure it times out
TimeUnit.MILLISECONDS.sleep(200);
assertThat(cache.get("one"), is(EMPTY));

cache.close();
}

@Test
void testOverallTimeout() throws InterruptedException {
EvictableCache<String, String> cache = EvictableCache.<String, String>builder()
Expand Down

0 comments on commit a8e4ebf

Please sign in to comment.