Skip to content

Commit 7255048

Browse files
authored
Attempt to call close() on evicted cache values that implement auto closeable (#3804)
1 parent 3a1731e commit 7255048

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

utils/src/main/java/software/amazon/awssdk/utils/cache/lru/LruCache.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.function.Function;
2222
import software.amazon.awssdk.annotations.SdkProtectedApi;
2323
import software.amazon.awssdk.annotations.ThreadSafe;
24+
import software.amazon.awssdk.utils.Logger;
2425
import software.amazon.awssdk.utils.Validate;
2526

2627
/**
@@ -40,6 +41,8 @@
4041
@ThreadSafe
4142
public final class LruCache<K, V> {
4243

44+
private static final Logger log = Logger.loggerFor(LruCache.class);
45+
4346
private static final int DEFAULT_SIZE = 100;
4447

4548
private final Map<K, CacheEntry<K, V>> cache;
@@ -146,10 +149,21 @@ private void addToQueue(CacheEntry<K, V> entry) {
146149
*/
147150
private void evict() {
148151
leastRecentlyUsed.isEvicted(true);
152+
closeEvictedResourcesIfPossible(leastRecentlyUsed.value);
149153
cache.remove(leastRecentlyUsed.key());
150154
removeFromQueue(leastRecentlyUsed);
151155
}
152156

157+
private void closeEvictedResourcesIfPossible(V value) {
158+
if (value instanceof AutoCloseable) {
159+
try {
160+
((AutoCloseable) value).close();
161+
} catch (Exception e) {
162+
log.warn(() -> "Attempted to close instance that was evicted by cache, but got exception: " + e.getMessage());
163+
}
164+
}
165+
}
166+
153167
public int size() {
154168
return cache.size();
155169
}

utils/src/test/java/software/amazon/awssdk/utils/cache/lru/LruCacheTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
2222

2323
import java.util.ArrayList;
24+
import java.util.Collections;
2425
import java.util.List;
2526
import java.util.concurrent.ExecutorService;
2627
import java.util.concurrent.Executors;
@@ -113,6 +114,37 @@ void when_cacheFillsUp_ValuesAreEvictedFromCache() {
113114
verify(simpleValueSupplier, times(1)).apply(simpleTestKeys.get(4));
114115
}
115116

117+
@Test
118+
void when_closeableValuesAreEvicted_CloseMethodIsCalled() {
119+
int cacheSize = 3;
120+
int evictNum = 2;
121+
LruCache<Integer, CloseableClass> cache = LruCache.builder(CloseableClass::new)
122+
.maxSize(cacheSize)
123+
.build();
124+
CloseableClass.reset();
125+
for (int i = 0; i < cacheSize + evictNum; i++) {
126+
cache.get(i);
127+
}
128+
assertThat(CloseableClass.evictedItems()).isNotEmpty();
129+
assertThat(CloseableClass.evictedItems()).hasSize(evictNum);
130+
assertThat(CloseableClass.evictedItems().get(0)).isEqualTo(0);
131+
assertThat(CloseableClass.evictedItems().get(1)).isEqualTo(1);
132+
}
133+
134+
@Test
135+
void when_closeableValuesAreEvicted_NoExceptionsAreThrownIfCloseFails() {
136+
int cacheSize = 3;
137+
int evictNum = 2;
138+
LruCache<Integer, FaultyCloseableClass> cache = LruCache.builder(FaultyCloseableClass::new)
139+
.maxSize(cacheSize)
140+
.build();
141+
CloseableClass.reset();
142+
for (int i = 0; i < cacheSize + evictNum; i++) {
143+
cache.get(i);
144+
}
145+
assertThat(CloseableClass.evictedItems()).isEmpty();
146+
}
147+
116148
@Test
117149
void when_mostRecentValueIsHit_ValuesAreReorderedCorrectly() {
118150
LruCache<Integer, String> cache = simpleCache.get();
@@ -257,4 +289,42 @@ public String apply(Integer key) {
257289
return value;
258290
}
259291
}
292+
293+
private static class CloseableClass implements AutoCloseable {
294+
295+
private static List<Integer> evictedList = new ArrayList<>();
296+
297+
private final Integer key;
298+
CloseableClass(Integer key) {
299+
this.key = key;
300+
}
301+
public Integer get() throws Exception {
302+
return key;
303+
}
304+
305+
public static void reset() {
306+
evictedList = new ArrayList<>();
307+
}
308+
309+
public static List<Integer> evictedItems() {
310+
return Collections.unmodifiableList(evictedList);
311+
}
312+
313+
@Override
314+
public void close() {
315+
evictedList.add(key);
316+
}
317+
}
318+
319+
private static class FaultyCloseableClass extends CloseableClass {
320+
321+
FaultyCloseableClass(Integer key) {
322+
super(key);
323+
}
324+
325+
@Override
326+
public void close() {
327+
throw new RuntimeException("Could not close resources!");
328+
}
329+
}
260330
}

0 commit comments

Comments
 (0)