/*
* 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.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.mutable.MutableInt;
import voldemort.cluster.Node;
import voldemort.cluster.failuredetector.FailureDetector;
import voldemort.store.InsufficientOperationalNodesException;
import voldemort.store.Store;
import voldemort.store.routed.GetAllPipelineData;
import voldemort.store.routed.Pipeline;
import voldemort.store.routed.Pipeline.Event;
import voldemort.store.routed.Response;
import voldemort.utils.ByteArray;
import voldemort.utils.ByteUtils;
import voldemort.utils.Time;
import voldemort.versioning.Versioned;
import com.google.common.collect.Lists;
public class PerformSerialGetAllRequests
extends
AbstractAction<Iterable<ByteArray>, Map<ByteArray, List<Versioned<byte[]>>>, GetAllPipelineData> {
private final Iterable<ByteArray> keys;
private final FailureDetector failureDetector;
private final Map<Integer, Store<ByteArray, byte[], byte[]>> stores;
private final int preferred;
private final int required;
private final boolean allowPartial;
public PerformSerialGetAllRequests(GetAllPipelineData pipelineData,
Event completeEvent,
Iterable<ByteArray> keys,
FailureDetector failureDetector,
Map<Integer, Store<ByteArray, byte[], byte[]>> stores,
int preferred,
int required,
boolean allowPartial) {
super(pipelineData, completeEvent);
this.keys = keys;
this.failureDetector = failureDetector;
this.stores = stores;
this.preferred = preferred;
this.required = required;
this.allowPartial = allowPartial;
}
public void execute(Pipeline pipeline) {
Map<ByteArray, List<Versioned<byte[]>>> result = pipelineData.getResult();
for(ByteArray key: keys) {
boolean zoneRequirement = false;
MutableInt successCount = pipelineData.getSuccessCount(key);
if(logger.isDebugEnabled())
logger.debug("GETALL for key " + ByteUtils.toHexString(key.get()) + " (keyRef: "
+ System.identityHashCode(key) + ") successes: "
+ successCount.intValue() + " preferred: " + preferred + " required: "
+ required);
if(successCount.intValue() >= preferred) {
if(pipelineData.getZonesRequired() != null && pipelineData.getZonesRequired() > 0) {
if(pipelineData.getKeyToZoneResponse().containsKey(key)) {
int zonesSatisfied = pipelineData.getKeyToZoneResponse().get(key).size();
if(zonesSatisfied >= (pipelineData.getZonesRequired() + 1)) {
continue;
} else {
zoneRequirement = true;
}
} else {
zoneRequirement = true;
}
} else {
continue;
}
}
List<Node> extraNodes = pipelineData.getKeyToExtraNodesMap().get(key);
Map<ByteArray, byte[]> transforms = pipelineData.getTransforms();
if(extraNodes == null)
continue;
for(Node node: extraNodes) {
long start = System.nanoTime();
try {
Store<ByteArray, byte[], byte[]> store = stores.get(node.getId());
List<Versioned<byte[]>> values;
if(transforms == null)
values = store.get(key, null);
else
values = store.get(key, transforms.get(key));
if(values.size() != 0) {
if(result.get(key) == null)
result.put(key, Lists.newArrayList(values));
else
result.get(key).addAll(values);
}
Map<ByteArray, List<Versioned<byte[]>>> map = new HashMap<ByteArray, List<Versioned<byte[]>>>();
map.put(key, values);
Response<Iterable<ByteArray>, Map<ByteArray, List<Versioned<byte[]>>>> response = new Response<Iterable<ByteArray>, Map<ByteArray, List<Versioned<byte[]>>>>(node,
Arrays.asList(key),
map,
((System.nanoTime() - start) / Time.NS_PER_MS));
successCount.increment();
pipelineData.getResponses().add(response);
failureDetector.recordSuccess(response.getNode(), response.getRequestTime());
if(logger.isDebugEnabled())
logger.debug("GET for key " + ByteUtils.toHexString(key.get())
+ " (keyRef: " + System.identityHashCode(key)
+ ") successes: " + successCount.intValue() + " preferred: "
+ preferred + " required: " + required
+ " new GET success on node " + node.getId());
HashSet<Integer> zoneResponses = null;
if(pipelineData.getKeyToZoneResponse().containsKey(key)) {
zoneResponses = pipelineData.getKeyToZoneResponse().get(key);
} else {
zoneResponses = new HashSet<Integer>();
pipelineData.getKeyToZoneResponse().put(key, zoneResponses);
}
zoneResponses.add(response.getNode().getZoneId());
if(zoneRequirement) {
if(zoneResponses.size() >= pipelineData.getZonesRequired())
break;
} else {
if(successCount.intValue() >= preferred)
break;
}
} catch(Exception e) {
long requestTime = (System.nanoTime() - start) / Time.NS_PER_MS;
if(handleResponseError(e, node, requestTime, pipeline, failureDetector))
return;
}
}
}
for(ByteArray key: keys) {
MutableInt successCount = pipelineData.getSuccessCount(key);
if(successCount.intValue() < required) {
// if we allow partial results, then just remove keys that did
// not meet 'required' guarantee; else raise error
if(allowPartial) {
if(logger.isDebugEnabled()) {
logger.debug("Excluding Key " + ByteUtils.toHexString(key.get())
+ " from partial get_all result");
}
result.remove(key);
} else {
pipelineData.setFatalError(new InsufficientOperationalNodesException(required
+ " "
+ pipeline.getOperation()
.getSimpleName()
+ "s required, but "
+ successCount.intValue()
+ " succeeded. Failing nodes : "
+ pipelineData.getFailedNodes(),
pipelineData.getFailures()));
pipeline.addEvent(Event.ERROR);
return;
}
}
}
pipeline.addEvent(completeEvent);
}
}