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.bulkLoader; 18 import static com.google.common.cache.TestingCacheLoaders.constantLoader; 19 import static com.google.common.cache.TestingCacheLoaders.errorLoader; 20 import static com.google.common.cache.TestingCacheLoaders.exceptionLoader; 21 import static com.google.common.cache.TestingCacheLoaders.identityLoader; 22 import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener; 23 import static com.google.common.truth.Truth.assertThat; 24 import static java.lang.Thread.currentThread; 25 import static java.util.Arrays.asList; 26 import static java.util.concurrent.TimeUnit.MILLISECONDS; 27 import static org.junit.Assert.assertThrows; 28 29 import com.google.common.cache.CacheLoader.InvalidCacheLoadException; 30 import com.google.common.cache.TestingCacheLoaders.CountingLoader; 31 import com.google.common.cache.TestingCacheLoaders.IdentityLoader; 32 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener; 33 import com.google.common.collect.ImmutableList; 34 import com.google.common.collect.ImmutableMap; 35 import com.google.common.collect.Lists; 36 import com.google.common.collect.Maps; 37 import com.google.common.testing.FakeTicker; 38 import com.google.common.testing.TestLogHandler; 39 import com.google.common.util.concurrent.Callables; 40 import com.google.common.util.concurrent.ExecutionError; 41 import com.google.common.util.concurrent.Futures; 42 import com.google.common.util.concurrent.ListenableFuture; 43 import com.google.common.util.concurrent.UncheckedExecutionException; 44 import java.io.IOException; 45 import java.lang.ref.WeakReference; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.Map.Entry; 49 import java.util.concurrent.Callable; 50 import java.util.concurrent.ConcurrentMap; 51 import java.util.concurrent.CountDownLatch; 52 import java.util.concurrent.ExecutionException; 53 import java.util.concurrent.TimeUnit; 54 import java.util.concurrent.atomic.AtomicInteger; 55 import java.util.concurrent.atomic.AtomicReferenceArray; 56 import java.util.logging.LogRecord; 57 import junit.framework.TestCase; 58 59 /** 60 * Tests relating to cache loading: concurrent loading, exceptions during loading, etc. 61 * 62 * @author mike nonemacher 63 */ 64 public class CacheLoadingTest extends TestCase { 65 TestLogHandler logHandler; 66 67 @Override setUp()68 public void setUp() throws Exception { 69 super.setUp(); 70 logHandler = new TestLogHandler(); 71 LocalCache.logger.addHandler(logHandler); 72 } 73 74 @Override tearDown()75 public void tearDown() throws Exception { 76 super.tearDown(); 77 // TODO(cpovirk): run tests in other thread instead of messing with main thread interrupt status 78 currentThread().interrupted(); 79 LocalCache.logger.removeHandler(logHandler); 80 } 81 popLoggedThrowable()82 private Throwable popLoggedThrowable() { 83 List<LogRecord> logRecords = logHandler.getStoredLogRecords(); 84 assertEquals(1, logRecords.size()); 85 LogRecord logRecord = logRecords.get(0); 86 logHandler.clear(); 87 return logRecord.getThrown(); 88 } 89 checkNothingLogged()90 private void checkNothingLogged() { 91 assertThat(logHandler.getStoredLogRecords()).isEmpty(); 92 } 93 checkLoggedCause(Throwable t)94 private void checkLoggedCause(Throwable t) { 95 assertThat(popLoggedThrowable()).hasCauseThat().isSameInstanceAs(t); 96 } 97 checkLoggedInvalidLoad()98 private void checkLoggedInvalidLoad() { 99 assertThat(popLoggedThrowable()).isInstanceOf(InvalidCacheLoadException.class); 100 } 101 testLoad()102 public void testLoad() throws ExecutionException { 103 LoadingCache<Object, Object> cache = 104 CacheBuilder.newBuilder().recordStats().build(identityLoader()); 105 CacheStats stats = cache.stats(); 106 assertEquals(0, stats.missCount()); 107 assertEquals(0, stats.loadSuccessCount()); 108 assertEquals(0, stats.loadExceptionCount()); 109 assertEquals(0, stats.hitCount()); 110 111 Object key = new Object(); 112 assertSame(key, cache.get(key)); 113 stats = cache.stats(); 114 assertEquals(1, stats.missCount()); 115 assertEquals(1, stats.loadSuccessCount()); 116 assertEquals(0, stats.loadExceptionCount()); 117 assertEquals(0, stats.hitCount()); 118 119 key = new Object(); 120 assertSame(key, cache.getUnchecked(key)); 121 stats = cache.stats(); 122 assertEquals(2, stats.missCount()); 123 assertEquals(2, stats.loadSuccessCount()); 124 assertEquals(0, stats.loadExceptionCount()); 125 assertEquals(0, stats.hitCount()); 126 127 key = new Object(); 128 cache.refresh(key); 129 checkNothingLogged(); 130 stats = cache.stats(); 131 assertEquals(2, stats.missCount()); 132 assertEquals(3, stats.loadSuccessCount()); 133 assertEquals(0, stats.loadExceptionCount()); 134 assertEquals(0, stats.hitCount()); 135 136 assertSame(key, cache.get(key)); 137 stats = cache.stats(); 138 assertEquals(2, stats.missCount()); 139 assertEquals(3, stats.loadSuccessCount()); 140 assertEquals(0, stats.loadExceptionCount()); 141 assertEquals(1, stats.hitCount()); 142 143 Object value = new Object(); 144 // callable is not called 145 assertSame(key, cache.get(key, throwing(new Exception()))); 146 stats = cache.stats(); 147 assertEquals(2, stats.missCount()); 148 assertEquals(3, stats.loadSuccessCount()); 149 assertEquals(0, stats.loadExceptionCount()); 150 assertEquals(2, stats.hitCount()); 151 152 key = new Object(); 153 assertSame(value, cache.get(key, Callables.returning(value))); 154 stats = cache.stats(); 155 assertEquals(3, stats.missCount()); 156 assertEquals(4, stats.loadSuccessCount()); 157 assertEquals(0, stats.loadExceptionCount()); 158 assertEquals(2, stats.hitCount()); 159 } 160 testReload()161 public void testReload() throws ExecutionException { 162 final Object one = new Object(); 163 final Object two = new Object(); 164 CacheLoader<Object, Object> loader = 165 new CacheLoader<Object, Object>() { 166 @Override 167 public Object load(Object key) { 168 return one; 169 } 170 171 @Override 172 public ListenableFuture<Object> reload(Object key, Object oldValue) { 173 return Futures.immediateFuture(two); 174 } 175 }; 176 177 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 178 Object key = new Object(); 179 CacheStats stats = cache.stats(); 180 assertEquals(0, stats.missCount()); 181 assertEquals(0, stats.loadSuccessCount()); 182 assertEquals(0, stats.loadExceptionCount()); 183 assertEquals(0, stats.hitCount()); 184 185 assertSame(one, cache.getUnchecked(key)); 186 stats = cache.stats(); 187 assertEquals(1, stats.missCount()); 188 assertEquals(1, stats.loadSuccessCount()); 189 assertEquals(0, stats.loadExceptionCount()); 190 assertEquals(0, stats.hitCount()); 191 192 cache.refresh(key); 193 checkNothingLogged(); 194 stats = cache.stats(); 195 assertEquals(1, stats.missCount()); 196 assertEquals(2, stats.loadSuccessCount()); 197 assertEquals(0, stats.loadExceptionCount()); 198 assertEquals(0, stats.hitCount()); 199 200 assertSame(two, cache.getUnchecked(key)); 201 stats = cache.stats(); 202 assertEquals(1, stats.missCount()); 203 assertEquals(2, stats.loadSuccessCount()); 204 assertEquals(0, stats.loadExceptionCount()); 205 assertEquals(1, stats.hitCount()); 206 } 207 testRefresh()208 public void testRefresh() { 209 final Object one = new Object(); 210 final Object two = new Object(); 211 FakeTicker ticker = new FakeTicker(); 212 CacheLoader<Object, Object> loader = 213 new CacheLoader<Object, Object>() { 214 @Override 215 public Object load(Object key) { 216 return one; 217 } 218 219 @Override 220 public ListenableFuture<Object> reload(Object key, Object oldValue) { 221 return Futures.immediateFuture(two); 222 } 223 }; 224 225 LoadingCache<Object, Object> cache = 226 CacheBuilder.newBuilder() 227 .recordStats() 228 .ticker(ticker) 229 .refreshAfterWrite(1, MILLISECONDS) 230 .build(loader); 231 Object key = new Object(); 232 CacheStats stats = cache.stats(); 233 assertEquals(0, stats.missCount()); 234 assertEquals(0, stats.loadSuccessCount()); 235 assertEquals(0, stats.loadExceptionCount()); 236 assertEquals(0, stats.hitCount()); 237 238 assertSame(one, cache.getUnchecked(key)); 239 stats = cache.stats(); 240 assertEquals(1, stats.missCount()); 241 assertEquals(1, stats.loadSuccessCount()); 242 assertEquals(0, stats.loadExceptionCount()); 243 assertEquals(0, stats.hitCount()); 244 245 ticker.advance(1, MILLISECONDS); 246 assertSame(one, cache.getUnchecked(key)); 247 stats = cache.stats(); 248 assertEquals(1, stats.missCount()); 249 assertEquals(1, stats.loadSuccessCount()); 250 assertEquals(0, stats.loadExceptionCount()); 251 assertEquals(1, stats.hitCount()); 252 253 ticker.advance(1, MILLISECONDS); 254 assertSame(two, cache.getUnchecked(key)); 255 stats = cache.stats(); 256 assertEquals(1, stats.missCount()); 257 assertEquals(2, stats.loadSuccessCount()); 258 assertEquals(0, stats.loadExceptionCount()); 259 assertEquals(2, stats.hitCount()); 260 261 ticker.advance(1, MILLISECONDS); 262 assertSame(two, cache.getUnchecked(key)); 263 stats = cache.stats(); 264 assertEquals(1, stats.missCount()); 265 assertEquals(2, stats.loadSuccessCount()); 266 assertEquals(0, stats.loadExceptionCount()); 267 assertEquals(3, stats.hitCount()); 268 } 269 testRefresh_getIfPresent()270 public void testRefresh_getIfPresent() { 271 final Object one = new Object(); 272 final Object two = new Object(); 273 FakeTicker ticker = new FakeTicker(); 274 CacheLoader<Object, Object> loader = 275 new CacheLoader<Object, Object>() { 276 @Override 277 public Object load(Object key) { 278 return one; 279 } 280 281 @Override 282 public ListenableFuture<Object> reload(Object key, Object oldValue) { 283 return Futures.immediateFuture(two); 284 } 285 }; 286 287 LoadingCache<Object, Object> cache = 288 CacheBuilder.newBuilder() 289 .recordStats() 290 .ticker(ticker) 291 .refreshAfterWrite(1, MILLISECONDS) 292 .build(loader); 293 Object key = new Object(); 294 CacheStats stats = cache.stats(); 295 assertEquals(0, stats.missCount()); 296 assertEquals(0, stats.loadSuccessCount()); 297 assertEquals(0, stats.loadExceptionCount()); 298 assertEquals(0, stats.hitCount()); 299 300 assertSame(one, cache.getUnchecked(key)); 301 stats = cache.stats(); 302 assertEquals(1, stats.missCount()); 303 assertEquals(1, stats.loadSuccessCount()); 304 assertEquals(0, stats.loadExceptionCount()); 305 assertEquals(0, stats.hitCount()); 306 307 ticker.advance(1, MILLISECONDS); 308 assertSame(one, cache.getIfPresent(key)); 309 stats = cache.stats(); 310 assertEquals(1, stats.missCount()); 311 assertEquals(1, stats.loadSuccessCount()); 312 assertEquals(0, stats.loadExceptionCount()); 313 assertEquals(1, stats.hitCount()); 314 315 ticker.advance(1, MILLISECONDS); 316 assertSame(two, cache.getIfPresent(key)); 317 stats = cache.stats(); 318 assertEquals(1, stats.missCount()); 319 assertEquals(2, stats.loadSuccessCount()); 320 assertEquals(0, stats.loadExceptionCount()); 321 assertEquals(2, stats.hitCount()); 322 323 ticker.advance(1, MILLISECONDS); 324 assertSame(two, cache.getIfPresent(key)); 325 stats = cache.stats(); 326 assertEquals(1, stats.missCount()); 327 assertEquals(2, stats.loadSuccessCount()); 328 assertEquals(0, stats.loadExceptionCount()); 329 assertEquals(3, stats.hitCount()); 330 } 331 testBulkLoad_default()332 public void testBulkLoad_default() throws ExecutionException { 333 LoadingCache<Integer, Integer> cache = 334 CacheBuilder.newBuilder() 335 .recordStats() 336 .build(TestingCacheLoaders.<Integer>identityLoader()); 337 CacheStats stats = cache.stats(); 338 assertEquals(0, stats.missCount()); 339 assertEquals(0, stats.loadSuccessCount()); 340 assertEquals(0, stats.loadExceptionCount()); 341 assertEquals(0, stats.hitCount()); 342 343 assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of())); 344 assertEquals(0, stats.missCount()); 345 assertEquals(0, stats.loadSuccessCount()); 346 assertEquals(0, stats.loadExceptionCount()); 347 assertEquals(0, stats.hitCount()); 348 349 assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1))); 350 stats = cache.stats(); 351 assertEquals(1, stats.missCount()); 352 assertEquals(1, stats.loadSuccessCount()); 353 assertEquals(0, stats.loadExceptionCount()); 354 assertEquals(0, stats.hitCount()); 355 356 assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4))); 357 stats = cache.stats(); 358 assertEquals(4, stats.missCount()); 359 assertEquals(4, stats.loadSuccessCount()); 360 assertEquals(0, stats.loadExceptionCount()); 361 assertEquals(1, stats.hitCount()); 362 363 assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3))); 364 stats = cache.stats(); 365 assertEquals(4, stats.missCount()); 366 assertEquals(4, stats.loadSuccessCount()); 367 assertEquals(0, stats.loadExceptionCount()); 368 assertEquals(3, stats.hitCount()); 369 370 // duplicate keys are ignored, and don't impact stats 371 assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5))); 372 stats = cache.stats(); 373 assertEquals(5, stats.missCount()); 374 assertEquals(5, stats.loadSuccessCount()); 375 assertEquals(0, stats.loadExceptionCount()); 376 assertEquals(4, stats.hitCount()); 377 } 378 testBulkLoad_loadAll()379 public void testBulkLoad_loadAll() throws ExecutionException { 380 IdentityLoader<Integer> backingLoader = identityLoader(); 381 CacheLoader<Integer, Integer> loader = bulkLoader(backingLoader); 382 LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().recordStats().build(loader); 383 CacheStats stats = cache.stats(); 384 assertEquals(0, stats.missCount()); 385 assertEquals(0, stats.loadSuccessCount()); 386 assertEquals(0, stats.loadExceptionCount()); 387 assertEquals(0, stats.hitCount()); 388 389 assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of())); 390 assertEquals(0, stats.missCount()); 391 assertEquals(0, stats.loadSuccessCount()); 392 assertEquals(0, stats.loadExceptionCount()); 393 assertEquals(0, stats.hitCount()); 394 395 assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1))); 396 stats = cache.stats(); 397 assertEquals(1, stats.missCount()); 398 assertEquals(1, stats.loadSuccessCount()); 399 assertEquals(0, stats.loadExceptionCount()); 400 assertEquals(0, stats.hitCount()); 401 402 assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4))); 403 stats = cache.stats(); 404 assertEquals(4, stats.missCount()); 405 assertEquals(2, stats.loadSuccessCount()); 406 assertEquals(0, stats.loadExceptionCount()); 407 assertEquals(1, stats.hitCount()); 408 409 assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3))); 410 stats = cache.stats(); 411 assertEquals(4, stats.missCount()); 412 assertEquals(2, stats.loadSuccessCount()); 413 assertEquals(0, stats.loadExceptionCount()); 414 assertEquals(3, stats.hitCount()); 415 416 // duplicate keys are ignored, and don't impact stats 417 assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5))); 418 stats = cache.stats(); 419 assertEquals(5, stats.missCount()); 420 assertEquals(3, stats.loadSuccessCount()); 421 assertEquals(0, stats.loadExceptionCount()); 422 assertEquals(4, stats.hitCount()); 423 } 424 testBulkLoad_extra()425 public void testBulkLoad_extra() throws ExecutionException { 426 CacheLoader<Object, Object> loader = 427 new CacheLoader<Object, Object>() { 428 @Override 429 public Object load(Object key) throws Exception { 430 return new Object(); 431 } 432 433 @Override 434 public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception { 435 Map<Object, Object> result = Maps.newHashMap(); 436 for (Object key : keys) { 437 Object value = new Object(); 438 result.put(key, value); 439 // add extra entries 440 result.put(value, key); 441 } 442 return result; 443 } 444 }; 445 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader); 446 447 Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; 448 Map<Object, Object> result = cache.getAll(asList(lookupKeys)); 449 assertThat(result.keySet()).containsExactlyElementsIn(asList(lookupKeys)); 450 for (Entry<Object, Object> entry : result.entrySet()) { 451 Object key = entry.getKey(); 452 Object value = entry.getValue(); 453 assertSame(value, result.get(key)); 454 assertNull(result.get(value)); 455 assertSame(value, cache.asMap().get(key)); 456 assertSame(key, cache.asMap().get(value)); 457 } 458 } 459 testBulkLoad_clobber()460 public void testBulkLoad_clobber() throws ExecutionException { 461 final Object extraKey = new Object(); 462 final Object extraValue = new Object(); 463 CacheLoader<Object, Object> loader = 464 new CacheLoader<Object, Object>() { 465 @Override 466 public Object load(Object key) throws Exception { 467 throw new AssertionError(); 468 } 469 470 @Override 471 public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception { 472 Map<Object, Object> result = Maps.newHashMap(); 473 for (Object key : keys) { 474 Object value = new Object(); 475 result.put(key, value); 476 } 477 result.put(extraKey, extraValue); 478 return result; 479 } 480 }; 481 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader); 482 cache.asMap().put(extraKey, extraKey); 483 assertSame(extraKey, cache.asMap().get(extraKey)); 484 485 Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; 486 Map<Object, Object> result = cache.getAll(asList(lookupKeys)); 487 assertThat(result.keySet()).containsExactlyElementsIn(asList(lookupKeys)); 488 for (Entry<Object, Object> entry : result.entrySet()) { 489 Object key = entry.getKey(); 490 Object value = entry.getValue(); 491 assertSame(value, result.get(key)); 492 assertSame(value, cache.asMap().get(key)); 493 } 494 assertNull(result.get(extraKey)); 495 assertSame(extraValue, cache.asMap().get(extraKey)); 496 } 497 testBulkLoad_clobberNullValue()498 public void testBulkLoad_clobberNullValue() throws ExecutionException { 499 final Object extraKey = new Object(); 500 final Object extraValue = new Object(); 501 CacheLoader<Object, Object> loader = 502 new CacheLoader<Object, Object>() { 503 @Override 504 public Object load(Object key) throws Exception { 505 throw new AssertionError(); 506 } 507 508 @Override 509 public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception { 510 Map<Object, Object> result = Maps.newHashMap(); 511 for (Object key : keys) { 512 Object value = new Object(); 513 result.put(key, value); 514 } 515 result.put(extraKey, extraValue); 516 result.put(extraValue, null); 517 return result; 518 } 519 }; 520 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader); 521 cache.asMap().put(extraKey, extraKey); 522 assertSame(extraKey, cache.asMap().get(extraKey)); 523 524 Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; 525 assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys))); 526 527 for (Object key : lookupKeys) { 528 assertTrue(cache.asMap().containsKey(key)); 529 } 530 assertSame(extraValue, cache.asMap().get(extraKey)); 531 assertFalse(cache.asMap().containsKey(extraValue)); 532 } 533 testBulkLoad_clobberNullKey()534 public void testBulkLoad_clobberNullKey() throws ExecutionException { 535 final Object extraKey = new Object(); 536 final Object extraValue = new Object(); 537 CacheLoader<Object, Object> loader = 538 new CacheLoader<Object, Object>() { 539 @Override 540 public Object load(Object key) throws Exception { 541 throw new AssertionError(); 542 } 543 544 @Override 545 public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception { 546 Map<Object, Object> result = Maps.newHashMap(); 547 for (Object key : keys) { 548 Object value = new Object(); 549 result.put(key, value); 550 } 551 result.put(extraKey, extraValue); 552 result.put(null, extraKey); 553 return result; 554 } 555 }; 556 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader); 557 cache.asMap().put(extraKey, extraKey); 558 assertSame(extraKey, cache.asMap().get(extraKey)); 559 560 Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; 561 assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys))); 562 563 for (Object key : lookupKeys) { 564 assertTrue(cache.asMap().containsKey(key)); 565 } 566 assertSame(extraValue, cache.asMap().get(extraKey)); 567 assertFalse(cache.asMap().containsValue(extraKey)); 568 } 569 testBulkLoad_partial()570 public void testBulkLoad_partial() throws ExecutionException { 571 final Object extraKey = new Object(); 572 final Object extraValue = new Object(); 573 CacheLoader<Object, Object> loader = 574 new CacheLoader<Object, Object>() { 575 @Override 576 public Object load(Object key) throws Exception { 577 throw new AssertionError(); 578 } 579 580 @Override 581 public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception { 582 Map<Object, Object> result = Maps.newHashMap(); 583 // ignore request keys 584 result.put(extraKey, extraValue); 585 return result; 586 } 587 }; 588 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader); 589 590 Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()}; 591 assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys))); 592 assertSame(extraValue, cache.asMap().get(extraKey)); 593 } 594 testLoadNull()595 public void testLoadNull() throws ExecutionException { 596 LoadingCache<Object, Object> cache = 597 CacheBuilder.newBuilder().recordStats().build(constantLoader(null)); 598 CacheStats stats = cache.stats(); 599 assertEquals(0, stats.missCount()); 600 assertEquals(0, stats.loadSuccessCount()); 601 assertEquals(0, stats.loadExceptionCount()); 602 assertEquals(0, stats.hitCount()); 603 604 assertThrows(InvalidCacheLoadException.class, () -> cache.get(new Object())); 605 stats = cache.stats(); 606 assertEquals(1, stats.missCount()); 607 assertEquals(0, stats.loadSuccessCount()); 608 assertEquals(1, stats.loadExceptionCount()); 609 assertEquals(0, stats.hitCount()); 610 611 assertThrows(InvalidCacheLoadException.class, () -> cache.getUnchecked(new Object())); 612 stats = cache.stats(); 613 assertEquals(2, stats.missCount()); 614 assertEquals(0, stats.loadSuccessCount()); 615 assertEquals(2, stats.loadExceptionCount()); 616 assertEquals(0, stats.hitCount()); 617 618 cache.refresh(new Object()); 619 checkLoggedInvalidLoad(); 620 stats = cache.stats(); 621 assertEquals(2, stats.missCount()); 622 assertEquals(0, stats.loadSuccessCount()); 623 assertEquals(3, stats.loadExceptionCount()); 624 assertEquals(0, stats.hitCount()); 625 626 assertThrows( 627 InvalidCacheLoadException.class, () -> cache.get(new Object(), Callables.returning(null))); 628 stats = cache.stats(); 629 assertEquals(3, stats.missCount()); 630 assertEquals(0, stats.loadSuccessCount()); 631 assertEquals(4, stats.loadExceptionCount()); 632 assertEquals(0, stats.hitCount()); 633 634 assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object()))); 635 stats = cache.stats(); 636 assertEquals(4, stats.missCount()); 637 assertEquals(0, stats.loadSuccessCount()); 638 assertEquals(5, stats.loadExceptionCount()); 639 assertEquals(0, stats.hitCount()); 640 } 641 testReloadNull()642 public void testReloadNull() throws ExecutionException { 643 final Object one = new Object(); 644 CacheLoader<Object, Object> loader = 645 new CacheLoader<Object, Object>() { 646 @Override 647 public Object load(Object key) { 648 return one; 649 } 650 651 @Override 652 public ListenableFuture<Object> reload(Object key, Object oldValue) { 653 return null; 654 } 655 }; 656 657 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 658 Object key = new Object(); 659 CacheStats stats = cache.stats(); 660 assertEquals(0, stats.missCount()); 661 assertEquals(0, stats.loadSuccessCount()); 662 assertEquals(0, stats.loadExceptionCount()); 663 assertEquals(0, stats.hitCount()); 664 665 assertSame(one, cache.getUnchecked(key)); 666 stats = cache.stats(); 667 assertEquals(1, stats.missCount()); 668 assertEquals(1, stats.loadSuccessCount()); 669 assertEquals(0, stats.loadExceptionCount()); 670 assertEquals(0, stats.hitCount()); 671 672 cache.refresh(key); 673 checkLoggedInvalidLoad(); 674 stats = cache.stats(); 675 assertEquals(1, stats.missCount()); 676 assertEquals(1, stats.loadSuccessCount()); 677 assertEquals(1, stats.loadExceptionCount()); 678 assertEquals(0, stats.hitCount()); 679 680 assertSame(one, cache.getUnchecked(key)); 681 stats = cache.stats(); 682 assertEquals(1, stats.missCount()); 683 assertEquals(1, stats.loadSuccessCount()); 684 assertEquals(1, stats.loadExceptionCount()); 685 assertEquals(1, stats.hitCount()); 686 } 687 testReloadNullFuture()688 public void testReloadNullFuture() throws ExecutionException { 689 final Object one = new Object(); 690 CacheLoader<Object, Object> loader = 691 new CacheLoader<Object, Object>() { 692 @Override 693 public Object load(Object key) { 694 return one; 695 } 696 697 @Override 698 public ListenableFuture<Object> reload(Object key, Object oldValue) { 699 return Futures.immediateFuture(null); 700 } 701 }; 702 703 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 704 Object key = new Object(); 705 CacheStats stats = cache.stats(); 706 assertEquals(0, stats.missCount()); 707 assertEquals(0, stats.loadSuccessCount()); 708 assertEquals(0, stats.loadExceptionCount()); 709 assertEquals(0, stats.hitCount()); 710 711 assertSame(one, cache.getUnchecked(key)); 712 stats = cache.stats(); 713 assertEquals(1, stats.missCount()); 714 assertEquals(1, stats.loadSuccessCount()); 715 assertEquals(0, stats.loadExceptionCount()); 716 assertEquals(0, stats.hitCount()); 717 718 cache.refresh(key); 719 checkLoggedInvalidLoad(); 720 stats = cache.stats(); 721 assertEquals(1, stats.missCount()); 722 assertEquals(1, stats.loadSuccessCount()); 723 assertEquals(1, stats.loadExceptionCount()); 724 assertEquals(0, stats.hitCount()); 725 726 assertSame(one, cache.getUnchecked(key)); 727 stats = cache.stats(); 728 assertEquals(1, stats.missCount()); 729 assertEquals(1, stats.loadSuccessCount()); 730 assertEquals(1, stats.loadExceptionCount()); 731 assertEquals(1, stats.hitCount()); 732 } 733 testRefreshNull()734 public void testRefreshNull() { 735 final Object one = new Object(); 736 FakeTicker ticker = new FakeTicker(); 737 CacheLoader<Object, Object> loader = 738 new CacheLoader<Object, Object>() { 739 @Override 740 public Object load(Object key) { 741 return one; 742 } 743 744 @Override 745 public ListenableFuture<Object> reload(Object key, Object oldValue) { 746 return Futures.immediateFuture(null); 747 } 748 }; 749 750 LoadingCache<Object, Object> cache = 751 CacheBuilder.newBuilder() 752 .recordStats() 753 .ticker(ticker) 754 .refreshAfterWrite(1, MILLISECONDS) 755 .build(loader); 756 Object key = new Object(); 757 CacheStats stats = cache.stats(); 758 assertEquals(0, stats.missCount()); 759 assertEquals(0, stats.loadSuccessCount()); 760 assertEquals(0, stats.loadExceptionCount()); 761 assertEquals(0, stats.hitCount()); 762 763 assertSame(one, cache.getUnchecked(key)); 764 stats = cache.stats(); 765 assertEquals(1, stats.missCount()); 766 assertEquals(1, stats.loadSuccessCount()); 767 assertEquals(0, stats.loadExceptionCount()); 768 assertEquals(0, stats.hitCount()); 769 770 ticker.advance(1, MILLISECONDS); 771 assertSame(one, cache.getUnchecked(key)); 772 stats = cache.stats(); 773 assertEquals(1, stats.missCount()); 774 assertEquals(1, stats.loadSuccessCount()); 775 assertEquals(0, stats.loadExceptionCount()); 776 assertEquals(1, stats.hitCount()); 777 778 ticker.advance(1, MILLISECONDS); 779 assertSame(one, cache.getUnchecked(key)); 780 // refreshed 781 stats = cache.stats(); 782 assertEquals(1, stats.missCount()); 783 assertEquals(1, stats.loadSuccessCount()); 784 assertEquals(1, stats.loadExceptionCount()); 785 assertEquals(2, stats.hitCount()); 786 787 ticker.advance(1, MILLISECONDS); 788 assertSame(one, cache.getUnchecked(key)); 789 stats = cache.stats(); 790 assertEquals(1, stats.missCount()); 791 assertEquals(1, stats.loadSuccessCount()); 792 assertEquals(2, stats.loadExceptionCount()); 793 assertEquals(3, stats.hitCount()); 794 } 795 testBulkLoadNull()796 public void testBulkLoadNull() throws ExecutionException { 797 LoadingCache<Object, Object> cache = 798 CacheBuilder.newBuilder().recordStats().build(bulkLoader(constantLoader(null))); 799 CacheStats stats = cache.stats(); 800 assertEquals(0, stats.missCount()); 801 assertEquals(0, stats.loadSuccessCount()); 802 assertEquals(0, stats.loadExceptionCount()); 803 assertEquals(0, stats.hitCount()); 804 805 assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object()))); 806 stats = cache.stats(); 807 assertEquals(1, stats.missCount()); 808 assertEquals(0, stats.loadSuccessCount()); 809 assertEquals(1, stats.loadExceptionCount()); 810 assertEquals(0, stats.hitCount()); 811 } 812 testBulkLoadNullMap()813 public void testBulkLoadNullMap() throws ExecutionException { 814 LoadingCache<Object, Object> cache = 815 CacheBuilder.newBuilder() 816 .recordStats() 817 .build( 818 new CacheLoader<Object, Object>() { 819 @Override 820 public Object load(Object key) { 821 throw new AssertionError(); 822 } 823 824 @Override 825 public Map<Object, Object> loadAll(Iterable<?> keys) { 826 return null; 827 } 828 }); 829 830 CacheStats stats = cache.stats(); 831 assertEquals(0, stats.missCount()); 832 assertEquals(0, stats.loadSuccessCount()); 833 assertEquals(0, stats.loadExceptionCount()); 834 assertEquals(0, stats.hitCount()); 835 836 assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object()))); 837 stats = cache.stats(); 838 assertEquals(1, stats.missCount()); 839 assertEquals(0, stats.loadSuccessCount()); 840 assertEquals(1, stats.loadExceptionCount()); 841 assertEquals(0, stats.hitCount()); 842 } 843 testLoadError()844 public void testLoadError() throws ExecutionException { 845 Error e = new Error(); 846 CacheLoader<Object, Object> loader = errorLoader(e); 847 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 848 CacheStats stats = cache.stats(); 849 assertEquals(0, stats.missCount()); 850 assertEquals(0, stats.loadSuccessCount()); 851 assertEquals(0, stats.loadExceptionCount()); 852 assertEquals(0, stats.hitCount()); 853 854 ExecutionError expected = assertThrows(ExecutionError.class, () -> cache.get(new Object())); 855 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 856 stats = cache.stats(); 857 assertEquals(1, stats.missCount()); 858 assertEquals(0, stats.loadSuccessCount()); 859 assertEquals(1, stats.loadExceptionCount()); 860 assertEquals(0, stats.hitCount()); 861 862 expected = assertThrows(ExecutionError.class, () -> cache.getUnchecked(new Object())); 863 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 864 stats = cache.stats(); 865 assertEquals(2, stats.missCount()); 866 assertEquals(0, stats.loadSuccessCount()); 867 assertEquals(2, stats.loadExceptionCount()); 868 assertEquals(0, stats.hitCount()); 869 870 cache.refresh(new Object()); 871 checkLoggedCause(e); 872 stats = cache.stats(); 873 assertEquals(2, stats.missCount()); 874 assertEquals(0, stats.loadSuccessCount()); 875 assertEquals(3, stats.loadExceptionCount()); 876 assertEquals(0, stats.hitCount()); 877 878 final Error callableError = new Error(); 879 expected = 880 assertThrows( 881 ExecutionError.class, 882 () -> 883 cache.get( 884 new Object(), 885 new Callable<Object>() { 886 @Override 887 public Object call() { 888 throw callableError; 889 } 890 })); 891 assertThat(expected).hasCauseThat().isSameInstanceAs(callableError); 892 stats = cache.stats(); 893 assertEquals(3, stats.missCount()); 894 assertEquals(0, stats.loadSuccessCount()); 895 assertEquals(4, stats.loadExceptionCount()); 896 assertEquals(0, stats.hitCount()); 897 898 expected = assertThrows(ExecutionError.class, () -> cache.getAll(asList(new Object()))); 899 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 900 stats = cache.stats(); 901 assertEquals(4, stats.missCount()); 902 assertEquals(0, stats.loadSuccessCount()); 903 assertEquals(5, stats.loadExceptionCount()); 904 assertEquals(0, stats.hitCount()); 905 } 906 testReloadError()907 public void testReloadError() throws ExecutionException { 908 final Object one = new Object(); 909 final Error e = new Error(); 910 CacheLoader<Object, Object> loader = 911 new CacheLoader<Object, Object>() { 912 @Override 913 public Object load(Object key) { 914 return one; 915 } 916 917 @Override 918 public ListenableFuture<Object> reload(Object key, Object oldValue) { 919 throw e; 920 } 921 }; 922 923 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 924 Object key = new Object(); 925 CacheStats stats = cache.stats(); 926 assertEquals(0, stats.missCount()); 927 assertEquals(0, stats.loadSuccessCount()); 928 assertEquals(0, stats.loadExceptionCount()); 929 assertEquals(0, stats.hitCount()); 930 931 assertSame(one, cache.getUnchecked(key)); 932 stats = cache.stats(); 933 assertEquals(1, stats.missCount()); 934 assertEquals(1, stats.loadSuccessCount()); 935 assertEquals(0, stats.loadExceptionCount()); 936 assertEquals(0, stats.hitCount()); 937 938 cache.refresh(key); 939 checkLoggedCause(e); 940 stats = cache.stats(); 941 assertEquals(1, stats.missCount()); 942 assertEquals(1, stats.loadSuccessCount()); 943 assertEquals(1, stats.loadExceptionCount()); 944 assertEquals(0, stats.hitCount()); 945 946 assertSame(one, cache.getUnchecked(key)); 947 stats = cache.stats(); 948 assertEquals(1, stats.missCount()); 949 assertEquals(1, stats.loadSuccessCount()); 950 assertEquals(1, stats.loadExceptionCount()); 951 assertEquals(1, stats.hitCount()); 952 } 953 testReloadFutureError()954 public void testReloadFutureError() throws ExecutionException { 955 final Object one = new Object(); 956 final Error e = new Error(); 957 CacheLoader<Object, Object> loader = 958 new CacheLoader<Object, Object>() { 959 @Override 960 public Object load(Object key) { 961 return one; 962 } 963 964 @Override 965 public ListenableFuture<Object> reload(Object key, Object oldValue) { 966 return Futures.immediateFailedFuture(e); 967 } 968 }; 969 970 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 971 Object key = new Object(); 972 CacheStats stats = cache.stats(); 973 assertEquals(0, stats.missCount()); 974 assertEquals(0, stats.loadSuccessCount()); 975 assertEquals(0, stats.loadExceptionCount()); 976 assertEquals(0, stats.hitCount()); 977 978 assertSame(one, cache.getUnchecked(key)); 979 stats = cache.stats(); 980 assertEquals(1, stats.missCount()); 981 assertEquals(1, stats.loadSuccessCount()); 982 assertEquals(0, stats.loadExceptionCount()); 983 assertEquals(0, stats.hitCount()); 984 985 cache.refresh(key); 986 checkLoggedCause(e); 987 stats = cache.stats(); 988 assertEquals(1, stats.missCount()); 989 assertEquals(1, stats.loadSuccessCount()); 990 assertEquals(1, stats.loadExceptionCount()); 991 assertEquals(0, stats.hitCount()); 992 993 assertSame(one, cache.getUnchecked(key)); 994 stats = cache.stats(); 995 assertEquals(1, stats.missCount()); 996 assertEquals(1, stats.loadSuccessCount()); 997 assertEquals(1, stats.loadExceptionCount()); 998 assertEquals(1, stats.hitCount()); 999 } 1000 testRefreshError()1001 public void testRefreshError() { 1002 final Object one = new Object(); 1003 final Error e = new Error(); 1004 FakeTicker ticker = new FakeTicker(); 1005 CacheLoader<Object, Object> loader = 1006 new CacheLoader<Object, Object>() { 1007 @Override 1008 public Object load(Object key) { 1009 return one; 1010 } 1011 1012 @Override 1013 public ListenableFuture<Object> reload(Object key, Object oldValue) { 1014 return Futures.immediateFailedFuture(e); 1015 } 1016 }; 1017 1018 LoadingCache<Object, Object> cache = 1019 CacheBuilder.newBuilder() 1020 .recordStats() 1021 .ticker(ticker) 1022 .refreshAfterWrite(1, MILLISECONDS) 1023 .build(loader); 1024 Object key = new Object(); 1025 CacheStats stats = cache.stats(); 1026 assertEquals(0, stats.missCount()); 1027 assertEquals(0, stats.loadSuccessCount()); 1028 assertEquals(0, stats.loadExceptionCount()); 1029 assertEquals(0, stats.hitCount()); 1030 1031 assertSame(one, cache.getUnchecked(key)); 1032 stats = cache.stats(); 1033 assertEquals(1, stats.missCount()); 1034 assertEquals(1, stats.loadSuccessCount()); 1035 assertEquals(0, stats.loadExceptionCount()); 1036 assertEquals(0, stats.hitCount()); 1037 1038 ticker.advance(1, MILLISECONDS); 1039 assertSame(one, cache.getUnchecked(key)); 1040 stats = cache.stats(); 1041 assertEquals(1, stats.missCount()); 1042 assertEquals(1, stats.loadSuccessCount()); 1043 assertEquals(0, stats.loadExceptionCount()); 1044 assertEquals(1, stats.hitCount()); 1045 1046 ticker.advance(1, MILLISECONDS); 1047 assertSame(one, cache.getUnchecked(key)); 1048 // refreshed 1049 stats = cache.stats(); 1050 assertEquals(1, stats.missCount()); 1051 assertEquals(1, stats.loadSuccessCount()); 1052 assertEquals(1, stats.loadExceptionCount()); 1053 assertEquals(2, stats.hitCount()); 1054 1055 ticker.advance(1, MILLISECONDS); 1056 assertSame(one, cache.getUnchecked(key)); 1057 stats = cache.stats(); 1058 assertEquals(1, stats.missCount()); 1059 assertEquals(1, stats.loadSuccessCount()); 1060 assertEquals(2, stats.loadExceptionCount()); 1061 assertEquals(3, stats.hitCount()); 1062 } 1063 testBulkLoadError()1064 public void testBulkLoadError() throws ExecutionException { 1065 Error e = new Error(); 1066 CacheLoader<Object, Object> loader = errorLoader(e); 1067 LoadingCache<Object, Object> cache = 1068 CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader)); 1069 CacheStats stats = cache.stats(); 1070 assertEquals(0, stats.missCount()); 1071 assertEquals(0, stats.loadSuccessCount()); 1072 assertEquals(0, stats.loadExceptionCount()); 1073 assertEquals(0, stats.hitCount()); 1074 1075 ExecutionError expected = 1076 assertThrows(ExecutionError.class, () -> cache.getAll(asList(new Object()))); 1077 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1078 stats = cache.stats(); 1079 assertEquals(1, stats.missCount()); 1080 assertEquals(0, stats.loadSuccessCount()); 1081 assertEquals(1, stats.loadExceptionCount()); 1082 assertEquals(0, stats.hitCount()); 1083 } 1084 testLoadCheckedException()1085 public void testLoadCheckedException() { 1086 Exception e = new Exception(); 1087 CacheLoader<Object, Object> loader = exceptionLoader(e); 1088 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 1089 CacheStats stats = cache.stats(); 1090 assertEquals(0, stats.missCount()); 1091 assertEquals(0, stats.loadSuccessCount()); 1092 assertEquals(0, stats.loadExceptionCount()); 1093 assertEquals(0, stats.hitCount()); 1094 1095 Exception expected = assertThrows(ExecutionException.class, () -> cache.get(new Object())); 1096 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1097 stats = cache.stats(); 1098 assertEquals(1, stats.missCount()); 1099 assertEquals(0, stats.loadSuccessCount()); 1100 assertEquals(1, stats.loadExceptionCount()); 1101 assertEquals(0, stats.hitCount()); 1102 1103 expected = 1104 assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); 1105 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1106 stats = cache.stats(); 1107 assertEquals(2, stats.missCount()); 1108 assertEquals(0, stats.loadSuccessCount()); 1109 assertEquals(2, stats.loadExceptionCount()); 1110 assertEquals(0, stats.hitCount()); 1111 1112 cache.refresh(new Object()); 1113 checkLoggedCause(e); 1114 stats = cache.stats(); 1115 assertEquals(2, stats.missCount()); 1116 assertEquals(0, stats.loadSuccessCount()); 1117 assertEquals(3, stats.loadExceptionCount()); 1118 assertEquals(0, stats.hitCount()); 1119 1120 Exception callableException = new Exception(); 1121 expected = 1122 assertThrows( 1123 ExecutionException.class, () -> cache.get(new Object(), throwing(callableException))); 1124 assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); 1125 stats = cache.stats(); 1126 assertEquals(3, stats.missCount()); 1127 assertEquals(0, stats.loadSuccessCount()); 1128 assertEquals(4, stats.loadExceptionCount()); 1129 assertEquals(0, stats.hitCount()); 1130 1131 expected = assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); 1132 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1133 stats = cache.stats(); 1134 assertEquals(4, stats.missCount()); 1135 assertEquals(0, stats.loadSuccessCount()); 1136 assertEquals(5, stats.loadExceptionCount()); 1137 assertEquals(0, stats.hitCount()); 1138 } 1139 testLoadInterruptedException()1140 public void testLoadInterruptedException() { 1141 Exception e = new InterruptedException(); 1142 CacheLoader<Object, Object> loader = exceptionLoader(e); 1143 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 1144 CacheStats stats = cache.stats(); 1145 assertEquals(0, stats.missCount()); 1146 assertEquals(0, stats.loadSuccessCount()); 1147 assertEquals(0, stats.loadExceptionCount()); 1148 assertEquals(0, stats.hitCount()); 1149 1150 // Sanity check: 1151 assertFalse(currentThread().interrupted()); 1152 1153 Exception expected = assertThrows(ExecutionException.class, () -> cache.get(new Object())); 1154 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1155 assertTrue(currentThread().interrupted()); 1156 stats = cache.stats(); 1157 assertEquals(1, stats.missCount()); 1158 assertEquals(0, stats.loadSuccessCount()); 1159 assertEquals(1, stats.loadExceptionCount()); 1160 assertEquals(0, stats.hitCount()); 1161 1162 expected = 1163 assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); 1164 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1165 assertTrue(currentThread().interrupted()); 1166 stats = cache.stats(); 1167 assertEquals(2, stats.missCount()); 1168 assertEquals(0, stats.loadSuccessCount()); 1169 assertEquals(2, stats.loadExceptionCount()); 1170 assertEquals(0, stats.hitCount()); 1171 1172 cache.refresh(new Object()); 1173 assertTrue(currentThread().interrupted()); 1174 checkLoggedCause(e); 1175 stats = cache.stats(); 1176 assertEquals(2, stats.missCount()); 1177 assertEquals(0, stats.loadSuccessCount()); 1178 assertEquals(3, stats.loadExceptionCount()); 1179 assertEquals(0, stats.hitCount()); 1180 1181 Exception callableException = new InterruptedException(); 1182 expected = 1183 assertThrows( 1184 ExecutionException.class, () -> cache.get(new Object(), throwing(callableException))); 1185 assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); 1186 assertTrue(currentThread().interrupted()); 1187 stats = cache.stats(); 1188 assertEquals(3, stats.missCount()); 1189 assertEquals(0, stats.loadSuccessCount()); 1190 assertEquals(4, stats.loadExceptionCount()); 1191 assertEquals(0, stats.hitCount()); 1192 1193 expected = assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); 1194 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1195 assertTrue(currentThread().interrupted()); 1196 stats = cache.stats(); 1197 assertEquals(4, stats.missCount()); 1198 assertEquals(0, stats.loadSuccessCount()); 1199 assertEquals(5, stats.loadExceptionCount()); 1200 assertEquals(0, stats.hitCount()); 1201 } 1202 testReloadCheckedException()1203 public void testReloadCheckedException() { 1204 final Object one = new Object(); 1205 final Exception e = new Exception(); 1206 CacheLoader<Object, Object> loader = 1207 new CacheLoader<Object, Object>() { 1208 @Override 1209 public Object load(Object key) { 1210 return one; 1211 } 1212 1213 @Override 1214 public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception { 1215 throw e; 1216 } 1217 }; 1218 1219 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 1220 Object key = new Object(); 1221 CacheStats stats = cache.stats(); 1222 assertEquals(0, stats.missCount()); 1223 assertEquals(0, stats.loadSuccessCount()); 1224 assertEquals(0, stats.loadExceptionCount()); 1225 assertEquals(0, stats.hitCount()); 1226 1227 assertSame(one, cache.getUnchecked(key)); 1228 stats = cache.stats(); 1229 assertEquals(1, stats.missCount()); 1230 assertEquals(1, stats.loadSuccessCount()); 1231 assertEquals(0, stats.loadExceptionCount()); 1232 assertEquals(0, stats.hitCount()); 1233 1234 cache.refresh(key); 1235 checkLoggedCause(e); 1236 stats = cache.stats(); 1237 assertEquals(1, stats.missCount()); 1238 assertEquals(1, stats.loadSuccessCount()); 1239 assertEquals(1, stats.loadExceptionCount()); 1240 assertEquals(0, stats.hitCount()); 1241 1242 assertSame(one, cache.getUnchecked(key)); 1243 stats = cache.stats(); 1244 assertEquals(1, stats.missCount()); 1245 assertEquals(1, stats.loadSuccessCount()); 1246 assertEquals(1, stats.loadExceptionCount()); 1247 assertEquals(1, stats.hitCount()); 1248 } 1249 testReloadFutureCheckedException()1250 public void testReloadFutureCheckedException() { 1251 final Object one = new Object(); 1252 final Exception e = new Exception(); 1253 CacheLoader<Object, Object> loader = 1254 new CacheLoader<Object, Object>() { 1255 @Override 1256 public Object load(Object key) { 1257 return one; 1258 } 1259 1260 @Override 1261 public ListenableFuture<Object> reload(Object key, Object oldValue) { 1262 return Futures.immediateFailedFuture(e); 1263 } 1264 }; 1265 1266 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 1267 Object key = new Object(); 1268 CacheStats stats = cache.stats(); 1269 assertEquals(0, stats.missCount()); 1270 assertEquals(0, stats.loadSuccessCount()); 1271 assertEquals(0, stats.loadExceptionCount()); 1272 assertEquals(0, stats.hitCount()); 1273 1274 assertSame(one, cache.getUnchecked(key)); 1275 stats = cache.stats(); 1276 assertEquals(1, stats.missCount()); 1277 assertEquals(1, stats.loadSuccessCount()); 1278 assertEquals(0, stats.loadExceptionCount()); 1279 assertEquals(0, stats.hitCount()); 1280 1281 cache.refresh(key); 1282 checkLoggedCause(e); 1283 stats = cache.stats(); 1284 assertEquals(1, stats.missCount()); 1285 assertEquals(1, stats.loadSuccessCount()); 1286 assertEquals(1, stats.loadExceptionCount()); 1287 assertEquals(0, stats.hitCount()); 1288 1289 assertSame(one, cache.getUnchecked(key)); 1290 stats = cache.stats(); 1291 assertEquals(1, stats.missCount()); 1292 assertEquals(1, stats.loadSuccessCount()); 1293 assertEquals(1, stats.loadExceptionCount()); 1294 assertEquals(1, stats.hitCount()); 1295 } 1296 testRefreshCheckedException()1297 public void testRefreshCheckedException() { 1298 final Object one = new Object(); 1299 final Exception e = new Exception(); 1300 FakeTicker ticker = new FakeTicker(); 1301 CacheLoader<Object, Object> loader = 1302 new CacheLoader<Object, Object>() { 1303 @Override 1304 public Object load(Object key) { 1305 return one; 1306 } 1307 1308 @Override 1309 public ListenableFuture<Object> reload(Object key, Object oldValue) { 1310 return Futures.immediateFailedFuture(e); 1311 } 1312 }; 1313 1314 LoadingCache<Object, Object> cache = 1315 CacheBuilder.newBuilder() 1316 .recordStats() 1317 .ticker(ticker) 1318 .refreshAfterWrite(1, MILLISECONDS) 1319 .build(loader); 1320 Object key = new Object(); 1321 CacheStats stats = cache.stats(); 1322 assertEquals(0, stats.missCount()); 1323 assertEquals(0, stats.loadSuccessCount()); 1324 assertEquals(0, stats.loadExceptionCount()); 1325 assertEquals(0, stats.hitCount()); 1326 1327 assertSame(one, cache.getUnchecked(key)); 1328 stats = cache.stats(); 1329 assertEquals(1, stats.missCount()); 1330 assertEquals(1, stats.loadSuccessCount()); 1331 assertEquals(0, stats.loadExceptionCount()); 1332 assertEquals(0, stats.hitCount()); 1333 1334 ticker.advance(1, MILLISECONDS); 1335 assertSame(one, cache.getUnchecked(key)); 1336 stats = cache.stats(); 1337 assertEquals(1, stats.missCount()); 1338 assertEquals(1, stats.loadSuccessCount()); 1339 assertEquals(0, stats.loadExceptionCount()); 1340 assertEquals(1, stats.hitCount()); 1341 1342 ticker.advance(1, MILLISECONDS); 1343 assertSame(one, cache.getUnchecked(key)); 1344 // refreshed 1345 stats = cache.stats(); 1346 assertEquals(1, stats.missCount()); 1347 assertEquals(1, stats.loadSuccessCount()); 1348 assertEquals(1, stats.loadExceptionCount()); 1349 assertEquals(2, stats.hitCount()); 1350 1351 ticker.advance(1, MILLISECONDS); 1352 assertSame(one, cache.getUnchecked(key)); 1353 stats = cache.stats(); 1354 assertEquals(1, stats.missCount()); 1355 assertEquals(1, stats.loadSuccessCount()); 1356 assertEquals(2, stats.loadExceptionCount()); 1357 assertEquals(3, stats.hitCount()); 1358 } 1359 testBulkLoadCheckedException()1360 public void testBulkLoadCheckedException() { 1361 Exception e = new Exception(); 1362 CacheLoader<Object, Object> loader = exceptionLoader(e); 1363 LoadingCache<Object, Object> cache = 1364 CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader)); 1365 CacheStats stats = cache.stats(); 1366 assertEquals(0, stats.missCount()); 1367 assertEquals(0, stats.loadSuccessCount()); 1368 assertEquals(0, stats.loadExceptionCount()); 1369 assertEquals(0, stats.hitCount()); 1370 1371 ExecutionException expected = 1372 assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); 1373 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1374 stats = cache.stats(); 1375 assertEquals(1, stats.missCount()); 1376 assertEquals(0, stats.loadSuccessCount()); 1377 assertEquals(1, stats.loadExceptionCount()); 1378 assertEquals(0, stats.hitCount()); 1379 } 1380 testBulkLoadInterruptedException()1381 public void testBulkLoadInterruptedException() { 1382 Exception e = new InterruptedException(); 1383 CacheLoader<Object, Object> loader = exceptionLoader(e); 1384 LoadingCache<Object, Object> cache = 1385 CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader)); 1386 CacheStats stats = cache.stats(); 1387 assertEquals(0, stats.missCount()); 1388 assertEquals(0, stats.loadSuccessCount()); 1389 assertEquals(0, stats.loadExceptionCount()); 1390 assertEquals(0, stats.hitCount()); 1391 1392 ExecutionException expected = 1393 assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object()))); 1394 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1395 assertTrue(currentThread().interrupted()); 1396 stats = cache.stats(); 1397 assertEquals(1, stats.missCount()); 1398 assertEquals(0, stats.loadSuccessCount()); 1399 assertEquals(1, stats.loadExceptionCount()); 1400 assertEquals(0, stats.hitCount()); 1401 } 1402 testLoadUncheckedException()1403 public void testLoadUncheckedException() throws ExecutionException { 1404 Exception e = new RuntimeException(); 1405 CacheLoader<Object, Object> loader = exceptionLoader(e); 1406 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 1407 CacheStats stats = cache.stats(); 1408 assertEquals(0, stats.missCount()); 1409 assertEquals(0, stats.loadSuccessCount()); 1410 assertEquals(0, stats.loadExceptionCount()); 1411 assertEquals(0, stats.hitCount()); 1412 1413 UncheckedExecutionException expected = 1414 assertThrows(UncheckedExecutionException.class, () -> cache.get(new Object())); 1415 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1416 stats = cache.stats(); 1417 assertEquals(1, stats.missCount()); 1418 assertEquals(0, stats.loadSuccessCount()); 1419 assertEquals(1, stats.loadExceptionCount()); 1420 assertEquals(0, stats.hitCount()); 1421 1422 expected = 1423 assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object())); 1424 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1425 stats = cache.stats(); 1426 assertEquals(2, stats.missCount()); 1427 assertEquals(0, stats.loadSuccessCount()); 1428 assertEquals(2, stats.loadExceptionCount()); 1429 assertEquals(0, stats.hitCount()); 1430 1431 cache.refresh(new Object()); 1432 checkLoggedCause(e); 1433 stats = cache.stats(); 1434 assertEquals(2, stats.missCount()); 1435 assertEquals(0, stats.loadSuccessCount()); 1436 assertEquals(3, stats.loadExceptionCount()); 1437 assertEquals(0, stats.hitCount()); 1438 1439 Exception callableException = new RuntimeException(); 1440 expected = 1441 assertThrows( 1442 UncheckedExecutionException.class, 1443 () -> cache.get(new Object(), throwing(callableException))); 1444 assertThat(expected).hasCauseThat().isSameInstanceAs(callableException); 1445 stats = cache.stats(); 1446 assertEquals(3, stats.missCount()); 1447 assertEquals(0, stats.loadSuccessCount()); 1448 assertEquals(4, stats.loadExceptionCount()); 1449 assertEquals(0, stats.hitCount()); 1450 1451 expected = 1452 assertThrows(UncheckedExecutionException.class, () -> cache.getAll(asList(new Object()))); 1453 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1454 stats = cache.stats(); 1455 assertEquals(4, stats.missCount()); 1456 assertEquals(0, stats.loadSuccessCount()); 1457 assertEquals(5, stats.loadExceptionCount()); 1458 assertEquals(0, stats.hitCount()); 1459 } 1460 testReloadUncheckedException()1461 public void testReloadUncheckedException() throws ExecutionException { 1462 final Object one = new Object(); 1463 final Exception e = new RuntimeException(); 1464 CacheLoader<Object, Object> loader = 1465 new CacheLoader<Object, Object>() { 1466 @Override 1467 public Object load(Object key) { 1468 return one; 1469 } 1470 1471 @Override 1472 public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception { 1473 throw e; 1474 } 1475 }; 1476 1477 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 1478 Object key = new Object(); 1479 CacheStats stats = cache.stats(); 1480 assertEquals(0, stats.missCount()); 1481 assertEquals(0, stats.loadSuccessCount()); 1482 assertEquals(0, stats.loadExceptionCount()); 1483 assertEquals(0, stats.hitCount()); 1484 1485 assertSame(one, cache.getUnchecked(key)); 1486 stats = cache.stats(); 1487 assertEquals(1, stats.missCount()); 1488 assertEquals(1, stats.loadSuccessCount()); 1489 assertEquals(0, stats.loadExceptionCount()); 1490 assertEquals(0, stats.hitCount()); 1491 1492 cache.refresh(key); 1493 checkLoggedCause(e); 1494 stats = cache.stats(); 1495 assertEquals(1, stats.missCount()); 1496 assertEquals(1, stats.loadSuccessCount()); 1497 assertEquals(1, stats.loadExceptionCount()); 1498 assertEquals(0, stats.hitCount()); 1499 1500 assertSame(one, cache.getUnchecked(key)); 1501 stats = cache.stats(); 1502 assertEquals(1, stats.missCount()); 1503 assertEquals(1, stats.loadSuccessCount()); 1504 assertEquals(1, stats.loadExceptionCount()); 1505 assertEquals(1, stats.hitCount()); 1506 } 1507 testReloadFutureUncheckedException()1508 public void testReloadFutureUncheckedException() throws ExecutionException { 1509 final Object one = new Object(); 1510 final Exception e = new RuntimeException(); 1511 CacheLoader<Object, Object> loader = 1512 new CacheLoader<Object, Object>() { 1513 @Override 1514 public Object load(Object key) { 1515 return one; 1516 } 1517 1518 @Override 1519 public ListenableFuture<Object> reload(Object key, Object oldValue) { 1520 return Futures.immediateFailedFuture(e); 1521 } 1522 }; 1523 1524 LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader); 1525 Object key = new Object(); 1526 CacheStats stats = cache.stats(); 1527 assertEquals(0, stats.missCount()); 1528 assertEquals(0, stats.loadSuccessCount()); 1529 assertEquals(0, stats.loadExceptionCount()); 1530 assertEquals(0, stats.hitCount()); 1531 1532 assertSame(one, cache.getUnchecked(key)); 1533 stats = cache.stats(); 1534 assertEquals(1, stats.missCount()); 1535 assertEquals(1, stats.loadSuccessCount()); 1536 assertEquals(0, stats.loadExceptionCount()); 1537 assertEquals(0, stats.hitCount()); 1538 1539 cache.refresh(key); 1540 checkLoggedCause(e); 1541 stats = cache.stats(); 1542 assertEquals(1, stats.missCount()); 1543 assertEquals(1, stats.loadSuccessCount()); 1544 assertEquals(1, stats.loadExceptionCount()); 1545 assertEquals(0, stats.hitCount()); 1546 1547 assertSame(one, cache.getUnchecked(key)); 1548 stats = cache.stats(); 1549 assertEquals(1, stats.missCount()); 1550 assertEquals(1, stats.loadSuccessCount()); 1551 assertEquals(1, stats.loadExceptionCount()); 1552 assertEquals(1, stats.hitCount()); 1553 } 1554 testRefreshUncheckedException()1555 public void testRefreshUncheckedException() { 1556 final Object one = new Object(); 1557 final Exception e = new RuntimeException(); 1558 FakeTicker ticker = new FakeTicker(); 1559 CacheLoader<Object, Object> loader = 1560 new CacheLoader<Object, Object>() { 1561 @Override 1562 public Object load(Object key) { 1563 return one; 1564 } 1565 1566 @Override 1567 public ListenableFuture<Object> reload(Object key, Object oldValue) { 1568 return Futures.immediateFailedFuture(e); 1569 } 1570 }; 1571 1572 LoadingCache<Object, Object> cache = 1573 CacheBuilder.newBuilder() 1574 .recordStats() 1575 .ticker(ticker) 1576 .refreshAfterWrite(1, MILLISECONDS) 1577 .build(loader); 1578 Object key = new Object(); 1579 CacheStats stats = cache.stats(); 1580 assertEquals(0, stats.missCount()); 1581 assertEquals(0, stats.loadSuccessCount()); 1582 assertEquals(0, stats.loadExceptionCount()); 1583 assertEquals(0, stats.hitCount()); 1584 1585 assertSame(one, cache.getUnchecked(key)); 1586 stats = cache.stats(); 1587 assertEquals(1, stats.missCount()); 1588 assertEquals(1, stats.loadSuccessCount()); 1589 assertEquals(0, stats.loadExceptionCount()); 1590 assertEquals(0, stats.hitCount()); 1591 1592 ticker.advance(1, MILLISECONDS); 1593 assertSame(one, cache.getUnchecked(key)); 1594 stats = cache.stats(); 1595 assertEquals(1, stats.missCount()); 1596 assertEquals(1, stats.loadSuccessCount()); 1597 assertEquals(0, stats.loadExceptionCount()); 1598 assertEquals(1, stats.hitCount()); 1599 1600 ticker.advance(1, MILLISECONDS); 1601 assertSame(one, cache.getUnchecked(key)); 1602 // refreshed 1603 stats = cache.stats(); 1604 assertEquals(1, stats.missCount()); 1605 assertEquals(1, stats.loadSuccessCount()); 1606 assertEquals(1, stats.loadExceptionCount()); 1607 assertEquals(2, stats.hitCount()); 1608 1609 ticker.advance(1, MILLISECONDS); 1610 assertSame(one, cache.getUnchecked(key)); 1611 stats = cache.stats(); 1612 assertEquals(1, stats.missCount()); 1613 assertEquals(1, stats.loadSuccessCount()); 1614 assertEquals(2, stats.loadExceptionCount()); 1615 assertEquals(3, stats.hitCount()); 1616 } 1617 testBulkLoadUncheckedException()1618 public void testBulkLoadUncheckedException() throws ExecutionException { 1619 Exception e = new RuntimeException(); 1620 CacheLoader<Object, Object> loader = exceptionLoader(e); 1621 LoadingCache<Object, Object> cache = 1622 CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader)); 1623 CacheStats stats = cache.stats(); 1624 assertEquals(0, stats.missCount()); 1625 assertEquals(0, stats.loadSuccessCount()); 1626 assertEquals(0, stats.loadExceptionCount()); 1627 assertEquals(0, stats.hitCount()); 1628 1629 UncheckedExecutionException expected = 1630 assertThrows(UncheckedExecutionException.class, () -> cache.getAll(asList(new Object()))); 1631 assertThat(expected).hasCauseThat().isSameInstanceAs(e); 1632 stats = cache.stats(); 1633 assertEquals(1, stats.missCount()); 1634 assertEquals(0, stats.loadSuccessCount()); 1635 assertEquals(1, stats.loadExceptionCount()); 1636 assertEquals(0, stats.hitCount()); 1637 } 1638 testReloadAfterFailure()1639 public void testReloadAfterFailure() throws ExecutionException { 1640 final AtomicInteger count = new AtomicInteger(); 1641 final Exception e = new IllegalStateException("exception to trigger failure on first load()"); 1642 CacheLoader<Integer, String> failOnceFunction = 1643 new CacheLoader<Integer, String>() { 1644 1645 @Override 1646 public String load(Integer key) throws Exception { 1647 if (count.getAndIncrement() == 0) { 1648 throw e; 1649 } 1650 return key.toString(); 1651 } 1652 }; 1653 CountingRemovalListener<Integer, String> removalListener = countingRemovalListener(); 1654 LoadingCache<Integer, String> cache = 1655 CacheBuilder.newBuilder().removalListener(removalListener).build(failOnceFunction); 1656 1657 UncheckedExecutionException ue = 1658 assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(1)); 1659 assertThat(ue).hasCauseThat().isSameInstanceAs(e); 1660 1661 assertEquals("1", cache.getUnchecked(1)); 1662 assertEquals(0, removalListener.getCount()); 1663 1664 count.set(0); 1665 cache.refresh(2); 1666 checkLoggedCause(e); 1667 1668 assertEquals("2", cache.getUnchecked(2)); 1669 assertEquals(0, removalListener.getCount()); 1670 } 1671 1672 1673 @AndroidIncompatible // Depends on GC behavior testReloadAfterValueReclamation()1674 public void testReloadAfterValueReclamation() throws InterruptedException, ExecutionException { 1675 CountingLoader countingLoader = new CountingLoader(); 1676 LoadingCache<Object, Object> cache = 1677 CacheBuilder.newBuilder().weakValues().build(countingLoader); 1678 ConcurrentMap<Object, Object> map = cache.asMap(); 1679 1680 int iterations = 10; 1681 WeakReference<Object> ref = new WeakReference<>(null); 1682 int expectedComputations = 0; 1683 for (int i = 0; i < iterations; i++) { 1684 // The entry should get garbage collected and recomputed. 1685 Object oldValue = ref.get(); 1686 if (oldValue == null) { 1687 expectedComputations++; 1688 } 1689 ref = new WeakReference<>(cache.getUnchecked(1)); 1690 oldValue = null; 1691 Thread.sleep(i); 1692 System.gc(); 1693 } 1694 assertEquals(expectedComputations, countingLoader.getCount()); 1695 1696 for (int i = 0; i < iterations; i++) { 1697 // The entry should get garbage collected and recomputed. 1698 Object oldValue = ref.get(); 1699 if (oldValue == null) { 1700 expectedComputations++; 1701 } 1702 cache.refresh(1); 1703 checkNothingLogged(); 1704 ref = new WeakReference<>(map.get(1)); 1705 oldValue = null; 1706 Thread.sleep(i); 1707 System.gc(); 1708 } 1709 assertEquals(expectedComputations, countingLoader.getCount()); 1710 } 1711 testReloadAfterSimulatedValueReclamation()1712 public void testReloadAfterSimulatedValueReclamation() throws ExecutionException { 1713 CountingLoader countingLoader = new CountingLoader(); 1714 LoadingCache<Object, Object> cache = 1715 CacheBuilder.newBuilder().concurrencyLevel(1).weakValues().build(countingLoader); 1716 1717 Object key = new Object(); 1718 assertNotNull(cache.getUnchecked(key)); 1719 1720 CacheTesting.simulateValueReclamation(cache, key); 1721 1722 // this blocks if computation can't deal with partially-collected values 1723 assertNotNull(cache.getUnchecked(key)); 1724 assertEquals(1, cache.size()); 1725 assertEquals(2, countingLoader.getCount()); 1726 1727 CacheTesting.simulateValueReclamation(cache, key); 1728 cache.refresh(key); 1729 checkNothingLogged(); 1730 assertEquals(1, cache.size()); 1731 assertEquals(3, countingLoader.getCount()); 1732 } 1733 testReloadAfterSimulatedKeyReclamation()1734 public void testReloadAfterSimulatedKeyReclamation() throws ExecutionException { 1735 CountingLoader countingLoader = new CountingLoader(); 1736 LoadingCache<Object, Object> cache = 1737 CacheBuilder.newBuilder().concurrencyLevel(1).weakKeys().build(countingLoader); 1738 1739 Object key = new Object(); 1740 assertNotNull(cache.getUnchecked(key)); 1741 assertEquals(1, cache.size()); 1742 1743 CacheTesting.simulateKeyReclamation(cache, key); 1744 1745 // this blocks if computation can't deal with partially-collected values 1746 assertNotNull(cache.getUnchecked(key)); 1747 assertEquals(2, countingLoader.getCount()); 1748 1749 CacheTesting.simulateKeyReclamation(cache, key); 1750 cache.refresh(key); 1751 checkNothingLogged(); 1752 assertEquals(3, countingLoader.getCount()); 1753 } 1754 1755 /** 1756 * Make sure LoadingCache correctly wraps ExecutionExceptions and UncheckedExecutionExceptions. 1757 */ testLoadingExceptionWithCause()1758 public void testLoadingExceptionWithCause() { 1759 final Exception cause = new Exception(); 1760 final UncheckedExecutionException uee = new UncheckedExecutionException(cause); 1761 final ExecutionException ee = new ExecutionException(cause); 1762 1763 LoadingCache<Object, Object> cacheUnchecked = 1764 CacheBuilder.newBuilder().build(exceptionLoader(uee)); 1765 LoadingCache<Object, Object> cacheChecked = 1766 CacheBuilder.newBuilder().build(exceptionLoader(ee)); 1767 1768 try { 1769 cacheUnchecked.get(new Object()); 1770 fail(); 1771 } catch (ExecutionException e) { 1772 fail(); 1773 } catch (UncheckedExecutionException caughtEe) { 1774 assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee); 1775 } 1776 1777 UncheckedExecutionException caughtUee = 1778 assertThrows( 1779 UncheckedExecutionException.class, () -> cacheUnchecked.getUnchecked(new Object())); 1780 assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee); 1781 1782 cacheUnchecked.refresh(new Object()); 1783 checkLoggedCause(uee); 1784 1785 try { 1786 cacheUnchecked.getAll(asList(new Object())); 1787 fail(); 1788 } catch (ExecutionException e) { 1789 fail(); 1790 } catch (UncheckedExecutionException caughtEe) { 1791 assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee); 1792 } 1793 1794 ExecutionException caughtEe = 1795 assertThrows(ExecutionException.class, () -> cacheChecked.get(new Object())); 1796 assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); 1797 1798 caughtUee = 1799 assertThrows( 1800 UncheckedExecutionException.class, () -> cacheChecked.getUnchecked(new Object())); 1801 assertThat(caughtUee).hasCauseThat().isSameInstanceAs(ee); 1802 1803 cacheChecked.refresh(new Object()); 1804 checkLoggedCause(ee); 1805 1806 caughtEe = 1807 assertThrows(ExecutionException.class, () -> cacheChecked.getAll(asList(new Object()))); 1808 assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); 1809 } 1810 testBulkLoadingExceptionWithCause()1811 public void testBulkLoadingExceptionWithCause() { 1812 final Exception cause = new Exception(); 1813 final UncheckedExecutionException uee = new UncheckedExecutionException(cause); 1814 final ExecutionException ee = new ExecutionException(cause); 1815 1816 LoadingCache<Object, Object> cacheUnchecked = 1817 CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(uee))); 1818 LoadingCache<Object, Object> cacheChecked = 1819 CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(ee))); 1820 1821 try { 1822 cacheUnchecked.getAll(asList(new Object())); 1823 fail(); 1824 } catch (ExecutionException e) { 1825 fail(); 1826 } catch (UncheckedExecutionException caughtEe) { 1827 assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee); 1828 } 1829 1830 ExecutionException caughtEe = 1831 assertThrows(ExecutionException.class, () -> cacheChecked.getAll(asList(new Object()))); 1832 assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee); 1833 } 1834 1835 @AndroidIncompatible // Bug? expected:<1> but was:<2> testConcurrentLoading()1836 public void testConcurrentLoading() throws InterruptedException { 1837 testConcurrentLoading(CacheBuilder.newBuilder()); 1838 } 1839 testConcurrentLoading(CacheBuilder<Object, Object> builder)1840 private static void testConcurrentLoading(CacheBuilder<Object, Object> builder) 1841 throws InterruptedException { 1842 testConcurrentLoadingDefault(builder); 1843 testConcurrentLoadingNull(builder); 1844 testConcurrentLoadingUncheckedException(builder); 1845 testConcurrentLoadingCheckedException(builder); 1846 } 1847 1848 @AndroidIncompatible // Bug? expected:<1> but was:<2> testConcurrentExpirationLoading()1849 public void testConcurrentExpirationLoading() throws InterruptedException { 1850 testConcurrentLoading(CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS)); 1851 } 1852 1853 /** 1854 * On a successful concurrent computation, only one thread does the work, but all the threads get 1855 * the same result. 1856 */ testConcurrentLoadingDefault(CacheBuilder<Object, Object> builder)1857 private static void testConcurrentLoadingDefault(CacheBuilder<Object, Object> builder) 1858 throws InterruptedException { 1859 1860 int count = 10; 1861 final AtomicInteger callCount = new AtomicInteger(); 1862 final CountDownLatch startSignal = new CountDownLatch(count + 1); 1863 final Object result = new Object(); 1864 1865 LoadingCache<String, Object> cache = 1866 builder.build( 1867 new CacheLoader<String, Object>() { 1868 @Override 1869 public Object load(String key) throws InterruptedException { 1870 callCount.incrementAndGet(); 1871 startSignal.await(); 1872 return result; 1873 } 1874 }); 1875 1876 List<Object> resultArray = doConcurrentGet(cache, "bar", count, startSignal); 1877 1878 assertEquals(1, callCount.get()); 1879 for (int i = 0; i < count; i++) { 1880 assertSame("result(" + i + ") didn't match expected", result, resultArray.get(i)); 1881 } 1882 } 1883 1884 /** 1885 * On a concurrent computation that returns null, all threads should get an 1886 * InvalidCacheLoadException, with the loader only called once. The result should not be cached (a 1887 * later request should call the loader again). 1888 */ testConcurrentLoadingNull(CacheBuilder<Object, Object> builder)1889 private static void testConcurrentLoadingNull(CacheBuilder<Object, Object> builder) 1890 throws InterruptedException { 1891 1892 int count = 10; 1893 final AtomicInteger callCount = new AtomicInteger(); 1894 final CountDownLatch startSignal = new CountDownLatch(count + 1); 1895 1896 LoadingCache<String, String> cache = 1897 builder.build( 1898 new CacheLoader<String, String>() { 1899 @Override 1900 public String load(String key) throws InterruptedException { 1901 callCount.incrementAndGet(); 1902 startSignal.await(); 1903 return null; 1904 } 1905 }); 1906 1907 List<Object> result = doConcurrentGet(cache, "bar", count, startSignal); 1908 1909 assertEquals(1, callCount.get()); 1910 for (int i = 0; i < count; i++) { 1911 assertThat(result.get(i)).isInstanceOf(InvalidCacheLoadException.class); 1912 } 1913 1914 // subsequent calls should call the loader again, not get the old exception 1915 try { 1916 cache.getUnchecked("bar"); 1917 fail(); 1918 } catch (InvalidCacheLoadException expected) { 1919 } 1920 assertEquals(2, callCount.get()); 1921 } 1922 1923 /** 1924 * On a concurrent computation that throws an unchecked exception, all threads should get the 1925 * (wrapped) exception, with the loader called only once. The result should not be cached (a later 1926 * request should call the loader again). 1927 */ testConcurrentLoadingUncheckedException(CacheBuilder<Object, Object> builder)1928 private static void testConcurrentLoadingUncheckedException(CacheBuilder<Object, Object> builder) 1929 throws InterruptedException { 1930 1931 int count = 10; 1932 final AtomicInteger callCount = new AtomicInteger(); 1933 final CountDownLatch startSignal = new CountDownLatch(count + 1); 1934 final RuntimeException e = new RuntimeException(); 1935 1936 LoadingCache<String, String> cache = 1937 builder.build( 1938 new CacheLoader<String, String>() { 1939 @Override 1940 public String load(String key) throws InterruptedException { 1941 callCount.incrementAndGet(); 1942 startSignal.await(); 1943 throw e; 1944 } 1945 }); 1946 1947 List<Object> result = doConcurrentGet(cache, "bar", count, startSignal); 1948 1949 assertEquals(1, callCount.get()); 1950 for (int i = 0; i < count; i++) { 1951 // doConcurrentGet alternates between calling getUnchecked and calling get, but an unchecked 1952 // exception thrown by the loader is always wrapped as an UncheckedExecutionException. 1953 assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class); 1954 assertThat(((UncheckedExecutionException) result.get(i))).hasCauseThat().isSameInstanceAs(e); 1955 } 1956 1957 // subsequent calls should call the loader again, not get the old exception 1958 try { 1959 cache.getUnchecked("bar"); 1960 fail(); 1961 } catch (UncheckedExecutionException expected) { 1962 } 1963 assertEquals(2, callCount.get()); 1964 } 1965 1966 /** 1967 * On a concurrent computation that throws a checked exception, all threads should get the 1968 * (wrapped) exception, with the loader called only once. The result should not be cached (a later 1969 * request should call the loader again). 1970 */ testConcurrentLoadingCheckedException(CacheBuilder<Object, Object> builder)1971 private static void testConcurrentLoadingCheckedException(CacheBuilder<Object, Object> builder) 1972 throws InterruptedException { 1973 1974 int count = 10; 1975 final AtomicInteger callCount = new AtomicInteger(); 1976 final CountDownLatch startSignal = new CountDownLatch(count + 1); 1977 final IOException e = new IOException(); 1978 1979 LoadingCache<String, String> cache = 1980 builder.build( 1981 new CacheLoader<String, String>() { 1982 @Override 1983 public String load(String key) throws IOException, InterruptedException { 1984 callCount.incrementAndGet(); 1985 startSignal.await(); 1986 throw e; 1987 } 1988 }); 1989 1990 List<Object> result = doConcurrentGet(cache, "bar", count, startSignal); 1991 1992 assertEquals(1, callCount.get()); 1993 for (int i = 0; i < count; i++) { 1994 // doConcurrentGet alternates between calling getUnchecked and calling get. If we call get(), 1995 // we should get an ExecutionException; if we call getUnchecked(), we should get an 1996 // UncheckedExecutionException. 1997 int mod = i % 3; 1998 if (mod == 0 || mod == 2) { 1999 assertThat(result.get(i)).isInstanceOf(ExecutionException.class); 2000 assertThat((ExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e); 2001 } else { 2002 assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class); 2003 assertThat((UncheckedExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e); 2004 } 2005 } 2006 2007 // subsequent calls should call the loader again, not get the old exception 2008 try { 2009 cache.getUnchecked("bar"); 2010 fail(); 2011 } catch (UncheckedExecutionException expected) { 2012 } 2013 assertEquals(2, callCount.get()); 2014 } 2015 2016 /** 2017 * Test-helper method that performs {@code nThreads} concurrent calls to {@code cache.get(key)} or 2018 * {@code cache.getUnchecked(key)}, and returns a List containing each of the results. The result 2019 * for any given call to {@code cache.get} or {@code cache.getUnchecked} is the value returned, or 2020 * the exception thrown. 2021 * 2022 * <p>As we iterate from {@code 0} to {@code nThreads}, threads with an even index will call 2023 * {@code getUnchecked}, and threads with an odd index will call {@code get}. If the cache throws 2024 * exceptions, this difference may be visible in the returned List. 2025 */ doConcurrentGet( final LoadingCache<K, ?> cache, final K key, int nThreads, final CountDownLatch gettersStartedSignal)2026 private static <K> List<Object> doConcurrentGet( 2027 final LoadingCache<K, ?> cache, 2028 final K key, 2029 int nThreads, 2030 final CountDownLatch gettersStartedSignal) 2031 throws InterruptedException { 2032 2033 final AtomicReferenceArray<Object> result = new AtomicReferenceArray<>(nThreads); 2034 final CountDownLatch gettersComplete = new CountDownLatch(nThreads); 2035 for (int i = 0; i < nThreads; i++) { 2036 final int index = i; 2037 Thread thread = 2038 new Thread( 2039 new Runnable() { 2040 @Override 2041 public void run() { 2042 gettersStartedSignal.countDown(); 2043 Object value = null; 2044 try { 2045 int mod = index % 3; 2046 if (mod == 0) { 2047 value = cache.get(key); 2048 } else if (mod == 1) { 2049 value = cache.getUnchecked(key); 2050 } else { 2051 cache.refresh(key); 2052 value = cache.get(key); 2053 } 2054 result.set(index, value); 2055 } catch (Throwable t) { 2056 result.set(index, t); 2057 } 2058 gettersComplete.countDown(); 2059 } 2060 }); 2061 thread.start(); 2062 // we want to wait until each thread is WAITING - one thread waiting inside CacheLoader.load 2063 // (in startSignal.await()), and the others waiting for that thread's result. 2064 while (thread.isAlive() && thread.getState() != Thread.State.WAITING) { 2065 Thread.yield(); 2066 } 2067 } 2068 gettersStartedSignal.countDown(); 2069 gettersComplete.await(); 2070 2071 List<Object> resultList = Lists.newArrayListWithExpectedSize(nThreads); 2072 for (int i = 0; i < nThreads; i++) { 2073 resultList.add(result.get(i)); 2074 } 2075 return resultList; 2076 } 2077 testAsMapDuringLoading()2078 public void testAsMapDuringLoading() throws InterruptedException, ExecutionException { 2079 final CountDownLatch getStartedSignal = new CountDownLatch(2); 2080 final CountDownLatch letGetFinishSignal = new CountDownLatch(1); 2081 final CountDownLatch getFinishedSignal = new CountDownLatch(2); 2082 final String getKey = "get"; 2083 final String refreshKey = "refresh"; 2084 final String suffix = "Suffix"; 2085 2086 CacheLoader<String, String> computeFunction = 2087 new CacheLoader<String, String>() { 2088 @Override 2089 public String load(String key) throws InterruptedException { 2090 getStartedSignal.countDown(); 2091 letGetFinishSignal.await(); 2092 return key + suffix; 2093 } 2094 }; 2095 2096 final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction); 2097 ConcurrentMap<String, String> map = cache.asMap(); 2098 map.put(refreshKey, refreshKey); 2099 assertEquals(1, map.size()); 2100 assertFalse(map.containsKey(getKey)); 2101 assertSame(refreshKey, map.get(refreshKey)); 2102 2103 new Thread() { 2104 @Override 2105 public void run() { 2106 cache.getUnchecked(getKey); 2107 getFinishedSignal.countDown(); 2108 } 2109 }.start(); 2110 new Thread() { 2111 @Override 2112 public void run() { 2113 cache.refresh(refreshKey); 2114 getFinishedSignal.countDown(); 2115 } 2116 }.start(); 2117 2118 getStartedSignal.await(); 2119 2120 // computation is in progress; asMap shouldn't have changed 2121 assertEquals(1, map.size()); 2122 assertFalse(map.containsKey(getKey)); 2123 assertSame(refreshKey, map.get(refreshKey)); 2124 2125 // let computation complete 2126 letGetFinishSignal.countDown(); 2127 getFinishedSignal.await(); 2128 checkNothingLogged(); 2129 2130 // asMap view should have been updated 2131 assertEquals(2, cache.size()); 2132 assertEquals(getKey + suffix, map.get(getKey)); 2133 assertEquals(refreshKey + suffix, map.get(refreshKey)); 2134 } 2135 testInvalidateDuringLoading()2136 public void testInvalidateDuringLoading() throws InterruptedException, ExecutionException { 2137 // computation starts; invalidate() is called on the key being computed, computation finishes 2138 final CountDownLatch computationStarted = new CountDownLatch(2); 2139 final CountDownLatch letGetFinishSignal = new CountDownLatch(1); 2140 final CountDownLatch getFinishedSignal = new CountDownLatch(2); 2141 final String getKey = "get"; 2142 final String refreshKey = "refresh"; 2143 final String suffix = "Suffix"; 2144 2145 CacheLoader<String, String> computeFunction = 2146 new CacheLoader<String, String>() { 2147 @Override 2148 public String load(String key) throws InterruptedException { 2149 computationStarted.countDown(); 2150 letGetFinishSignal.await(); 2151 return key + suffix; 2152 } 2153 }; 2154 2155 final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction); 2156 ConcurrentMap<String, String> map = cache.asMap(); 2157 map.put(refreshKey, refreshKey); 2158 2159 new Thread() { 2160 @Override 2161 public void run() { 2162 cache.getUnchecked(getKey); 2163 getFinishedSignal.countDown(); 2164 } 2165 }.start(); 2166 new Thread() { 2167 @Override 2168 public void run() { 2169 cache.refresh(refreshKey); 2170 getFinishedSignal.countDown(); 2171 } 2172 }.start(); 2173 2174 computationStarted.await(); 2175 cache.invalidate(getKey); 2176 cache.invalidate(refreshKey); 2177 assertFalse(map.containsKey(getKey)); 2178 assertFalse(map.containsKey(refreshKey)); 2179 2180 // let computation complete 2181 letGetFinishSignal.countDown(); 2182 getFinishedSignal.await(); 2183 checkNothingLogged(); 2184 2185 // results should be visible 2186 assertEquals(2, cache.size()); 2187 assertEquals(getKey + suffix, map.get(getKey)); 2188 assertEquals(refreshKey + suffix, map.get(refreshKey)); 2189 assertEquals(2, cache.size()); 2190 } 2191 testInvalidateAndReloadDuringLoading()2192 public void testInvalidateAndReloadDuringLoading() 2193 throws InterruptedException, ExecutionException { 2194 // computation starts; clear() is called, computation finishes 2195 final CountDownLatch computationStarted = new CountDownLatch(2); 2196 final CountDownLatch letGetFinishSignal = new CountDownLatch(1); 2197 final CountDownLatch getFinishedSignal = new CountDownLatch(4); 2198 final String getKey = "get"; 2199 final String refreshKey = "refresh"; 2200 final String suffix = "Suffix"; 2201 2202 CacheLoader<String, String> computeFunction = 2203 new CacheLoader<String, String>() { 2204 @Override 2205 public String load(String key) throws InterruptedException { 2206 computationStarted.countDown(); 2207 letGetFinishSignal.await(); 2208 return key + suffix; 2209 } 2210 }; 2211 2212 final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction); 2213 ConcurrentMap<String, String> map = cache.asMap(); 2214 map.put(refreshKey, refreshKey); 2215 2216 new Thread() { 2217 @Override 2218 public void run() { 2219 cache.getUnchecked(getKey); 2220 getFinishedSignal.countDown(); 2221 } 2222 }.start(); 2223 new Thread() { 2224 @Override 2225 public void run() { 2226 cache.refresh(refreshKey); 2227 getFinishedSignal.countDown(); 2228 } 2229 }.start(); 2230 2231 computationStarted.await(); 2232 cache.invalidate(getKey); 2233 cache.invalidate(refreshKey); 2234 assertFalse(map.containsKey(getKey)); 2235 assertFalse(map.containsKey(refreshKey)); 2236 2237 // start new computations 2238 new Thread() { 2239 @Override 2240 public void run() { 2241 cache.getUnchecked(getKey); 2242 getFinishedSignal.countDown(); 2243 } 2244 }.start(); 2245 new Thread() { 2246 @Override 2247 public void run() { 2248 cache.refresh(refreshKey); 2249 getFinishedSignal.countDown(); 2250 } 2251 }.start(); 2252 2253 // let computation complete 2254 letGetFinishSignal.countDown(); 2255 getFinishedSignal.await(); 2256 checkNothingLogged(); 2257 2258 // results should be visible 2259 assertEquals(2, cache.size()); 2260 assertEquals(getKey + suffix, map.get(getKey)); 2261 assertEquals(refreshKey + suffix, map.get(refreshKey)); 2262 } 2263 testExpandDuringLoading()2264 public void testExpandDuringLoading() throws InterruptedException { 2265 final int count = 3; 2266 final AtomicInteger callCount = new AtomicInteger(); 2267 // tells the computing thread when to start computing 2268 final CountDownLatch computeSignal = new CountDownLatch(1); 2269 // tells the main thread when computation is pending 2270 final CountDownLatch secondSignal = new CountDownLatch(1); 2271 // tells the main thread when the second get has started 2272 final CountDownLatch thirdSignal = new CountDownLatch(1); 2273 // tells the main thread when the third get has started 2274 final CountDownLatch fourthSignal = new CountDownLatch(1); 2275 // tells the test when all gets have returned 2276 final CountDownLatch doneSignal = new CountDownLatch(count); 2277 2278 CacheLoader<String, String> computeFunction = 2279 new CacheLoader<String, String>() { 2280 @Override 2281 public String load(String key) throws InterruptedException { 2282 callCount.incrementAndGet(); 2283 secondSignal.countDown(); 2284 computeSignal.await(); 2285 return key + "foo"; 2286 } 2287 }; 2288 2289 final LoadingCache<String, String> cache = 2290 CacheBuilder.newBuilder().weakKeys().build(computeFunction); 2291 2292 final AtomicReferenceArray<String> result = new AtomicReferenceArray<>(count); 2293 2294 final String key = "bar"; 2295 2296 // start computing thread 2297 new Thread() { 2298 @Override 2299 public void run() { 2300 result.set(0, cache.getUnchecked(key)); 2301 doneSignal.countDown(); 2302 } 2303 }.start(); 2304 2305 // wait for computation to start 2306 secondSignal.await(); 2307 2308 // start waiting thread 2309 new Thread() { 2310 @Override 2311 public void run() { 2312 thirdSignal.countDown(); 2313 result.set(1, cache.getUnchecked(key)); 2314 doneSignal.countDown(); 2315 } 2316 }.start(); 2317 2318 // give the second get a chance to run; it is okay for this to be racy 2319 // as the end result should be the same either way 2320 thirdSignal.await(); 2321 Thread.yield(); 2322 2323 // Expand! 2324 CacheTesting.forceExpandSegment(cache, key); 2325 2326 // start another waiting thread 2327 new Thread() { 2328 @Override 2329 public void run() { 2330 fourthSignal.countDown(); 2331 result.set(2, cache.getUnchecked(key)); 2332 doneSignal.countDown(); 2333 } 2334 }.start(); 2335 2336 // give the third get a chance to run; it is okay for this to be racy 2337 // as the end result should be the same either way 2338 fourthSignal.await(); 2339 Thread.yield(); 2340 2341 // let computation finish 2342 computeSignal.countDown(); 2343 doneSignal.await(); 2344 2345 assertTrue(callCount.get() == 1); 2346 assertEquals("barfoo", result.get(0)); 2347 assertEquals("barfoo", result.get(1)); 2348 assertEquals("barfoo", result.get(2)); 2349 assertEquals("barfoo", cache.getUnchecked(key)); 2350 } 2351 2352 // Test ignored because it is extremely flaky in CI builds 2353 public void ignoreTestExpandDuringRefresh()2354 ignoreTestExpandDuringRefresh() 2355 throws InterruptedException, ExecutionException { 2356 final AtomicInteger callCount = new AtomicInteger(); 2357 // tells the computing thread when to start computing 2358 final CountDownLatch computeSignal = new CountDownLatch(1); 2359 // tells the main thread when computation is pending 2360 final CountDownLatch secondSignal = new CountDownLatch(1); 2361 // tells the main thread when the second get has started 2362 final CountDownLatch thirdSignal = new CountDownLatch(1); 2363 // tells the main thread when the third get has started 2364 final CountDownLatch fourthSignal = new CountDownLatch(1); 2365 // tells the test when all gets have returned 2366 final CountDownLatch doneSignal = new CountDownLatch(3); 2367 final String suffix = "Suffix"; 2368 2369 CacheLoader<String, String> computeFunction = 2370 new CacheLoader<String, String>() { 2371 @Override 2372 public String load(String key) throws InterruptedException { 2373 callCount.incrementAndGet(); 2374 secondSignal.countDown(); 2375 computeSignal.await(); 2376 return key + suffix; 2377 } 2378 }; 2379 2380 final AtomicReferenceArray<String> result = new AtomicReferenceArray<>(2); 2381 2382 final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction); 2383 final String key = "bar"; 2384 cache.asMap().put(key, key); 2385 2386 // start computing thread 2387 new Thread() { 2388 @Override 2389 public void run() { 2390 cache.refresh(key); 2391 doneSignal.countDown(); 2392 } 2393 }.start(); 2394 2395 // wait for computation to start 2396 secondSignal.await(); 2397 checkNothingLogged(); 2398 2399 // start waiting thread 2400 new Thread() { 2401 @Override 2402 public void run() { 2403 thirdSignal.countDown(); 2404 result.set(0, cache.getUnchecked(key)); 2405 doneSignal.countDown(); 2406 } 2407 }.start(); 2408 2409 // give the second get a chance to run; it is okay for this to be racy 2410 // as the end result should be the same either way 2411 thirdSignal.await(); 2412 Thread.yield(); 2413 2414 // Expand! 2415 CacheTesting.forceExpandSegment(cache, key); 2416 2417 // start another waiting thread 2418 new Thread() { 2419 @Override 2420 public void run() { 2421 fourthSignal.countDown(); 2422 result.set(1, cache.getUnchecked(key)); 2423 doneSignal.countDown(); 2424 } 2425 }.start(); 2426 2427 // give the third get a chance to run; it is okay for this to be racy 2428 // as the end result should be the same either way 2429 fourthSignal.await(); 2430 Thread.yield(); 2431 2432 // let computation finish 2433 computeSignal.countDown(); 2434 doneSignal.await(); 2435 2436 assertTrue(callCount.get() == 1); 2437 assertEquals(key, result.get(0)); 2438 assertEquals(key, result.get(1)); 2439 assertEquals(key + suffix, cache.getUnchecked(key)); 2440 } 2441 throwing(final Exception exception)2442 static <T> Callable<T> throwing(final Exception exception) { 2443 return new Callable<T>() { 2444 @Override 2445 public T call() throws Exception { 2446 throw exception; 2447 } 2448 }; 2449 } 2450 } 2451