package voldemort.store.pausable; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import voldemort.VoldemortException; import voldemort.annotations.jmx.JmxOperation; import voldemort.store.AbstractStorageEngine; import voldemort.store.StoreCapabilityType; import voldemort.store.memory.InMemoryStorageEngine; import voldemort.utils.ClosableIterator; import voldemort.utils.Pair; import voldemort.versioning.Version; import voldemort.versioning.Versioned; /** * A storage engine that can be paused to simulate a failure for testing. While * paused all operations on the store will block indefinitely. The methods to * pause and unpause are also exposed through JMX. * * * @param <K> The type of the key * @param <V> The type of the value * @param <T> The type of the transforms */ public class PausableStorageEngine<K, V, T> extends AbstractStorageEngine<K, V, T> { private static final Logger logger = Logger.getLogger(PausableStorageEngine.class); private final InMemoryStorageEngine<K, V, T> inner; private final Object condition = new Object(); private volatile boolean paused; public PausableStorageEngine(InMemoryStorageEngine<K, V, T> inner) { super(inner.getName()); this.inner = inner; } @Override public void close() throws VoldemortException { inner.close(); } @Override public boolean delete(K key, Version version) { blockIfNecessary(); return inner.delete(key); } private void blockIfNecessary() { synchronized(condition) { while(paused) { try { condition.wait(); } catch(InterruptedException e) { throw new VoldemortException("Pausable store interrupted while paused."); } } } } @Override public List<Versioned<V>> get(K key, T transforms) { blockIfNecessary(); return inner.get(key, transforms); } @Override public Map<K, List<Versioned<V>>> getAll(Iterable<K> keys, Map<K, T> transforms) { blockIfNecessary(); return inner.getAll(keys, transforms); } @Override public void put(K key, Versioned<V> value, T transforms) { blockIfNecessary(); inner.put(key, value, transforms); } @Override public List<Versioned<V>> multiVersionPut(K key, final List<Versioned<V>> values) { blockIfNecessary(); return inner.multiVersionPut(key, values); } @Override public ClosableIterator<Pair<K, Versioned<V>>> entries() { blockIfNecessary(); return inner.entries(); } @Override public ClosableIterator<K> keys() { blockIfNecessary(); return inner.keys(); } @Override public ClosableIterator<Pair<K, Versioned<V>>> entries(int partition) { blockIfNecessary(); return inner.entries(partition); } @Override public ClosableIterator<K> keys(int partition) { blockIfNecessary(); return inner.keys(partition); } @Override public void truncate() { blockIfNecessary(); inner.deleteAll(); } @Override public List<Version> getVersions(K key) { blockIfNecessary(); return inner.getVersions(key); } @Override public Object getCapability(StoreCapabilityType capability) { return inner.getCapability(capability); } @JmxOperation(description = "Pause all operations on the storage engine.") public void pause() { logger.info("Pausing store '" + getName() + "'."); paused = true; } @JmxOperation(description = "Unpause the storage engine.") public void unpause() { logger.info("Unpausing store '" + getName() + "'."); paused = false; synchronized(condition) { condition.notifyAll(); } } @Override public boolean isPartitionAware() { return inner.isPartitionAware(); } @Override public boolean isPartitionScanSupported() { return inner.isPartitionScanSupported(); } }