/*
* Copyright Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ehcache.impl.internal.resilience;
import org.ehcache.core.exceptions.ExceptionFactory;
import org.ehcache.spi.loaderwriter.BulkCacheLoadingException;
import org.ehcache.spi.loaderwriter.BulkCacheWritingException;
import org.ehcache.spi.loaderwriter.CacheLoaderWriter;
import org.ehcache.spi.resilience.RecoveryStore;
import org.ehcache.spi.resilience.StoreAccessException;
import java.util.Map;
import java.util.Objects;
Default resilience strategy used by a Cache
with a CacheLoaderWriter
specified. It will behaves in two ways:
- Keep the loader-writer in sync. E.g. a put will write to it
- Answer by retrieving the value from the loader-writer
Note: This behavior is the most accurate one but will add load to the loader-writer backend.
It also tries to cleanup any corrupted key.
/**
* Default resilience strategy used by a {@link org.ehcache.Cache} with a {@link CacheLoaderWriter} specified. It will
* behaves in two ways:
* <ul>
* <li>Keep the loader-writer in sync. E.g. a put will write to it</li>
* <li>Answer by retrieving the value from the loader-writer</li>
* <ul>
* Note: This behavior is the most accurate one but will add load to the loader-writer backend.
* <p>
* It also tries to cleanup any corrupted key.
*/
public class RobustLoaderWriterResilienceStrategy<K, V> extends AbstractResilienceStrategy<K, V> {
private final CacheLoaderWriter<? super K, V> loaderWriter;
public RobustLoaderWriterResilienceStrategy(RecoveryStore<K> store, CacheLoaderWriter<? super K, V> loaderWriter) {
super(store);
this.loaderWriter = Objects.requireNonNull(loaderWriter);
}
Get the value from the loader-writer.
Params: - key – the key being retrieved
- e – the triggered failure
Returns: value as loaded from the loader-writer
/**
* Get the value from the loader-writer.
*
* @param key the key being retrieved
* @param e the triggered failure
* @return value as loaded from the loader-writer
*/
@Override
public V getFailure(K key, StoreAccessException e) {
try {
return loaderWriter.load(key);
} catch (Exception e1) {
throw ExceptionFactory.newCacheLoadingException(e1, e);
} finally {
cleanup(key, e);
}
}
Return false. It doesn't matter if the key is present in the backend, we consider it's not in the cache.
Params: - key – the key being queried
- e – the triggered failure
Returns: false
/**
* Return false. It doesn't matter if the key is present in the backend, we consider it's not in the cache.
*
* @param key the key being queried
* @param e the triggered failure
* @return false
*/
@Override
public boolean containsKeyFailure(K key, StoreAccessException e) {
cleanup(key, e);
return false;
}
Write the value to the loader-write.
Params: - key – the key being put
- value – the value being put
- e – the triggered failure
/**
* Write the value to the loader-write.
*
* @param key the key being put
* @param value the value being put
* @param e the triggered failure
*/
@Override
public void putFailure(K key, V value, StoreAccessException e) {
try {
loaderWriter.write(key, value);
} catch (Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
} finally {
cleanup(key, e);
}
}
Delete the key from the loader-writer.
Params: - key – the key being removed
- e – the triggered failure
/**
* Delete the key from the loader-writer.
*
* @param key the key being removed
* @param e the triggered failure
*/
@Override
public void removeFailure(K key, StoreAccessException e) {
try {
loaderWriter.delete(key);
} catch(Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
} finally {
cleanup(key, e);
}
}
Do nothing.
Params: - e – the triggered failure
/**
* Do nothing.
*
* @param e the triggered failure
*/
@Override
public void clearFailure(StoreAccessException e) {
cleanup(e);
}
Write the value to the loader-writer if it doesn't already exist in it. Note that the load and write pair
is not atomic. This atomicity, if needed, should be handled by the something else.
Params: - key – the key being put
- value – the value being put
- e – the triggered failure
Returns: the existing value or null if the new was set
/**
* Write the value to the loader-writer if it doesn't already exist in it. Note that the load and write pair
* is not atomic. This atomicity, if needed, should be handled by the something else.
*
* @param key the key being put
* @param value the value being put
* @param e the triggered failure
* @return the existing value or null if the new was set
*/
@Override
public V putIfAbsentFailure(K key, V value, StoreAccessException e) {
// FIXME: Should I care about useLoaderInAtomics?
try {
try {
V loaded = loaderWriter.load(key);
if (loaded != null) {
return loaded;
}
} catch (Exception e1) {
throw ExceptionFactory.newCacheLoadingException(e1, e);
}
try {
loaderWriter.write(key, value);
} catch (Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
}
} finally {
cleanup(key, e);
}
return null;
}
Delete the key from the loader-writer if it is found with a matching value. Note that the load and write pair
is not atomic. This atomicity, if needed, should be handled by the something else.
Params: - key – the key being removed
- value – the value being removed
- e – the triggered failure
Returns: if the value was removed
/**
* Delete the key from the loader-writer if it is found with a matching value. Note that the load and write pair
* is not atomic. This atomicity, if needed, should be handled by the something else.
*
* @param key the key being removed
* @param value the value being removed
* @param e the triggered failure
* @return if the value was removed
*/
@Override
public boolean removeFailure(K key, V value, StoreAccessException e) {
try {
V loadedValue;
try {
loadedValue = loaderWriter.load(key);
} catch (Exception e1) {
throw ExceptionFactory.newCacheLoadingException(e1, e);
}
if (loadedValue == null) {
return false;
}
if (!loadedValue.equals(value)) {
return false;
}
try {
loaderWriter.delete(key);
} catch (Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
}
return true;
} finally {
cleanup(key, e);
}
}
Write the value to the loader-writer if the key already exists. Note that the load and write pair
is not atomic. This atomicity, if needed, should be handled by the something else.
Params: - key – the key being replaced
- value – the value being replaced
- e – the triggered failure
Returns: the old value or null if not found
/**
* Write the value to the loader-writer if the key already exists. Note that the load and write pair
* is not atomic. This atomicity, if needed, should be handled by the something else.
*
* @param key the key being replaced
* @param value the value being replaced
* @param e the triggered failure
* @return the old value or null if not found
*/
@Override
public V replaceFailure(K key, V value, StoreAccessException e) {
try {
V oldValue;
try {
oldValue = loaderWriter.load(key);
} catch (Exception e1) {
throw ExceptionFactory.newCacheLoadingException(e1, e);
}
if (oldValue != null) {
try {
loaderWriter.write(key, value);
} catch (Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
}
}
return oldValue;
} finally {
cleanup(key, e);
}
}
Write the value to the loader-writer if the entry already exists with a matching value. Note that the load and write pair
is not atomic. This atomicity, if needed, should be handled by the something else.
Params: - key – the key being replaced
- value – the expected value
- newValue – the replacement value
- e – the triggered failure
Returns: if the value was replaced
/**
* Write the value to the loader-writer if the entry already exists with a matching value. Note that the load and write pair
* is not atomic. This atomicity, if needed, should be handled by the something else.
*
* @param key the key being replaced
* @param value the expected value
* @param newValue the replacement value
* @param e the triggered failure
* @return if the value was replaced
*/
@Override
public boolean replaceFailure(K key, V value, V newValue, StoreAccessException e) {
try {
V oldValue;
try {
oldValue = loaderWriter.load(key);
} catch (Exception e1) {
throw ExceptionFactory.newCacheLoadingException(e1, e);
}
if (oldValue != null && oldValue.equals(value)) {
try {
loaderWriter.write(key, newValue);
return true;
} catch (Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
}
}
return false;
} finally {
cleanup(key, e);
}
}
Get all entries for the provided keys. Entries not found by the loader-writer are expected to be an entry
with the key and a null value.
Params: - keys – the keys being retrieved
- e – the triggered failure
Returns: a map of key-value pairs as loaded by the loader-writer
/**
* Get all entries for the provided keys. Entries not found by the loader-writer are expected to be an entry
* with the key and a null value.
*
* @param keys the keys being retrieved
* @param e the triggered failure
* @return a map of key-value pairs as loaded by the loader-writer
*/
@SuppressWarnings("unchecked")
@Override
public Map<K, V> getAllFailure(Iterable<? extends K> keys, StoreAccessException e) {
try {
return loaderWriter.loadAll((Iterable) keys); // FIXME: bad typing that we should fix
} catch(BulkCacheLoadingException e1) {
throw e1;
} catch (Exception e1) {
throw ExceptionFactory.newCacheLoadingException(e1, e);
} finally {
cleanup(keys, e);
}
}
Write all entries to the loader-writer.
Params: - entries – the entries being put
- e – the triggered failure
/**
* Write all entries to the loader-writer.
*
* @param entries the entries being put
* @param e the triggered failure
*/
@Override
public void putAllFailure(Map<? extends K, ? extends V> entries, StoreAccessException e) {
try {
loaderWriter.writeAll(entries.entrySet()); // FIXME: bad typing that we should fix
} catch(BulkCacheWritingException e1) {
throw e1;
} catch (Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
} finally {
cleanup(entries.keySet(), e);
}
}
Delete all keys from the loader-writer.
Params: - keys – the keys being removed
- e – the triggered failure
/**
* Delete all keys from the loader-writer.
*
* @param keys the keys being removed
* @param e the triggered failure
*/
@Override
public void removeAllFailure(Iterable<? extends K> keys, StoreAccessException e) {
try {
loaderWriter.deleteAll(keys);
} catch(BulkCacheWritingException e1) {
throw e1;
} catch (Exception e1) {
throw ExceptionFactory.newCacheWritingException(e1, e);
} finally {
cleanup(keys, e);
}
}
}