• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.cache;
16 
17 import static com.google.common.cache.TestingCacheLoaders.identityLoader;
18 import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener;
19 import static com.google.common.cache.TestingWeighers.constantWeigher;
20 import static com.google.common.cache.TestingWeighers.intKeyWeigher;
21 import static com.google.common.truth.Truth.assertThat;
22 import static java.util.Arrays.asList;
23 
24 import com.google.common.cache.CacheTesting.Receiver;
25 import com.google.common.cache.LocalCache.ReferenceEntry;
26 import com.google.common.cache.TestingCacheLoaders.IdentityLoader;
27 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
28 
29 import junit.framework.TestCase;
30 
31 import java.util.List;
32 import java.util.Set;
33 
34 /**
35  * Tests relating to cache eviction: what does and doesn't count toward maximumSize, what happens
36  * when maximumSize is reached, etc.
37  *
38  * @author mike nonemacher
39  */
40 public class CacheEvictionTest extends TestCase {
41   static final int MAX_SIZE = 100;
42 
testEviction_setMaxSegmentSize()43   public void testEviction_setMaxSegmentSize() {
44     IdentityLoader<Object> loader = identityLoader();
45     for (int i = 1; i < 1000; i++) {
46       LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
47           .maximumSize(i)
48           .build(loader);
49       assertEquals(i, CacheTesting.getTotalSegmentSize(cache));
50     }
51   }
52 
testEviction_setMaxSegmentWeight()53   public void testEviction_setMaxSegmentWeight() {
54     IdentityLoader<Object> loader = identityLoader();
55     for (int i = 1; i < 1000; i++) {
56       LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
57           .maximumWeight(i)
58           .weigher(constantWeigher(1))
59           .build(loader);
60       assertEquals(i, CacheTesting.getTotalSegmentSize(cache));
61     }
62   }
63 
testEviction_maxSizeOneSegment()64   public void testEviction_maxSizeOneSegment() {
65     IdentityLoader<Integer> loader = identityLoader();
66     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
67         .concurrencyLevel(1)
68         .maximumSize(MAX_SIZE)
69         .build(loader);
70     for (int i = 0; i < 2 * MAX_SIZE; i++) {
71       cache.getUnchecked(i);
72       assertEquals(Math.min(i + 1, MAX_SIZE), cache.size());
73     }
74 
75     assertEquals(MAX_SIZE, cache.size());
76     CacheTesting.checkValidState(cache);
77   }
78 
testEviction_maxWeightOneSegment()79   public void testEviction_maxWeightOneSegment() {
80     IdentityLoader<Integer> loader = identityLoader();
81     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
82         .concurrencyLevel(1)
83         .maximumWeight(2 * MAX_SIZE)
84         .weigher(constantWeigher(2))
85         .build(loader);
86     for (int i = 0; i < 2 * MAX_SIZE; i++) {
87       cache.getUnchecked(i);
88       assertEquals(Math.min(i + 1, MAX_SIZE), cache.size());
89     }
90 
91     assertEquals(MAX_SIZE, cache.size());
92     CacheTesting.checkValidState(cache);
93   }
94 
testEviction_maxSize()95   public void testEviction_maxSize() {
96     CountingRemovalListener<Integer, Integer> removalListener = countingRemovalListener();
97     IdentityLoader<Integer> loader = identityLoader();
98     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
99         .maximumSize(MAX_SIZE)
100         .removalListener(removalListener)
101         .build(loader);
102     for (int i = 0; i < 2 * MAX_SIZE; i++) {
103       cache.getUnchecked(i);
104       assertTrue(cache.size() <= MAX_SIZE);
105     }
106 
107     assertEquals(MAX_SIZE, CacheTesting.accessQueueSize(cache));
108     assertEquals(MAX_SIZE, cache.size());
109     CacheTesting.processPendingNotifications(cache);
110     assertEquals(MAX_SIZE, removalListener.getCount());
111     CacheTesting.checkValidState(cache);
112   }
113 
testEviction_maxWeight()114   public void testEviction_maxWeight() {
115     CountingRemovalListener<Integer, Integer> removalListener = countingRemovalListener();
116     IdentityLoader<Integer> loader = identityLoader();
117     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
118         .maximumWeight(2 * MAX_SIZE)
119         .weigher(constantWeigher(2))
120         .removalListener(removalListener)
121         .build(loader);
122     for (int i = 0; i < 2 * MAX_SIZE; i++) {
123       cache.getUnchecked(i);
124       assertTrue(cache.size() <= MAX_SIZE);
125     }
126 
127     assertEquals(MAX_SIZE, CacheTesting.accessQueueSize(cache));
128     assertEquals(MAX_SIZE, cache.size());
129     CacheTesting.processPendingNotifications(cache);
130     assertEquals(MAX_SIZE, removalListener.getCount());
131     CacheTesting.checkValidState(cache);
132   }
133 
testEviction_overflow()134   public void testEviction_overflow() {
135     CountingRemovalListener<Object, Object> removalListener = countingRemovalListener();
136     IdentityLoader<Object> loader = identityLoader();
137     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder()
138         .concurrencyLevel(1)
139         .maximumWeight(1L << 31)
140         .weigher(constantWeigher(Integer.MAX_VALUE))
141         .removalListener(removalListener)
142         .build(loader);
143     cache.getUnchecked(objectWithHash(0));
144     cache.getUnchecked(objectWithHash(0));
145     CacheTesting.processPendingNotifications(cache);
146     assertEquals(1, removalListener.getCount());
147   }
148 
testUpdateRecency_onGet()149   public void testUpdateRecency_onGet() {
150     IdentityLoader<Integer> loader = identityLoader();
151     final LoadingCache<Integer, Integer> cache =
152         CacheBuilder.newBuilder().maximumSize(MAX_SIZE).build(loader);
153     CacheTesting.checkRecency(cache, MAX_SIZE,
154         new Receiver<ReferenceEntry<Integer, Integer>>() {
155           @Override
156           public void accept(ReferenceEntry<Integer, Integer> entry) {
157             cache.getUnchecked(entry.getKey());
158           }
159         });
160   }
161 
testUpdateRecency_onInvalidate()162   public void testUpdateRecency_onInvalidate() {
163     IdentityLoader<Integer> loader = identityLoader();
164     final LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
165         .maximumSize(MAX_SIZE)
166         .concurrencyLevel(1)
167         .build(loader);
168     CacheTesting.checkRecency(cache, MAX_SIZE,
169         new Receiver<ReferenceEntry<Integer, Integer>>() {
170           @Override
171           public void accept(ReferenceEntry<Integer, Integer> entry) {
172             Integer key = entry.getKey();
173             cache.invalidate(key);
174           }
175         });
176   }
177 
testEviction_lru()178   public void testEviction_lru() {
179     // test lru within a single segment
180     IdentityLoader<Integer> loader = identityLoader();
181     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
182         .concurrencyLevel(1)
183         .maximumSize(10)
184         .build(loader);
185     CacheTesting.warmUp(cache, 0, 10);
186     Set<Integer> keySet = cache.asMap().keySet();
187     assertThat(keySet).has().exactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
188 
189     // re-order
190     getAll(cache, asList(0, 1, 2));
191     CacheTesting.drainRecencyQueues(cache);
192     assertThat(keySet).has().exactly(3, 4, 5, 6, 7, 8, 9, 0, 1, 2);
193 
194     // evict 3, 4, 5
195     getAll(cache, asList(10, 11, 12));
196     CacheTesting.drainRecencyQueues(cache);
197     assertThat(keySet).has().exactly(6, 7, 8, 9, 0, 1, 2, 10, 11, 12);
198 
199     // re-order
200     getAll(cache, asList(6, 7, 8));
201     CacheTesting.drainRecencyQueues(cache);
202     assertThat(keySet).has().exactly(9, 0, 1, 2, 10, 11, 12, 6, 7, 8);
203 
204     // evict 9, 0, 1
205     getAll(cache, asList(13, 14, 15));
206     CacheTesting.drainRecencyQueues(cache);
207     assertThat(keySet).has().exactly(2, 10, 11, 12, 6, 7, 8, 13, 14, 15);
208   }
209 
testEviction_weightedLru()210   public void testEviction_weightedLru() {
211     // test weighted lru within a single segment
212     IdentityLoader<Integer> loader = identityLoader();
213     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
214         .concurrencyLevel(1)
215         .maximumWeight(45)
216         .weigher(intKeyWeigher())
217         .build(loader);
218     CacheTesting.warmUp(cache, 0, 10);
219     Set<Integer> keySet = cache.asMap().keySet();
220     assertThat(keySet).has().exactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
221 
222     // re-order
223     getAll(cache, asList(0, 1, 2));
224     CacheTesting.drainRecencyQueues(cache);
225     assertThat(keySet).has().exactly(3, 4, 5, 6, 7, 8, 9, 0, 1, 2);
226 
227     // evict 3, 4, 5
228     getAll(cache, asList(10));
229     CacheTesting.drainRecencyQueues(cache);
230     assertThat(keySet).has().exactly(6, 7, 8, 9, 0, 1, 2, 10);
231 
232     // re-order
233     getAll(cache, asList(6, 7, 8));
234     CacheTesting.drainRecencyQueues(cache);
235     assertThat(keySet).has().exactly(9, 0, 1, 2, 10, 6, 7, 8);
236 
237     // evict 9, 1, 2, 10
238     getAll(cache, asList(15));
239     CacheTesting.drainRecencyQueues(cache);
240     assertThat(keySet).has().exactly(0, 6, 7, 8, 15);
241 
242     // fill empty space
243     getAll(cache, asList(9));
244     CacheTesting.drainRecencyQueues(cache);
245     assertThat(keySet).has().exactly(0, 6, 7, 8, 15, 9);
246 
247     // evict 6
248     getAll(cache, asList(1));
249     CacheTesting.drainRecencyQueues(cache);
250     assertThat(keySet).has().exactly(0, 7, 8, 15, 9, 1);
251   }
252 
testEviction_overweight()253   public void testEviction_overweight() {
254     // test weighted lru within a single segment
255     IdentityLoader<Integer> loader = identityLoader();
256     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
257         .concurrencyLevel(1)
258         .maximumWeight(45)
259         .weigher(intKeyWeigher())
260         .build(loader);
261     CacheTesting.warmUp(cache, 0, 10);
262     Set<Integer> keySet = cache.asMap().keySet();
263     assertThat(keySet).has().exactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
264 
265     // add an at-the-maximum-weight entry
266     getAll(cache, asList(45));
267     CacheTesting.drainRecencyQueues(cache);
268     assertThat(keySet).has().exactly(0, 45);
269 
270     // add an over-the-maximum-weight entry
271     getAll(cache, asList(46));
272     CacheTesting.drainRecencyQueues(cache);
273     assertThat(keySet).has().item(0);
274   }
275 
testEviction_invalidateAll()276   public void testEviction_invalidateAll() {
277     // test that .invalidateAll() resets total weight state correctly
278     IdentityLoader<Integer> loader = identityLoader();
279     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
280         .concurrencyLevel(1)
281         .maximumSize(10)
282         .build(loader);
283 
284     Set<Integer> keySet = cache.asMap().keySet();
285     assertThat(keySet).isEmpty();
286 
287     // add 0, 1, 2, 3, 4
288     getAll(cache, asList(0, 1, 2, 3, 4));
289     CacheTesting.drainRecencyQueues(cache);
290     assertThat(keySet).has().exactly(0, 1, 2, 3, 4);
291 
292     // invalidate all
293     cache.invalidateAll();
294     CacheTesting.drainRecencyQueues(cache);
295     assertThat(keySet).isEmpty();
296 
297     // add 5, 6, 7, 8, 9, 10, 11, 12
298     getAll(cache, asList(5, 6, 7, 8, 9, 10, 11, 12));
299     CacheTesting.drainRecencyQueues(cache);
300     assertThat(keySet).has().exactly(5, 6, 7, 8, 9, 10, 11, 12);
301   }
302 
getAll(LoadingCache<Integer, Integer> cache, List<Integer> keys)303   private void getAll(LoadingCache<Integer, Integer> cache, List<Integer> keys) {
304     for (int i : keys) {
305       cache.getUnchecked(i);
306     }
307   }
308 
objectWithHash(final int hash)309   private Object objectWithHash(final int hash) {
310     return new Object() {
311       @Override public int hashCode() {
312         return hash;
313       }
314     };
315   }
316 }
317