/** * Returns the cached {@link RevObject}, if present in either the L1 or L2 cache, or * {@code null} otherwise. * <p> * As {@link RevTree}s are frequently requested and tend to be slower to parse, cache miss * to the L1 cache that resulted in a cache hit to the L2 cache, and where the resulting * object is a {@code RevTree}, will result in the tree being added back to the L1 cache. */ public @Nullable RevObject getIfPresent(Key key) { RevObject obj = L1Cache.getIfPresent(key); if (obj == null) { // call cache.getIfPresent instead of map.get() or the cache stats don't record the // hits/misses byte[] val = L2Cache.getIfPresent(key); if (val != null) { obj = decode(key, val); if (TYPE.TREE == obj.getType()) {// keep L1 hot on tree objects L1Cache.asMap().putIfAbsent(key, (RevTree) obj); } } } return obj; }
void insert(Key key, RevObject obj) { byte[] value = encode(obj); if (null == L2Cache.asMap().putIfAbsent(key, value)) { sizeTracker.inserted(key, value); } }
@Nullable Future<?> putInternal(Key key, RevObject obj) { if (!L2Cache.asMap().containsKey(key)) { return WRITE_BACK_EXECUTOR.submit(() -> insert(key, obj)); } return null; }
Impl(final int L1Capacity, Cache<Key, byte[]> byteCache, SizeTracker sizeTracker) { this.L2Cache = byteCache; this.sizeTracker = sizeTracker; RemovalListener<Key, RevObject> L1WriteBack = (notification) -> { RemovalCause cause = notification.getCause(); if (RemovalCause.SIZE == cause) { Key key = notification.getKey(); RevObject value = notification.getValue(); if (value != null) { putInternal(key, value); } } }; this.L1Cache = CacheBuilder.newBuilder()// .concurrencyLevel(1)// .maximumSize(L1Capacity)// .softValues()// .removalListener(L1WriteBack)// .build(); }
/** * Adds the given object to the cache under the given key, if not already present. * <p> * If the object happens to be a {@link RevTree}, it will first be added to the * {@link #L1Cache}. In either case, it's serialized version will be, possibly * asynchronously, added to the {@link #L2Cache}. */ public @Nullable Future<?> put(Key key, RevObject obj) { RevObject l1val = TYPE.TREE == obj.getType() ? L1Cache.asMap().putIfAbsent(key, (RevTree) obj) : null; if (l1val == null) { // add it to L2 if not already present, even if it's a RevTree and has been added to // the L1 cache, since removal notifications happen after the fact return putInternal(key, obj); } return null; }
public void dispose() { invalidateAll(); }
public void invalidateAll(CacheIdentifier prefix) { invalidateAll(prefix, L1Cache.asMap()); invalidateAll(prefix, L2Cache.asMap()); }