/*
* Copyright 2012 LinkedIn, 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 voldemort.store.slow;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import voldemort.VoldemortException;
import voldemort.common.OpTimeMap;
import voldemort.common.VoldemortOpCode;
import voldemort.store.AbstractStorageEngine;
import voldemort.store.StorageEngine;
import voldemort.store.StoreCapabilityType;
import voldemort.utils.ClosableIterator;
import voldemort.utils.Pair;
import voldemort.versioning.Version;
import voldemort.versioning.Versioned;
/**
* A wrapped version of InMemoryStorageEngine that can add delay to each
* operation type. Useful for unit testing.
*
* Any operation with queueingDelays of more than 0 ms blocks until it has
* slept. It will queue up behind other queuingDelays operations that need to
* sleep first.
*
* Any operation with concurrentDelays of more than 0 ms sleeps for the
* specified amount of time. This delay does not block other operations. I.e.,
* multiple operations sleep for the specified concurrentDelays simultaneously.
*
* Both queueingDelays and concurrentDelays may be specified for each operation.
* queueingDelays are done before concurrentDelays; time spent in queueingDelays
* does not affect concurrentDelays.
*
*/
public class SlowStorageEngine<K, V, T> extends AbstractStorageEngine<K, V, T> {
private final StorageEngine<K, V, T> innerStorageEngine;
private final OpTimeMap queueingDelays;
private final OpTimeMap concurrentDelays;
public SlowStorageEngine(StorageEngine<K, V, T> innerStorageEngine) {
this(innerStorageEngine, new OpTimeMap(0), new OpTimeMap(0));
}
public SlowStorageEngine(StorageEngine<K, V, T> innerStorageEngine,
OpTimeMap queueingDelays,
OpTimeMap concurrentDelays) {
super(innerStorageEngine.getName());
this.innerStorageEngine = innerStorageEngine;
this.queueingDelays = queueingDelays;
this.concurrentDelays = concurrentDelays;
}
private synchronized void queueingSleep(long ms) {
try {
TimeUnit.MILLISECONDS.sleep(ms);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
private void concurrentSleep(long ms) {
try {
TimeUnit.MILLISECONDS.sleep(ms);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
private void delayByOp(byte opCode) {
if(queueingDelays.getOpTime(opCode) > 0)
queueingSleep(queueingDelays.getOpTime(opCode));
if(concurrentDelays.getOpTime(opCode) > 0)
concurrentSleep(concurrentDelays.getOpTime(opCode));
}
public boolean delete(K key) {
return delete(key, null);
}
@Override
public boolean delete(K key, Version version) {
delayByOp(VoldemortOpCode.DELETE_OP_CODE);
return innerStorageEngine.delete(key, version);
}
@Override
public List<Version> getVersions(K key) {
delayByOp(VoldemortOpCode.GET_VERSION_OP_CODE);
return innerStorageEngine.getVersions(key);
}
@Override
public List<Versioned<V>> get(K key, T transform) throws VoldemortException {
delayByOp(VoldemortOpCode.GET_OP_CODE);
return innerStorageEngine.get(key, transform);
}
@Override
public Map<K, List<Versioned<V>>> getAll(Iterable<K> keys, Map<K, T> transforms)
throws VoldemortException {
delayByOp(VoldemortOpCode.GET_ALL_OP_CODE);
return innerStorageEngine.getAll(keys, transforms);
}
@Override
public void put(K key, Versioned<V> value, T transforms) throws VoldemortException {
delayByOp(VoldemortOpCode.PUT_OP_CODE);
innerStorageEngine.put(key, value, transforms);
}
@Override
public List<Versioned<V>> multiVersionPut(K key, final List<Versioned<V>> values) {
delayByOp(VoldemortOpCode.PUT_OP_CODE);
return innerStorageEngine.multiVersionPut(key, values);
}
@Override
public ClosableIterator<Pair<K, Versioned<V>>> entries() {
return innerStorageEngine.entries();
}
@Override
public ClosableIterator<K> keys() {
return innerStorageEngine.keys();
}
@Override
public void truncate() {
innerStorageEngine.truncate();
}
@Override
public boolean isPartitionAware() {
return innerStorageEngine.isPartitionAware();
}
@Override
public void close() {
innerStorageEngine.close();
}
@Override
public Object getCapability(StoreCapabilityType capability) {
return innerStorageEngine.getCapability(capability);
}
@Override
public ClosableIterator<Pair<K, Versioned<V>>> entries(int partition) {
return innerStorageEngine.entries(partition);
}
@Override
public ClosableIterator<K> keys(int partition) {
return innerStorageEngine.keys(partition);
}
@Override
public boolean isPartitionScanSupported() {
return innerStorageEngine.isPartitionScanSupported();
}
}