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