Skip to content

Commit 6548c02

Browse files
baranowbfl4via
authored andcommitted
[UNDERTOW-2010] Add invalidate() to caching manager
1 parent 5da0017 commit 6548c02

File tree

2 files changed

+95
-12
lines changed

2 files changed

+95
-12
lines changed

core/src/main/java/io/undertow/server/handlers/cache/LRUCache.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ public void clear() {
172172
accessQueue.clear();
173173
}
174174

175+
public int getMaxEntries() {
176+
return maxEntries;
177+
}
178+
179+
public int getMaxAge() {
180+
return maxAge;
181+
}
182+
175183
public static final class CacheEntry<K, V> {
176184

177185
private static final Object CLAIM_TOKEN = new Object();

core/src/main/java/io/undertow/server/handlers/resource/CachingResourceManager.java

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
import java.io.IOException;
2222
import java.util.Collection;
2323
import java.util.Set;
24+
import java.util.concurrent.locks.Lock;
25+
import java.util.concurrent.locks.ReentrantReadWriteLock;
2426

2527
import io.undertow.UndertowLogger;
2628
import io.undertow.server.handlers.cache.DirectBufferCache;
29+
import io.undertow.server.handlers.cache.DirectBufferCache.CacheEntry;
2730
import io.undertow.server.handlers.cache.LRUCache;
2831

2932
/**
@@ -57,7 +60,9 @@ public class CachingResourceManager implements ResourceManager {
5760
/**
5861
* A cache of file metadata, such as if a file exists or not
5962
*/
60-
private final LRUCache<String, Object> cache;
63+
private LRUCache<String, Object> cache;
64+
65+
private final ReentrantReadWriteLock cacheAccessLock = new ReentrantReadWriteLock();
6166

6267
private int maxAge;
6368

@@ -107,16 +112,30 @@ public CachedResource getResource(final String p) throws IOException {
107112
} else {
108113
path = p;
109114
}
110-
Object res = cache.get(path);
115+
// ReadLock will allow multiple reads and changes, guarded by WriteLock for purge
116+
final Lock readLock = this.cacheAccessLock.readLock();
117+
final Lock writeLock = this.cacheAccessLock.writeLock();
118+
Object res = null;
119+
try {
120+
readLock.lock();
121+
res = cache.get(path);
122+
} finally {
123+
readLock.unlock();
124+
}
111125
if (res instanceof NoResourceMarker) {
112126
NoResourceMarker marker = (NoResourceMarker) res;
113127
long nextCheck = marker.getNextCheckTime();
114-
if(nextCheck > 0) {
128+
if (nextCheck > 0) {
115129
long time = System.currentTimeMillis();
116-
if(time > nextCheck) {
130+
if (time > nextCheck) {
117131
marker.setNextCheckTime(time + maxAge);
118-
if(underlyingResourceManager.getResource(path) != null) {
119-
cache.remove(path);
132+
if (underlyingResourceManager.getResource(path) != null) {
133+
try {
134+
writeLock.lock();
135+
cache.remove(path);
136+
} finally {
137+
writeLock.unlock();
138+
}
120139
} else {
121140
return null;
122141
}
@@ -131,19 +150,47 @@ public CachedResource getResource(final String p) throws IOException {
131150
if (resource.checkStillValid()) {
132151
return resource;
133152
} else {
134-
invalidate(path);
153+
try {
154+
writeLock.lock();
155+
invalidate(this.cache, path);
156+
} finally {
157+
writeLock.unlock();
158+
}
135159
}
136160
}
137161
final Resource underlying = underlyingResourceManager.getResource(path);
138162
if (underlying == null) {
139-
if(this.maxAge != MAX_AGE_NO_CACHING) {
140-
cache.add(path, new NoResourceMarker(maxAge > 0 ? System.currentTimeMillis() + maxAge : -1));
163+
if (this.maxAge != MAX_AGE_NO_CACHING) {
164+
try {
165+
writeLock.lock();
166+
cache.add(path, new NoResourceMarker(maxAge > 0 ? System.currentTimeMillis() + maxAge : -1));
167+
} finally {
168+
writeLock.unlock();
169+
}
141170
}
142171
return null;
172+
// NOTE; in case of purge, no need to clear this, if there was resource, now there is none
173+
// invalidete() will purge potential dataCache entry
174+
}
175+
CachedResource resource = null;
176+
try {
177+
writeLock.lock();
178+
resource = new CachedResource(this, underlying, path);
179+
final DirectBufferCache dataCache = getDataCache();
180+
if (dataCache != null) {
181+
final CacheEntry o = dataCache.get(resource.getCacheKey());
182+
if (o != null) {
183+
// lazy remove, #invalidate() might have not get to this point.
184+
dataCache.remove(o.key());
185+
}
186+
}
187+
188+
cache.add(path, resource);
189+
} finally {
190+
writeLock.unlock();
143191
}
144-
final CachedResource resource = new CachedResource(this, underlying, path);
145-
cache.add(path, resource);
146192
return resource;
193+
147194
}
148195

149196
@Override
@@ -162,10 +209,38 @@ public void removeResourceChangeListener(ResourceChangeListener listener) {
162209
}
163210

164211
public void invalidate(String path) {
212+
final Lock writeLock = this.cacheAccessLock.writeLock();
213+
writeLock.lock();
214+
try {
215+
this.invalidate(this.cache, path);
216+
} finally {
217+
writeLock.unlock();
218+
}
219+
}
220+
221+
public void invalidate() {
222+
final Lock writeLock = this.cacheAccessLock.writeLock();
223+
final LRUCache<String, Object> localCopy = this.cache;
224+
writeLock.lock();
225+
try {
226+
this.cache = new LRUCache(localCopy.getMaxEntries(),localCopy.getMaxAge());
227+
} finally {
228+
writeLock.unlock();
229+
}
230+
for(String key:localCopy.keySet()) {
231+
//clear dataCache while new entries are made. This can potentially
232+
//remove dataCache entries, but those can be recreated.
233+
this.invalidate(localCopy, key);
234+
}
235+
//just in case
236+
localCopy.clear();
237+
}
238+
239+
private void invalidate(LRUCache<String, Object> localCopy, String path) {
165240
if(path.startsWith("/")) {
166241
path = path.substring(1);
167242
}
168-
Object entry = cache.remove(path);
243+
Object entry = localCopy.remove(path);
169244
if (entry instanceof CachedResource) {
170245
((CachedResource) entry).invalidate();
171246
}

0 commit comments

Comments
 (0)