/*
* Copyright 2010 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.routed.action;
import java.util.List;
import java.util.Map;
import voldemort.VoldemortApplicationException;
import voldemort.cluster.Node;
import voldemort.store.nonblockingstore.NonblockingStore;
import voldemort.store.routed.NodeValue;
import voldemort.store.routed.Pipeline;
import voldemort.store.routed.Pipeline.Event;
import voldemort.store.routed.PipelineData;
import voldemort.store.routed.ReadRepairer;
import voldemort.utils.ByteArray;
import voldemort.utils.ByteUtils;
import voldemort.versioning.VectorClock;
import voldemort.versioning.Versioned;
import com.google.common.collect.Lists;
public abstract class AbstractReadRepair<K, V, PD extends PipelineData<K, V>> extends
AbstractAction<K, V, PD> {
private final int preferred;
private final long timeoutMs;
private final Map<Integer, NonblockingStore> nonblockingStores;
private final ReadRepairer<ByteArray, byte[]> readRepairer;
private final List<NodeValue<ByteArray, byte[]>> nodeValues;
public AbstractReadRepair(PD pipelineData,
Event completeEvent,
int preferred,
long timeoutMs,
Map<Integer, NonblockingStore> nonblockingStores,
ReadRepairer<ByteArray, byte[]> readRepairer) {
super(pipelineData, completeEvent);
this.preferred = preferred;
this.timeoutMs = timeoutMs;
this.nonblockingStores = nonblockingStores;
this.readRepairer = readRepairer;
this.nodeValues = Lists.newArrayListWithExpectedSize(pipelineData.getResponses().size());
}
protected abstract void insertNodeValues();
protected void insertNodeValue(Node node, ByteArray key, List<Versioned<byte[]>> value) {
if(value.size() == 0) {
Versioned<byte[]> versioned = new Versioned<byte[]>(null);
nodeValues.add(new NodeValue<ByteArray, byte[]>(node.getId(), key, versioned));
} else {
for(Versioned<byte[]> versioned: value)
nodeValues.add(new NodeValue<ByteArray, byte[]>(node.getId(), key, versioned));
}
}
public void execute(Pipeline pipeline) {
insertNodeValues();
long startTimeNs = -1;
if(logger.isDebugEnabled())
startTimeNs = System.nanoTime();
if(nodeValues.size() > 1 && preferred > 1) {
List<NodeValue<ByteArray, byte[]>> toReadRepair = Lists.newArrayList();
/*
* We clone after computing read repairs in the assumption that the
* output will be smaller than the input. Note that we clone the
* version, but not the key or value as the latter two are not
* mutated.
*/
for(NodeValue<ByteArray, byte[]> v: readRepairer.getRepairs(nodeValues)) {
Versioned<byte[]> versioned = Versioned.value(v.getVersioned().getValue(),
((VectorClock) v.getVersion()).clone());
toReadRepair.add(new NodeValue<ByteArray, byte[]>(v.getNodeId(),
v.getKey(),
versioned));
}
for(NodeValue<ByteArray, byte[]> v: toReadRepair) {
try {
if(logger.isDebugEnabled())
logger.debug("Doing read repair on node " + v.getNodeId() + " for key '"
+ ByteUtils.toHexString(v.getKey().get()) + "' with version "
+ v.getVersion() + ".");
NonblockingStore store = nonblockingStores.get(v.getNodeId());
store.submitPutRequest(v.getKey(), v.getVersioned(), null, null, timeoutMs);
} catch(VoldemortApplicationException e) {
if(logger.isDebugEnabled())
logger.debug("Read repair cancelled due to application level exception on node "
+ v.getNodeId()
+ " for key '"
+ ByteUtils.toHexString(v.getKey().get())
+ "' with version "
+ v.getVersion() + ": " + e.getMessage());
} catch(Exception e) {
logger.debug("Read repair failed: ", e);
}
}
if(logger.isDebugEnabled()) {
String logStr = "Repaired (node, key, version): (";
for(NodeValue<ByteArray, byte[]> v: toReadRepair) {
logStr += "(" + v.getNodeId() + ", " + v.getKey() + "," + v.getVersion() + ") ";
}
logStr += "in " + (System.nanoTime() - startTimeNs) + " ns";
logger.debug(logStr);
}
}
pipeline.addEvent(completeEvent);
}
}