Search in sources :

Example 1 with InsufficientOperationalNodesException

use of voldemort.store.InsufficientOperationalNodesException in project voldemort by voldemort.

the class PerformParallelRequests method execute.

public void execute(final Pipeline pipeline) {
    List<Node> nodes = pipelineData.getNodes();
    int attempts = Math.min(preferred, nodes.size());
    final Map<Integer, Response<ByteArray, Object>> responses = new ConcurrentHashMap<Integer, Response<ByteArray, Object>>();
    final CountDownLatch latch = new CountDownLatch(attempts);
    if (logger.isTraceEnabled())
        logger.trace("Attempting " + attempts + " " + pipeline.getOperation().getSimpleName() + " operations in parallel for key " + key);
    final AtomicBoolean isResponseProcessed = new AtomicBoolean(false);
    for (int i = 0; i < attempts; i++) {
        final Node node = nodes.get(i);
        pipelineData.incrementNodeIndex();
        final long startMs = logger.isDebugEnabled() ? System.currentTimeMillis() : -1;
        NonblockingStoreCallback callback = new NonblockingStoreCallback() {

            public void requestComplete(Object result, long requestTime) {
                if (logger.isTraceEnabled())
                    logger.trace(pipeline.getOperation().getSimpleName() + " response received (" + requestTime + " ms.) from node " + node.getId() + "for key " + key);
                Response<ByteArray, Object> response = new Response<ByteArray, Object>(node, key, result, requestTime);
                if (logger.isDebugEnabled())
                    logger.debug("Finished " + pipeline.getOperation().getSimpleName() + " for key " + ByteUtils.toHexString(key.get()) + " (keyRef: " + System.identityHashCode(key) + "); started at " + startMs + " took " + requestTime + " ms on node " + node.getId() + "(" + node.getHost() + ")");
                responses.put(node.getId(), response);
                latch.countDown();
                // This reduces the window where an exception is lost
                if (isResponseProcessed.get() && response.getValue() instanceof Exception) {
                    if (response.getValue() instanceof InvalidMetadataException) {
                        pipelineData.reportException((InvalidMetadataException) response.getValue());
                        logger.warn("Received invalid metadata problem after a successful " + pipeline.getOperation().getSimpleName() + " call on node " + node.getId() + ", store '" + pipelineData.getStoreName() + "' for key " + key);
                    } else {
                        handleResponseError(response, pipeline, failureDetector);
                    }
                }
            }
        };
        if (logger.isTraceEnabled())
            logger.trace("Submitting " + pipeline.getOperation().getSimpleName() + " request on node " + node.getId() + " for key " + key);
        NonblockingStore store = nonblockingStores.get(node.getId());
        if (pipeline.getOperation() == Operation.GET)
            store.submitGetRequest(key, transforms, callback, timeoutMs);
        else if (pipeline.getOperation() == Operation.GET_VERSIONS)
            store.submitGetVersionsRequest(key, callback, timeoutMs);
        else
            throw new IllegalStateException(getClass().getName() + " does not support pipeline operation " + pipeline.getOperation());
    }
    try {
        latch.await(timeoutMs, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        if (logger.isEnabledFor(Level.WARN))
            logger.warn(e, e);
    }
    for (Response<ByteArray, Object> response : responses.values()) {
        if (response.getValue() instanceof Exception) {
            if (handleResponseError(response, pipeline, failureDetector))
                return;
        } else {
            pipelineData.incrementSuccesses();
            Response<ByteArray, V> rCast = Utils.uncheckedCast(response);
            pipelineData.getResponses().add(rCast);
            failureDetector.recordSuccess(response.getNode(), response.getRequestTime());
            pipelineData.getZoneResponses().add(response.getNode().getZoneId());
        }
    }
    isResponseProcessed.set(true);
    if (logger.isDebugEnabled())
        logger.debug("GET for key " + ByteUtils.toHexString(key.get()) + " (keyRef: " + System.identityHashCode(key) + "); successes: " + pipelineData.getSuccesses() + " preferred: " + preferred + " required: " + required);
    if (pipelineData.getSuccesses() < required) {
        if (insufficientSuccessesEvent != null) {
            pipeline.addEvent(insufficientSuccessesEvent);
        } else {
            pipelineData.setFatalError(new InsufficientOperationalNodesException(required + " " + pipeline.getOperation().getSimpleName() + "s required, but only " + pipelineData.getSuccesses() + " succeeded", pipelineData.getReplicationSet(), pipelineData.getNodes(), pipelineData.getFailedNodes(), pipelineData.getFailures()));
            pipeline.abort();
        }
    } else {
        if (pipelineData.getZonesRequired() != null) {
            int zonesSatisfied = pipelineData.getZoneResponses().size();
            if (zonesSatisfied >= (pipelineData.getZonesRequired() + 1)) {
                pipeline.addEvent(completeEvent);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Operation " + pipeline.getOperation().getSimpleName() + "failed due to insufficient zone responses, required " + pipelineData.getZonesRequired() + " obtained " + zonesSatisfied + " " + pipelineData.getZoneResponses() + " for key " + key);
                }
                if (this.insufficientZonesEvent != null) {
                    pipeline.addEvent(this.insufficientZonesEvent);
                } else {
                    pipelineData.setFatalError(new InsufficientZoneResponsesException((pipelineData.getZonesRequired() + 1) + " " + pipeline.getOperation().getSimpleName() + "s required zone, but only " + zonesSatisfied + " succeeded"));
                }
            }
        } else {
            pipeline.addEvent(completeEvent);
        }
    }
}
Also used : NonblockingStore(voldemort.store.nonblockingstore.NonblockingStore) Node(voldemort.cluster.Node) InvalidMetadataException(voldemort.store.InvalidMetadataException) CountDownLatch(java.util.concurrent.CountDownLatch) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) InsufficientZoneResponsesException(voldemort.store.InsufficientZoneResponsesException) InvalidMetadataException(voldemort.store.InvalidMetadataException) Response(voldemort.store.routed.Response) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) NonblockingStoreCallback(voldemort.store.nonblockingstore.NonblockingStoreCallback) InsufficientZoneResponsesException(voldemort.store.InsufficientZoneResponsesException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) ByteArray(voldemort.utils.ByteArray) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap)

Example 2 with InsufficientOperationalNodesException

use of voldemort.store.InsufficientOperationalNodesException in project voldemort by voldemort.

the class PerformParallelDeleteRequests method executeInternal.

private void executeInternal(final Pipeline pipeline) {
    List<Node> nodes = pipelineData.getNodes();
    final Map<Integer, Response<ByteArray, Object>> responses = new ConcurrentHashMap<Integer, Response<ByteArray, Object>>();
    int attempts = nodes.size();
    int blocks = Math.min(preferred, attempts);
    final CountDownLatch attemptsLatch = new CountDownLatch(attempts);
    final CountDownLatch blocksLatch = new CountDownLatch(blocks);
    if (logger.isTraceEnabled())
        logger.trace("Attempting " + attempts + " " + pipeline.getOperation().getSimpleName() + " operations in parallel");
    long beginTime = System.nanoTime();
    for (int i = 0; i < attempts; i++) {
        final Node node = nodes.get(i);
        pipelineData.incrementNodeIndex();
        NonblockingStoreCallback callback = new NonblockingStoreCallback() {

            public void requestComplete(Object result, long requestTime) {
                if (logger.isTraceEnabled())
                    logger.trace(pipeline.getOperation().getSimpleName() + " response received (" + requestTime + " ms.) from node " + node.getId());
                Response<ByteArray, Object> response = new Response<ByteArray, Object>(node, key, result, requestTime);
                if (logger.isTraceEnabled()) {
                    logger.trace(attemptsLatch.getCount() + " attempts remaining. Will block " + " for " + blocksLatch.getCount() + " more ");
                }
                responses.put(node.getId(), response);
                if (response.getValue() instanceof Exception && isOperationCompleted.get()) {
                    handleException(response, pipeline);
                }
                attemptsLatch.countDown();
                blocksLatch.countDown();
            }
        };
        if (logger.isTraceEnabled())
            logger.info("Submitting " + pipeline.getOperation().getSimpleName() + " request on node " + node.getId());
        NonblockingStore store = nonblockingStores.get(node.getId());
        store.submitDeleteRequest(key, version, callback, timeoutMs);
    }
    try {
        long ellapsedNs = System.nanoTime() - beginTime;
        long remainingNs = (timeoutMs * Time.NS_PER_MS) - ellapsedNs;
        if (remainingNs > 0) {
            blocksLatch.await(remainingNs, TimeUnit.NANOSECONDS);
        }
    } catch (InterruptedException e) {
        if (logger.isEnabledFor(Level.WARN))
            logger.warn(e, e);
    }
    if (processResponses(responses, pipeline))
        return;
    // wait for more responses in case we did not have enough successful
    // response to achieve the required count
    boolean quorumSatisfied = true;
    if (pipelineData.getSuccesses() < required) {
        long ellapsedNs = System.nanoTime() - beginTime;
        long remainingNs = (timeoutMs * Time.NS_PER_MS) - ellapsedNs;
        if (remainingNs > 0) {
            try {
                attemptsLatch.await(remainingNs, TimeUnit.NANOSECONDS);
            } catch (InterruptedException e) {
                if (logger.isEnabledFor(Level.WARN))
                    logger.warn(e, e);
            }
            if (processResponses(responses, pipeline))
                return;
        }
        if (pipelineData.getSuccesses() < required) {
            pipelineData.setFatalError(new InsufficientOperationalNodesException(required + " " + pipeline.getOperation().getSimpleName() + "s required, but only " + pipelineData.getSuccesses() + " succeeded", pipelineData.getReplicationSet(), pipelineData.getNodes(), pipelineData.getFailedNodes(), pipelineData.getFailures()));
            abortPipeline(pipeline);
            quorumSatisfied = false;
        }
    }
    if (quorumSatisfied) {
        if (pipelineData.getZonesRequired() != null) {
            int zonesSatisfied = pipelineData.getZoneResponses().size();
            if (zonesSatisfied >= (pipelineData.getZonesRequired() + 1)) {
                completePipeline(pipeline);
            } else {
                long timeMs = (System.nanoTime() - beginTime) / Time.NS_PER_MS;
                if ((timeoutMs - timeMs) > 0) {
                    try {
                        attemptsLatch.await(timeoutMs - timeMs, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        if (logger.isEnabledFor(Level.WARN))
                            logger.warn(e, e);
                    }
                    if (processResponses(responses, pipeline))
                        return;
                }
                if (pipelineData.getZoneResponses().size() >= (pipelineData.getZonesRequired() + 1)) {
                    completePipeline(pipeline);
                } else {
                    pipelineData.setFatalError(new InsufficientZoneResponsesException((pipelineData.getZonesRequired() + 1) + " " + pipeline.getOperation().getSimpleName() + "s required zone, but only " + zonesSatisfied + " succeeded"));
                    abortPipeline(pipeline);
                }
            }
        } else {
            completePipeline(pipeline);
        }
    }
}
Also used : NonblockingStore(voldemort.store.nonblockingstore.NonblockingStore) Node(voldemort.cluster.Node) CountDownLatch(java.util.concurrent.CountDownLatch) ObsoleteVersionException(voldemort.versioning.ObsoleteVersionException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) InsufficientZoneResponsesException(voldemort.store.InsufficientZoneResponsesException) UnreachableStoreException(voldemort.store.UnreachableStoreException) InvalidMetadataException(voldemort.store.InvalidMetadataException) QuotaExceededException(voldemort.store.quota.QuotaExceededException) Response(voldemort.store.routed.Response) NonblockingStoreCallback(voldemort.store.nonblockingstore.NonblockingStoreCallback) InsufficientZoneResponsesException(voldemort.store.InsufficientZoneResponsesException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) ByteArray(voldemort.utils.ByteArray) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap)

Example 3 with InsufficientOperationalNodesException

use of voldemort.store.InsufficientOperationalNodesException in project voldemort by voldemort.

the class PerformParallelPutRequests method execute.

@Override
public void execute(final Pipeline pipeline) {
    final Node masterNode = pipelineData.getMaster();
    final List<Node> nodes = pipelineData.getNodes();
    final Versioned<byte[]> versionedCopy = pipelineData.getVersionedCopy();
    final Integer numNodesTouchedInSerialPut = nodes.indexOf(masterNode) + 1;
    numNodesPendingResponse = nodes.size() - numNodesTouchedInSerialPut;
    if (logger.isDebugEnabled())
        logger.debug("PUT {key:" + key + "} MasterNode={id:" + masterNode.getId() + "} totalNodesToAsyncPut=" + numNodesPendingResponse);
    // initiate parallel puts
    for (int i = numNodesTouchedInSerialPut; i < nodes.size(); i++) {
        final Node node = nodes.get(i);
        pipelineData.incrementNodeIndex();
        NonblockingStoreCallback callback = new NonblockingStoreCallback() {

            @Override
            public void requestComplete(Object result, long requestTime) {
                boolean responseHandledByMaster = false;
                if (logger.isDebugEnabled())
                    logger.debug("PUT {key:" + key + "} response received from node={id:" + node.getId() + "} in " + requestTime + " ms)");
                Response<ByteArray, Object> response;
                response = new Response<ByteArray, Object>(node, key, result, requestTime);
                if (logger.isDebugEnabled()) {
                    logger.debug("PUT {key:" + key + "} Parallel put thread trying to return result to main thread");
                }
                responseHandledByMaster = pipelineData.getSynchronizer().tryDelegateResponseHandling(response);
                if (logger.isDebugEnabled()) {
                    logger.debug("PUT {key:" + key + "} Master thread accepted the response: " + responseHandledByMaster);
                }
                if (!responseHandledByMaster) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("PUT {key:" + key + "} Master thread did not accept the response: will handle in worker thread");
                    }
                    if (PipelineRoutedStore.isSlopableFailure(response.getValue()) || response.getValue() instanceof QuotaExceededException) {
                        if (logger.isDebugEnabled())
                            logger.debug("PUT {key:" + key + "} failed on node={id:" + node.getId() + ",host:" + node.getHost() + "}");
                        if (isHintedHandoffEnabled()) {
                            boolean triedDelegateSlop = pipelineData.getSynchronizer().tryDelegateSlop(node);
                            if (logger.isDebugEnabled()) {
                                logger.debug("PUT {key:" + key + "} triedDelegateSlop: " + triedDelegateSlop);
                            }
                            if (!triedDelegateSlop) {
                                Slop slop = new Slop(pipelineData.getStoreName(), Slop.Operation.PUT, key, versionedCopy.getValue(), transforms, node.getId(), new Date());
                                pipelineData.addFailedNode(node);
                                if (logger.isDebugEnabled())
                                    logger.debug("PUT {key:" + key + "} Start registering Slop(node:" + node.getId() + ",host:" + node.getHost() + ")");
                                hintedHandoff.sendHintParallel(node, versionedCopy.getVersion(), slop);
                                if (logger.isDebugEnabled())
                                    logger.debug("PUT {key:" + key + "} Sent out request to register Slop(node: " + node.getId() + ",host:" + node.getHost() + ")");
                            }
                        }
                    } else {
                        // the exception is ignorable
                        if (logger.isDebugEnabled()) {
                            if (result instanceof Exception) {
                                logger.debug("PUT {key:" + key + "} will not send hint. Response is ignorable exception: " + result.getClass().toString());
                            } else {
                                logger.debug("PUT {key:" + key + "} will not send hint. Response is success");
                            }
                        }
                    }
                    if (result instanceof Exception && !(result instanceof ObsoleteVersionException)) {
                        if (response.getValue() instanceof InvalidMetadataException) {
                            pipelineData.reportException((InvalidMetadataException) response.getValue());
                            logger.warn("Received invalid metadata problem after a successful " + pipeline.getOperation().getSimpleName() + " call on node " + node.getId() + ", store '" + pipelineData.getStoreName() + "'");
                        } else if (response.getValue() instanceof QuotaExceededException) {
                            /**
                             * TODO Not sure if we need to count this
                             * Exception for stats or silently ignore and
                             * just log a warning. While
                             * QuotaExceededException thrown from other
                             * places mean the operation failed, this one
                             * does not fail the operation but instead
                             * stores slops. Introduce a new Exception in
                             * client side to just monitor how mamy Async
                             * writes fail on exceeding Quota?
                             */
                            logger.warn("Received QuotaExceededException after a successful " + pipeline.getOperation().getSimpleName() + " call on node " + node.getId() + ", store '" + pipelineData.getStoreName() + "', master-node '" + masterNode.getId() + "'");
                        } else {
                            handleResponseError(response, pipeline, failureDetector);
                        }
                    }
                }
            }
        };
        if (logger.isTraceEnabled())
            logger.trace("Submitting " + pipeline.getOperation().getSimpleName() + " request on node " + node.getId() + " for key " + key);
        NonblockingStore store = nonblockingStores.get(node.getId());
        store.submitPutRequest(key, versionedCopy, transforms, callback, timeoutMs);
    }
    try {
        boolean preferredSatisfied = false;
        while (true) {
            long elapsedNs = System.nanoTime() - pipelineData.getStartTimeNs();
            long remainingNs = (timeoutMs * Time.NS_PER_MS) - elapsedNs;
            remainingNs = Math.max(0, remainingNs);
            // preferred check
            if (numResponsesGot >= preferred - 1) {
                preferredSatisfied = true;
            }
            quorumSatisfied = isQuorumSatisfied();
            zonesSatisfied = isZonesSatisfied();
            if (quorumSatisfied && zonesSatisfied && preferredSatisfied || remainingNs <= 0 || numNodesPendingResponse <= 0) {
                pipelineData.getSynchronizer().cutoffHandling();
                break;
            } else {
                if (logger.isTraceEnabled()) {
                    logger.trace("PUT {key:" + key + "} trying to poll from queue");
                }
                Response<ByteArray, Object> response = pipelineData.getSynchronizer().responseQueuePoll(remainingNs, TimeUnit.NANOSECONDS);
                processResponse(response, pipeline);
                if (logger.isTraceEnabled()) {
                    logger.trace("PUT {key:" + key + "} tried to poll from queue. Null?: " + (response == null) + " numResponsesGot:" + numResponsesGot + " parallelResponseToWait: " + numNodesPendingResponse + "; preferred-1: " + (preferred - 1) + "; preferredOK: " + preferredSatisfied + " quorumOK: " + quorumSatisfied + "; zoneOK: " + zonesSatisfied);
                }
            }
        }
        // leftover)
        while (!pipelineData.getSynchronizer().responseQueueIsEmpty()) {
            Response<ByteArray, Object> response = pipelineData.getSynchronizer().responseQueuePoll(0, TimeUnit.NANOSECONDS);
            processResponse(response, pipeline);
        }
        quorumSatisfied = isQuorumSatisfied();
        zonesSatisfied = isZonesSatisfied();
        if (quorumSatisfied && zonesSatisfied) {
            if (logger.isDebugEnabled()) {
                logger.debug("PUT {key:" + key + "} succeeded at parallel put stage");
            }
            pipelineData.getSynchronizer().disallowDelegateSlop();
            pipeline.addEvent(completeEvent);
        } else {
            VoldemortException fatalError;
            if (!quorumSatisfied) {
                if (logger.isDebugEnabled()) {
                    logger.debug("PUT {key:" + key + "} failed due to insufficient nodes. required=" + required + " success=" + pipelineData.getSuccesses());
                }
                fatalError = new InsufficientOperationalNodesException(required + " " + pipeline.getOperation().getSimpleName() + "s required, but only " + pipelineData.getSuccesses() + " succeeded", pipelineData.getReplicationSet(), pipelineData.getNodes(), pipelineData.getFailedNodes(), pipelineData.getFailures());
                pipelineData.setFatalError(fatalError);
            } else if (!zonesSatisfied) {
                if (logger.isDebugEnabled()) {
                    logger.debug("PUT {key:" + key + "} failed due to insufficient zones. required=" + pipelineData.getZonesRequired() + 1 + " success=" + pipelineData.getZoneResponses().size());
                }
                fatalError = new InsufficientZoneResponsesException((pipelineData.getZonesRequired() + 1) + " " + pipeline.getOperation().getSimpleName() + "s required zone, but only " + (pipelineData.getZoneResponses().size()) + " succeeded. Failing nodes : " + pipelineData.getFailedNodes());
                pipelineData.setFatalError(fatalError);
            }
            pipeline.abort();
        }
    } catch (InterruptedException e) {
        if (logger.isEnabledFor(Level.WARN))
            logger.warn(e, e);
    } catch (NoSuchElementException e) {
        if (logger.isEnabledFor(Level.ERROR))
            logger.error("Response Queue is empty. There may be a bug in PerformParallelPutRequest", e);
    } finally {
        if (logger.isDebugEnabled()) {
            logger.debug("PUT {key:" + key + "} marking parallel put stage finished");
        }
    }
}
Also used : QuotaExceededException(voldemort.store.quota.QuotaExceededException) NonblockingStore(voldemort.store.nonblockingstore.NonblockingStore) Node(voldemort.cluster.Node) InvalidMetadataException(voldemort.store.InvalidMetadataException) VoldemortException(voldemort.VoldemortException) Date(java.util.Date) ObsoleteVersionException(voldemort.versioning.ObsoleteVersionException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) InsufficientZoneResponsesException(voldemort.store.InsufficientZoneResponsesException) NoSuchElementException(java.util.NoSuchElementException) VoldemortException(voldemort.VoldemortException) InvalidMetadataException(voldemort.store.InvalidMetadataException) QuotaExceededException(voldemort.store.quota.QuotaExceededException) NonblockingStoreCallback(voldemort.store.nonblockingstore.NonblockingStoreCallback) ObsoleteVersionException(voldemort.versioning.ObsoleteVersionException) InsufficientZoneResponsesException(voldemort.store.InsufficientZoneResponsesException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) ByteArray(voldemort.utils.ByteArray) Slop(voldemort.store.slop.Slop) NoSuchElementException(java.util.NoSuchElementException)

Example 4 with InsufficientOperationalNodesException

use of voldemort.store.InsufficientOperationalNodesException in project voldemort by voldemort.

the class ThreadPoolRoutedStore method getAll.

@Override
public Map<ByteArray, List<Versioned<byte[]>>> getAll(Iterable<ByteArray> keys, Map<ByteArray, byte[]> transforms) throws VoldemortException {
    StoreUtils.assertValidKeys(keys);
    Map<ByteArray, List<Versioned<byte[]>>> result = StoreUtils.newEmptyHashMap(keys);
    // Keys for each node needed to satisfy storeDef.getPreferredReads() if
    // no failures.
    Map<Node, List<ByteArray>> nodeToKeysMap = Maps.newHashMap();
    // Keep track of nodes per key that might be needed if there are
    // failures during getAll
    Map<ByteArray, List<Node>> keyToExtraNodesMap = Maps.newHashMap();
    for (ByteArray key : keys) {
        List<Node> availableNodes = availableNodes(routingStrategy.routeRequest(key.get()));
        // quickly fail if there aren't enough nodes to meet the requirement
        checkRequiredReads(availableNodes);
        int preferredReads = storeDef.getPreferredReads();
        List<Node> preferredNodes = Lists.newArrayListWithCapacity(preferredReads);
        List<Node> extraNodes = Lists.newArrayListWithCapacity(3);
        for (Node node : availableNodes) {
            if (preferredNodes.size() < preferredReads)
                preferredNodes.add(node);
            else
                extraNodes.add(node);
        }
        for (Node node : preferredNodes) {
            List<ByteArray> nodeKeys = nodeToKeysMap.get(node);
            if (nodeKeys == null) {
                nodeKeys = Lists.newArrayList();
                nodeToKeysMap.put(node, nodeKeys);
            }
            nodeKeys.add(key);
        }
        if (!extraNodes.isEmpty()) {
            List<Node> nodes = keyToExtraNodesMap.get(key);
            if (nodes == null)
                keyToExtraNodesMap.put(key, extraNodes);
            else
                nodes.addAll(extraNodes);
        }
    }
    List<Callable<GetAllResult>> callables = Lists.newArrayList();
    for (Map.Entry<Node, List<ByteArray>> entry : nodeToKeysMap.entrySet()) {
        final Node node = entry.getKey();
        final Collection<ByteArray> nodeKeys = entry.getValue();
        if (failureDetector.isAvailable(node))
            callables.add(new GetAllCallable(node, nodeKeys, transforms));
    }
    // A list of thrown exceptions, indicating the number of failures
    List<Throwable> failures = new CopyOnWriteArrayList<Throwable>();
    List<NodeValue<ByteArray, byte[]>> nodeValues = Lists.newArrayList();
    Map<ByteArray, MutableInt> keyToSuccessCount = Maps.newHashMap();
    for (ByteArray key : keys) keyToSuccessCount.put(key, new MutableInt(0));
    List<Future<GetAllResult>> futures;
    long timeoutMs = timeoutConfig.getOperationTimeout(VoldemortOpCode.GET_ALL_OP_CODE);
    try {
        // TODO What to do about timeouts? They should be longer as getAll
        // is likely to
        // take longer. At the moment, it's just timeoutMs * 3, but should
        // this be based on the number of the keys?
        futures = executor.invokeAll(callables, timeoutMs * 3, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        throw new InsufficientOperationalNodesException("getAll operation interrupted.", e);
    }
    for (Future<GetAllResult> f : futures) {
        if (f.isCancelled()) {
            logger.warn("Get operation timed out after " + timeoutMs + " ms.");
            continue;
        }
        try {
            GetAllResult getResult = f.get();
            if (getResult.exception != null) {
                if (getResult.exception instanceof VoldemortApplicationException) {
                    throw (VoldemortException) getResult.exception;
                }
                failures.add(getResult.exception);
                continue;
            }
            for (ByteArray key : getResult.callable.nodeKeys) {
                List<Versioned<byte[]>> retrieved = getResult.retrieved.get(key);
                MutableInt successCount = keyToSuccessCount.get(key);
                successCount.increment();
                /*
                     * retrieved can be null if there are no values for the key
                     * provided
                     */
                if (retrieved != null) {
                    List<Versioned<byte[]>> existing = result.get(key);
                    if (existing == null)
                        result.put(key, Lists.newArrayList(retrieved));
                    else
                        existing.addAll(retrieved);
                }
            }
            nodeValues.addAll(getResult.nodeValues);
        } catch (InterruptedException e) {
            throw new InsufficientOperationalNodesException("getAll operation interrupted.", e);
        } catch (ExecutionException e) {
            // should never happen
            if (e.getCause() instanceof Error)
                throw (Error) e.getCause();
            else
                logger.error(e.getMessage(), e);
        }
    }
    for (ByteArray key : keys) {
        MutableInt successCountWrapper = keyToSuccessCount.get(key);
        int successCount = successCountWrapper.intValue();
        if (successCount < storeDef.getPreferredReads()) {
            List<Node> extraNodes = keyToExtraNodesMap.get(key);
            if (extraNodes != null) {
                for (Node node : extraNodes) {
                    long startNs = System.nanoTime();
                    try {
                        List<Versioned<byte[]>> values = innerStores.get(node.getId()).get(key, transforms == null ? null : transforms.get(key));
                        fillRepairReadsValues(nodeValues, key, node, values);
                        List<Versioned<byte[]>> versioneds = result.get(key);
                        if (versioneds == null)
                            result.put(key, Lists.newArrayList(values));
                        else
                            versioneds.addAll(values);
                        recordSuccess(node, startNs);
                        if (++successCount >= storeDef.getPreferredReads())
                            break;
                    } catch (UnreachableStoreException e) {
                        failures.add(e);
                        recordException(node, startNs, e);
                    } catch (VoldemortApplicationException e) {
                        throw e;
                    } catch (Exception e) {
                        logger.warn("Error in GET_ALL on node " + node.getId() + "(" + node.getHost() + ")", e);
                        failures.add(e);
                    }
                }
            }
        }
        successCountWrapper.setValue(successCount);
    }
    repairReads(nodeValues, repairReads && (transforms == null || transforms.size() == 0));
    for (Map.Entry<ByteArray, MutableInt> mapEntry : keyToSuccessCount.entrySet()) {
        int successCount = mapEntry.getValue().intValue();
        if (successCount < storeDef.getRequiredReads())
            throw new InsufficientOperationalNodesException(this.storeDef.getRequiredReads() + " reads required, but " + successCount + " succeeded.", failures);
    }
    return result;
}
Also used : Versioned(voldemort.versioning.Versioned) Node(voldemort.cluster.Node) VoldemortException(voldemort.VoldemortException) Callable(java.util.concurrent.Callable) ByteArray(voldemort.utils.ByteArray) ArrayList(java.util.ArrayList) List(java.util.List) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) UnreachableStoreException(voldemort.store.UnreachableStoreException) ExecutionException(java.util.concurrent.ExecutionException) VoldemortApplicationException(voldemort.VoldemortApplicationException) ObsoleteVersionException(voldemort.versioning.ObsoleteVersionException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) VoldemortException(voldemort.VoldemortException) VoldemortApplicationException(voldemort.VoldemortApplicationException) UnreachableStoreException(voldemort.store.UnreachableStoreException) ExecutionException(java.util.concurrent.ExecutionException) MutableInt(org.apache.commons.lang.mutable.MutableInt) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) Future(java.util.concurrent.Future) Map(java.util.Map) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList)

Example 5 with InsufficientOperationalNodesException

use of voldemort.store.InsufficientOperationalNodesException in project voldemort by voldemort.

the class ThreadPoolRoutedStore method get.

/*
     * 1. Attempt preferredReads, and then wait for these to complete 2. If we
     * got all the reads we wanted, then we are done. 3. If not then continue
     * serially attempting to read from each node until we get preferredReads or
     * run out of nodes. 4. If we have multiple results do a read repair 5. If
     * we have at least requiredReads return. Otherwise throw an exception.
     */
private <R> List<R> get(final ByteArray key, final byte[] transforms, StoreOp<R> fetcher, Function<List<GetResult<R>>, Void> preReturnProcedure) throws VoldemortException {
    StoreUtils.assertValidKey(key);
    final List<Node> nodes = availableNodes(routingStrategy.routeRequest(key.get()));
    // quickly fail if there aren't enough nodes to meet the requirement
    checkRequiredReads(nodes);
    final List<GetResult<R>> retrieved = Lists.newArrayList();
    // A count of the number of successful operations
    int successes = 0;
    // A list of thrown exceptions, indicating the number of failures
    final List<Throwable> failures = new CopyOnWriteArrayList<Throwable>();
    // Do the preferred number of reads in parallel
    int attempts = Math.min(this.storeDef.getPreferredReads(), nodes.size());
    int nodeIndex = 0;
    List<Callable<GetResult<R>>> callables = Lists.newArrayListWithCapacity(attempts);
    for (; nodeIndex < attempts; nodeIndex++) {
        final Node node = nodes.get(nodeIndex);
        callables.add(new GetCallable<R>(node, key, transforms, fetcher));
    }
    List<Future<GetResult<R>>> futures;
    long timeoutMs = (fetcher == VERSION_OP) ? timeoutConfig.getOperationTimeout(VoldemortOpCode.GET_VERSION_OP_CODE) : timeoutConfig.getOperationTimeout(VoldemortOpCode.GET_OP_CODE);
    try {
        futures = executor.invokeAll(callables, timeoutMs, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        throw new InsufficientOperationalNodesException("Get operation interrupted!", e);
    }
    for (Future<GetResult<R>> f : futures) {
        if (f.isCancelled()) {
            logger.warn("Get operation timed out after " + timeoutMs + " ms.");
            continue;
        }
        try {
            GetResult<R> getResult = f.get();
            if (getResult.exception != null) {
                if (getResult.exception instanceof VoldemortApplicationException) {
                    throw (VoldemortException) getResult.exception;
                }
                failures.add(getResult.exception);
                continue;
            }
            ++successes;
            retrieved.add(getResult);
        } catch (InterruptedException e) {
            throw new InsufficientOperationalNodesException("Get operation interrupted!", e);
        } catch (ExecutionException e) {
            // part should never happen.
            if (e.getCause() instanceof Error)
                throw (Error) e.getCause();
            else
                logger.error(e.getMessage(), e);
        }
    }
    // reads to make up for these.
    while (successes < this.storeDef.getPreferredReads() && nodeIndex < nodes.size()) {
        Node node = nodes.get(nodeIndex);
        long startNs = System.nanoTime();
        try {
            retrieved.add(new GetResult<R>(node, key, fetcher.execute(innerStores.get(node.getId()), key, transforms), null));
            ++successes;
            recordSuccess(node, startNs);
        } catch (UnreachableStoreException e) {
            failures.add(e);
            recordException(node, startNs, e);
        } catch (VoldemortApplicationException e) {
            throw e;
        } catch (Exception e) {
            logger.warn("Error in GET on node " + node.getId() + "(" + node.getHost() + ")", e);
            failures.add(e);
        }
        nodeIndex++;
    }
    if (logger.isTraceEnabled())
        logger.trace("GET retrieved the following node values: " + formatNodeValues(retrieved));
    if (preReturnProcedure != null)
        preReturnProcedure.apply(retrieved);
    if (successes >= this.storeDef.getRequiredReads()) {
        List<R> result = Lists.newArrayListWithExpectedSize(retrieved.size());
        for (GetResult<R> getResult : retrieved) result.addAll(getResult.retrieved);
        return result;
    } else
        throw new InsufficientOperationalNodesException(this.storeDef.getRequiredReads() + " reads required, but " + successes + " succeeded.", failures);
}
Also used : Node(voldemort.cluster.Node) VoldemortException(voldemort.VoldemortException) Callable(java.util.concurrent.Callable) UnreachableStoreException(voldemort.store.UnreachableStoreException) ExecutionException(java.util.concurrent.ExecutionException) VoldemortApplicationException(voldemort.VoldemortApplicationException) ObsoleteVersionException(voldemort.versioning.ObsoleteVersionException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) VoldemortException(voldemort.VoldemortException) VoldemortApplicationException(voldemort.VoldemortApplicationException) UnreachableStoreException(voldemort.store.UnreachableStoreException) ExecutionException(java.util.concurrent.ExecutionException) InsufficientOperationalNodesException(voldemort.store.InsufficientOperationalNodesException) Future(java.util.concurrent.Future) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList)

Aggregations

InsufficientOperationalNodesException (voldemort.store.InsufficientOperationalNodesException)26 Node (voldemort.cluster.Node)17 ByteArray (voldemort.utils.ByteArray)16 Test (org.junit.Test)10 VoldemortException (voldemort.VoldemortException)9 UnreachableStoreException (voldemort.store.UnreachableStoreException)7 ObsoleteVersionException (voldemort.versioning.ObsoleteVersionException)7 ArrayList (java.util.ArrayList)6 HashMap (java.util.HashMap)5 ExecutionException (java.util.concurrent.ExecutionException)5 AbstractByteArrayStoreTest (voldemort.store.AbstractByteArrayStoreTest)5 InsufficientZoneResponsesException (voldemort.store.InsufficientZoneResponsesException)5 VectorClock (voldemort.versioning.VectorClock)5 Versioned (voldemort.versioning.Versioned)5 CopyOnWriteArrayList (java.util.concurrent.CopyOnWriteArrayList)4 VoldemortApplicationException (voldemort.VoldemortApplicationException)4 VoldemortTestConstants.getNineNodeCluster (voldemort.VoldemortTestConstants.getNineNodeCluster)4 Cluster (voldemort.cluster.Cluster)4 SerializerDefinition (voldemort.serialization.SerializerDefinition)4 FailingReadsStore (voldemort.store.FailingReadsStore)4