Skip to content

Commit

Permalink
[UNDERTOW-2010] Add invalidate() to caching manager
Browse files Browse the repository at this point in the history
  • Loading branch information
baranowb committed Jun 7, 2023
1 parent 219e23a commit f36fa32
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 27 deletions.
13 changes: 13 additions & 0 deletions core/src/main/java/io/undertow/server/handlers/cache/LRUCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package io.undertow.server.handlers.cache;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
Expand Down Expand Up @@ -155,6 +156,18 @@ public void clear() {
accessQueue.clear();
}

public Set<K> keys(){
return cache.keySet();
}

public int getMaxEntries() {
return maxEntries;
}

public int getMaxAge() {
return maxAge;
}

public static final class CacheEntry<K, V> {

private static final Object CLAIM_TOKEN = new Object();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import io.undertow.UndertowLogger;
import io.undertow.server.handlers.cache.DirectBufferCache;
import io.undertow.server.handlers.cache.DirectBufferCache.CacheEntry;
import io.undertow.server.handlers.cache.LRUCache;

/**
Expand All @@ -49,7 +52,9 @@ public class CachingResourceManager implements ResourceManager {
/**
* A cache of file metadata, such as if a file exists or not
*/
private final LRUCache<String, Object> cache;
private LRUCache<String, Object> cache;

private final ReentrantReadWriteLock cacheAccessLock = new ReentrantReadWriteLock();

private final int maxAge;

Expand Down Expand Up @@ -87,41 +92,59 @@ public CachedResource getResource(final String p) throws IOException {
} else {
path = p;
}
Object res = cache.get(path);
if (res instanceof NoResourceMarker) {
NoResourceMarker marker = (NoResourceMarker) res;
long nextCheck = marker.getNextCheckTime();
if(nextCheck > 0) {
long time = System.currentTimeMillis();
if(time > nextCheck) {
marker.setNextCheckTime(time + maxAge);
if(underlyingResourceManager.getResource(path) != null) {
cache.remove(path);
//ReadLock will allow multiple reads and changes, guarded by WriteLock for purge
final Lock readLock = this.cacheAccessLock.readLock();
try {
readLock.lock();
Object res = cache.get(path);
if (res instanceof NoResourceMarker) {
NoResourceMarker marker = (NoResourceMarker) res;
long nextCheck = marker.getNextCheckTime();
if(nextCheck > 0) {
long time = System.currentTimeMillis();
if(time > nextCheck) {
marker.setNextCheckTime(time + maxAge);
if(underlyingResourceManager.getResource(path) != null) {
cache.remove(path);
} else {
return null;
}
} else {
return null;
}
} else {
return null;
}
} else {
} else if (res != null) {
CachedResource resource = (CachedResource) res;
if (resource.checkStillValid()) {
return resource;
} else {
//NOTE: non locking
invalidate(this.cache, path);
}
}
final Resource underlying = underlyingResourceManager.getResource(path);
if (underlying == null) {
cache.add(path, new NoResourceMarker(maxAge > 0 ? System.currentTimeMillis() + maxAge : -1));
return null;
//NOTE; in case of purge, no need to clear this, if there was resource, now there is none
//invalidete() will purge potential dataCache entry
}
} else if (res != null) {
CachedResource resource = (CachedResource) res;
if (resource.checkStillValid()) {
return resource;
} else {
invalidate(path);
final CachedResource resource = new CachedResource(this, underlying, path);
final DirectBufferCache dataCache = getDataCache();
if(dataCache != null) {
final CacheEntry o = dataCache.get(resource.getCacheKey());
if (o != null) {
// lazy remove, #invalidate() might have not get to this point.
dataCache.remove(o.key());
}
}
cache.add(path, resource);
return resource;
} finally {
readLock.unlock();
}
final Resource underlying = underlyingResourceManager.getResource(path);
if (underlying == null) {
cache.add(path, new NoResourceMarker(maxAge > 0 ? System.currentTimeMillis() + maxAge : -1));
return null;
}
final CachedResource resource = new CachedResource(this, underlying, path);
cache.add(path, resource);
return resource;
}

@Override
Expand All @@ -140,10 +163,36 @@ public void removeResourceChangeListener(ResourceChangeListener listener) {
}

public void invalidate(String path) {
//NOTE: dont need write here?
final Lock readLock = this.cacheAccessLock.readLock();
readLock.lock();
this.invalidate(this.cache, path);
readLock.unlock();
}

public void invalidate() {
final Lock writeLock = this.cacheAccessLock.writeLock();
final LRUCache<String, Object> localCopy = this.cache;
writeLock.lock();
try {
this.cache = new LRUCache(localCopy.getMaxEntries(),localCopy.getMaxAge());
} finally {
writeLock.unlock();
}
for(String key:localCopy.keys()) {
//clear dataCache while new entries are made. This can potentially
//remove dataCache entries, but those can be recreated.
this.invalidate(localCopy, key);
}
//just in case
localCopy.clear();
}

private void invalidate(LRUCache<String, Object> localCopy, String path) {
if(path.startsWith("/")) {
path = path.substring(1);
}
Object entry = cache.remove(path);
Object entry = localCopy.remove(path);
if (entry instanceof CachedResource) {
((CachedResource) entry).invalidate();
}
Expand Down

0 comments on commit f36fa32

Please sign in to comment.