@Override public Map<K, ValueHolder<V>> bulkComputeIfAbsent(Set<? extends K> keys, final Function<Iterable<? extends K>, Iterable<? extends Map.Entry<? extends K, ? extends V>>> mappingFunction) throws StoreAccessException { Map<K, ValueHolder<V>> result = new HashMap<>(keys.size()); for (K key : keys) { checkKey(key); Function<K, V> function = k -> { java.util.Iterator<? extends Map.Entry<? extends K, ? extends V>> iterator = mappingFunction.apply(Collections.singleton(k)).iterator(); Map.Entry<? extends K, ? extends V> result1 = iterator.next(); if (result1 != null) { checkKey(result1.getKey()); return result1.getValue(); } else { return null; } }; ValueHolder<V> computed = computeIfAbsent(key, function); result.put(key, computed); } return result; }
@Test public void testInvalidateKeyPresent() throws Exception { offHeapStore = createAndInitStore(timeSource, ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(15L))); offHeapStore.put("1", "one"); final AtomicReference<Store.ValueHolder<String>> invalidated = new AtomicReference<>(); offHeapStore.setInvalidationListener((key, valueHolder) -> invalidated.set(valueHolder)); offHeapStore.invalidate("1"); assertThat(invalidated.get().get(), equalTo("one")); validateStats(offHeapStore, EnumSet.of(LowerCachingTierOperationsOutcome.InvalidateOutcome.REMOVED)); assertThat(offHeapStore.get("1"), is(nullValue())); }
@Override public boolean remove(final K key) throws StoreAccessException { checkKey(key); try { backingMap().computeIfPresent(key, (mappedKey, mappedValue) -> { onExpiration(mappedKey, mappedValue, eventSink); return null;
@Override public boolean containsKey(K key) throws StoreAccessException { checkKey(key); return internalGet(key, false, false) != null; }
private <T extends Serializable> void registerStatistic(String name, StatisticType type, Set<String> tags, Function<EhcacheOffHeapBackingMap<K, OffHeapValueHolder<V>>, T> fn) { registerStatistic(name, type, tags, () -> { EhcacheOffHeapBackingMap<K, OffHeapValueHolder<V>> map = backingMap(); // Returning null means not available. // Do not return -1 because a stat can be negative and it's hard to tell the difference // between -1 meaning unavailable for a stat and for the other one -1 being a right value; return map == null ? null : fn.apply(map); }); }
this.getObserver = createObserver("get", StoreOperationOutcomes.GetOutcome.class, true); this.putObserver = createObserver("put", StoreOperationOutcomes.PutOutcome.class, true); this.putIfAbsentObserver = createObserver("putIfAbsent", StoreOperationOutcomes.PutIfAbsentOutcome.class, true); this.removeObserver = createObserver("remove", StoreOperationOutcomes.RemoveOutcome.class, true); this.conditionalRemoveObserver = createObserver("conditionalRemove", StoreOperationOutcomes.ConditionalRemoveOutcome.class, true); this.replaceObserver = createObserver("replace", StoreOperationOutcomes.ReplaceOutcome.class, true); this.conditionalReplaceObserver = createObserver("conditionalReplace", StoreOperationOutcomes.ConditionalReplaceOutcome.class, true); this.computeObserver = createObserver("compute", StoreOperationOutcomes.ComputeOutcome.class, true); this.computeIfAbsentObserver = createObserver("computeIfAbsent", StoreOperationOutcomes.ComputeIfAbsentOutcome.class, true); this.evictionObserver = createObserver("eviction", StoreOperationOutcomes.EvictionOutcome.class, false); this.expirationObserver = createObserver("expiration", StoreOperationOutcomes.ExpirationOutcome.class, false); this.getAndFaultObserver = createObserver("getAndFault", AuthoritativeTierOperationOutcomes.GetAndFaultOutcome.class, true); this.computeIfAbsentAndFaultObserver = createObserver("computeIfAbsentAndFault", AuthoritativeTierOperationOutcomes.ComputeIfAbsentAndFaultOutcome.class, true); this.flushObserver = createObserver("flush", AuthoritativeTierOperationOutcomes.FlushOutcome.class, true); this.invalidateObserver = createObserver("invalidate", LowerCachingTierOperationsOutcome.InvalidateOutcome.class, true); this.invalidateAllObserver = createObserver("invalidateAll", LowerCachingTierOperationsOutcome.InvalidateAllOutcome.class, true); this.invalidateAllWithHashObserver = createObserver("invalidateAllWithHash", LowerCachingTierOperationsOutcome.InvalidateAllWithHashOutcome.class, true); this.getAndRemoveObserver= createObserver("getAndRemove", LowerCachingTierOperationsOutcome.GetAndRemoveOutcome.class, true); this.installMappingObserver= createObserver("installMapping", LowerCachingTierOperationsOutcome.InstallMappingOutcome.class, true); Set<String> tags = tags(getStatisticsTag(), "tier"); registerStatistic("allocatedMemory", GAUGE, tags, EhcacheOffHeapBackingMap::allocatedMemory); registerStatistic("occupiedMemory", GAUGE, tags, EhcacheOffHeapBackingMap::occupiedMemory); registerStatistic("dataAllocatedMemory", GAUGE, tags, EhcacheOffHeapBackingMap::dataAllocatedMemory); registerStatistic("dataOccupiedMemory", GAUGE, tags, EhcacheOffHeapBackingMap::dataOccupiedMemory); registerStatistic("dataSize", GAUGE, tags, EhcacheOffHeapBackingMap::dataSize); registerStatistic("dataVitalMemory", GAUGE, tags, EhcacheOffHeapBackingMap::dataVitalMemory);
@Override public ReplaceStatus replace(final K key, final V oldValue, final V newValue) throws NullPointerException, IllegalArgumentException, StoreAccessException { checkKey(key); checkValue(oldValue); checkValue(newValue); onExpiration(mappedKey, mappedValue, eventSink); return newUpdatedValueHolder(mappedKey, newValue, mappedValue, now, eventSink); } else { mappingExists.set(true); return setAccessTimeAndExpiryThenReturnMapping(mappedKey, mappedValue, now, eventSink); computeWithRetry(key, mappingFunction, false); eventDispatcher.releaseEventSink(eventSink); if (replaced.get()) {
@Override public Store.ValueHolder<V> putIfAbsent(final K key, final V value, Consumer<Boolean> put) throws NullPointerException, StoreAccessException { checkKey(key); checkValue(value); onExpiration(mappedKey, mappedValue, eventSink); return newCreateValueHolder(mappedKey, value, now, eventSink); return setAccessTimeAndExpiryThenReturnMapping(mappedKey, mappedValue, now, eventSink); }; computeWithRetry(key, mappingFunction, false);
@Override public ValueHolder<V> computeAndGet(final K key, final BiFunction<? super K, ? super V, ? extends V> mappingFunction, final Supplier<Boolean> replaceEqual, Supplier<Boolean> invokeWriter) throws StoreAccessException { checkKey(key); if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpiration(mappedKey, mappedValue, eventSink); } else if (safeEquals(existingValue, computedValue) && !replaceEqual.get()) { if (mappedValue != null) { OffHeapValueHolder<V> valueHolder = setAccessTimeAndExpiryThenReturnMapping(mappedKey, mappedValue, now, eventSink); if (valueHolder == null) { valueHeld.set(mappedValue); checkValue(computedValue); write.set(true); if (mappedValue != null) { OffHeapValueHolder<V> valueHolder = newUpdatedValueHolder(key, computedValue, mappedValue, now, eventSink); if (valueHolder == null) { valueHeld.set(new BasicOffHeapValueHolder<>(mappedValue.getId(), computedValue, now, now)); return newCreateValueHolder(key, computedValue, now, eventSink); result = computeWithRetry(key, computeFunction, false); if (result == null && valueHeld.get() != null) { result = valueHeld.get();
@Override public RemoveStatus remove(final K key, final V value) throws StoreAccessException { checkKey(key); checkValue(value); backingMap().computeIfPresent(key, (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); onExpiration(mappedKey, mappedValue, eventSink); return null; } else if (mappedValue.get().equals(value)) { } else { mappingExists.set(true); return setAccessTimeAndExpiryThenReturnMapping(mappedKey, mappedValue, now, eventSink);
private OffHeapValueHolder<V> computeWithRetry(K key, BiFunction<K, OffHeapValueHolder<V>, OffHeapValueHolder<V>> computeFunction, boolean fault) throws StoreAccessException { OffHeapValueHolder<V> computeResult; try { computeResult = backingMap().compute(key, computeFunction, fault); } catch (OversizeMappingException ex) { try { evictionAdvisor().setSwitchedOn(false); invokeValve(); computeResult = backingMap().compute(key, computeFunction, fault); } catch (OversizeMappingException e) { throw new StoreAccessException("The element with key '" + key + "' is too large to be stored" + " in this offheap store.", e); } catch (RuntimeException e) { throw handleException(e); } finally { evictionAdvisor().setSwitchedOn(true); } } catch (RuntimeException re) { throw handleException(re); } return computeResult; }
final AtomicReference<OffHeapValueHolder<V>> heldValue = new AtomicReference<>(); try { OffHeapValueHolder<V> result = backingMap().computeIfPresent(key, (mappedKey, mappedValue) -> { long now = timeSource.getTimeMillis(); onExpiration(mappedKey, mappedValue, eventSink); return null; OffHeapValueHolder<V> valueHolder = setAccessTimeAndExpiryThenReturnMapping(mappedKey, mappedValue, now, eventSink); if (valueHolder == null) { heldValue.set(mappedValue);
if (valueHolder != null) { if (valueHolder.isExpired(timeSource.getTimeMillis())) { onExpirationInCachingTier(valueHolder, key); return null; } else { return newTransferValueHolder(valueHolder); OffHeapValueHolder<V> computeResult; try { computeResult = computeWithRetry(key, computeFunction, false); if (computeResult != null) { installMappingObserver.end(LowerCachingTierOperationsOutcome.InstallMappingOutcome.PUT);
checkKey(key); if (mappedValue == null || mappedValue.isExpired(now)) { if (mappedValue != null) { onExpirationInCachingTier(mappedValue, key); backingMap().compute(key, computeFunction, false); ValueHolder<V> result = valueHolderAtomicReference.get(); if (result == null) {
@Override public void invalidateAllWithHash(long hash) { invalidateAllWithHashObserver.begin(); int intHash = HashUtils.longHashToInt(hash); Map<K, OffHeapValueHolder<V>> removed = backingMap().removeAllWithHash(intHash); for (Map.Entry<K, OffHeapValueHolder<V>> entry : removed.entrySet()) { notifyInvalidation(entry.getKey(), entry.getValue()); } invalidateAllWithHashObserver.end(LowerCachingTierOperationsOutcome.InvalidateAllWithHashOutcome.SUCCESS); }
@Override public void invalidateAll() throws StoreAccessException { invalidateAllObserver.begin(); StoreAccessException exception = null; long errorCount = 0; for (K k : backingMap().keySet()) { try { invalidate(k); } catch (StoreAccessException e) { errorCount++; if (exception == null) { exception = e; } } } if (exception != null) { invalidateAllObserver.end(LowerCachingTierOperationsOutcome.InvalidateAllOutcome.FAILURE); throw new StoreAccessException("invalidateAll failed - error count: " + errorCount, exception); } invalidateAllObserver.end(LowerCachingTierOperationsOutcome.InvalidateAllOutcome.SUCCESS); }
Map<K, ValueHolder<V>> result = new HashMap<>(keys.size()); for (K key : keys) { checkKey(key); BiFunction<K, V, V> biFunction = (k, v) -> { Map.Entry<K, V> entry = new Map.Entry<K, V>() { Map.Entry<? extends K, ? extends V> result1 = iterator.next(); if (result1 != null) { checkKey(result1.getKey()); return result1.getValue(); } else { ValueHolder<V> computed = computeAndGet(key, biFunction, replaceEqual, () -> false); result.put(key, computed);
@Override public void clear() throws StoreAccessException { try { backingMap().clear(); } catch (RuntimeException re) { throw handleException(re); } }
@Test public void testInvalidateKeyAbsent() throws Exception { offHeapStore = createAndInitStore(timeSource, ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(15L))); final AtomicReference<Store.ValueHolder<String>> invalidated = new AtomicReference<>(); offHeapStore.setInvalidationListener((key, valueHolder) -> invalidated.set(valueHolder)); offHeapStore.invalidate("1"); assertThat(invalidated.get(), is(nullValue())); validateStats(offHeapStore, EnumSet.of(LowerCachingTierOperationsOutcome.InvalidateOutcome.MISS)); }
@Test public void testComputeIfAbsentOnExpiredEntry() throws StoreAccessException { offHeapStore = createAndInitStore(timeSource, ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(10L))); offHeapStore.put("key", "value"); timeSource.advanceTime(20L); offHeapStore.computeIfAbsent("key", mappedKey -> { assertThat(mappedKey, is("key")); return "value2"; }); assertThat(getExpirationStatistic(offHeapStore).count(StoreOperationOutcomes.ExpirationOutcome.SUCCESS), is(1L)); }