Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.hbase.client;
import java.util.ArrayList;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A completion service for the RpcRetryingCallerFactory. Keeps the list of the futures, and allows
* to cancel them all. This means as well that it can be used for a small set of tasks only.
* Implementation is not Thread safe. CompletedTasks is implemented as a queue, the entry is added
* based on the time order. I.e, when the first task completes (whether it is a success or failure),
* it is added as a first entry in the queue, the next completed task is added as a second entry in
* the queue, ... When iterating through the queue, we know it is based on time order. If the first
* completed task succeeds, it is returned. If it is failure, the iteration goes on until it finds a
* success.
*/
@InterfaceAudience.Private
public class ResultBoundedCompletionService {
private static final Logger LOG = LoggerFactory.getLogger(ResultBoundedCompletionService.class);
private final RpcRetryingCallerFactory retryingCallerFactory;
private final Executor executor;
private final QueueingFuture[] tasks; // all the tasks
private final ArrayList completedTasks; // completed tasks
private volatile boolean cancelled = false;
class QueueingFuture implements RunnableFuture {
private final RetryingCallable future;
private T result = null;
private ExecutionException exeEx = null;
private volatile boolean cancelled = false;
private final int operationTimeout;
private final RpcRetryingCaller retryingCaller;
private boolean resultObtained = false;
private final int replicaId; // replica id
public QueueingFuture(RetryingCallable future, int rpcTimeout, int operationTimeout,
int id) {
this.future = future;
this.operationTimeout = operationTimeout;
this.retryingCaller = retryingCallerFactory. newCaller(rpcTimeout);
this.replicaId = id;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
try {
if (!cancelled) {
result = this.retryingCaller.callWithRetries(future, operationTimeout);
resultObtained = true;
}
} catch (Throwable t) {
exeEx = new ExecutionException(t);
} finally {
synchronized (tasks) {
// If this wasn't canceled then store the result.
if (!cancelled) {
completedTasks.add(QueueingFuture.this);
}
// Notify just in case there was someone waiting and this was canceled.
// That shouldn't happen but better safe than sorry.
tasks.notify();
}
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (resultObtained || exeEx != null) return false;
retryingCaller.cancel();
if (future instanceof Cancellable) ((Cancellable) future).cancel();
cancelled = true;
return true;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public boolean isDone() {
return resultObtained || exeEx != null;
}
@Override
public T get() throws InterruptedException, ExecutionException {
try {
return get(1000, TimeUnit.DAYS);
} catch (TimeoutException e) {
throw new RuntimeException("You did wait for 1000 days here?", e);
}
}
@Override
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
synchronized (tasks) {
if (resultObtained) {
return result;
}
if (exeEx != null) {
throw exeEx;
}
unit.timedWait(tasks, timeout);
}
if (resultObtained) {
return result;
}
if (exeEx != null) {
throw exeEx;
}
throw new TimeoutException("timeout=" + timeout + ", " + unit);
}
public int getReplicaId() {
return replicaId;
}
public ExecutionException getExeEx() {
return exeEx;
}
}
@SuppressWarnings("unchecked")
public ResultBoundedCompletionService(RpcRetryingCallerFactory retryingCallerFactory,
Executor executor, int maxTasks) {
this.retryingCallerFactory = retryingCallerFactory;
this.executor = executor;
this.tasks = new QueueingFuture[maxTasks];
this.completedTasks = new ArrayList<>(maxTasks);
}
public void submit(RetryingCallable task, int rpcTimeout, int operationTimeout, int id) {
QueueingFuture newFuture = new QueueingFuture<>(task, rpcTimeout, operationTimeout, id);
// remove trace for runnable because HBASE-25373 and OpenTelemetry do not cover TraceRunnable
executor.execute(newFuture);
tasks[id] = newFuture;
}
public QueueingFuture take() throws InterruptedException {
synchronized (tasks) {
while (!cancelled && (completedTasks.size() < 1))
tasks.wait();
}
return completedTasks.get(0);
}
/**
* Poll for the first completed task whether it is a success or execution exception.
* @param timeout - time to wait before it times out
* @param unit - time unit for timeout
*/
public QueueingFuture poll(long timeout, TimeUnit unit) throws InterruptedException {
return pollForSpecificCompletedTask(timeout, unit, 0);
}
/**
* Poll for the first successfully completed task whose completed order is in startIndex,
* endIndex(exclusive) range
* @param timeout - time to wait before it times out
* @param unit - time unit for timeout
* @param startIndex - start index, starting from 0, inclusive
* @param endIndex - end index, exclusive
* @return If within timeout time, there is no successfully completed task, return null; If all
* tasks get execution exception, it will throw out the last execution exception,
* otherwise return the first successfully completed task's result.
*/
public QueueingFuture pollForFirstSuccessfullyCompletedTask(long timeout, TimeUnit unit,
int startIndex, int endIndex)
throws InterruptedException, CancellationException, ExecutionException {
QueueingFuture f;
long start, duration;
for (int i = startIndex; i < endIndex; i++) {
start = EnvironmentEdgeManager.currentTime();
f = pollForSpecificCompletedTask(timeout, unit, i);
duration = EnvironmentEdgeManager.currentTime() - start;
// Even with operationTimeout less than 0, still loop through the rest as there could
// be other completed tasks before operationTimeout.
timeout -= duration;
if (f == null) {
return null;
} else if (f.getExeEx() != null) {
// we continue here as we need to loop through all the results.
if (LOG.isDebugEnabled()) {
LOG.debug("Replica " + ((f == null) ? 0 : f.getReplicaId()) + " returns "
+ f.getExeEx().getCause());
}
if (i == (endIndex - 1)) {
// Rethrow this exception
throw f.getExeEx();
}
continue;
}
return f;
}
return null;
}
/**
* Poll for the Nth completed task (index starts from 0 (the 1st), 1 (the second)...)
* @param timeout - time to wait before it times out
* @param unit - time unit for timeout
* @param index - the index(th) completed task, index starting from 0
*/
private QueueingFuture pollForSpecificCompletedTask(long timeout, TimeUnit unit, int index)
throws InterruptedException {
if (index < 0) {
return null;
}
synchronized (tasks) {
if (!cancelled && (completedTasks.size() <= index)) unit.timedWait(tasks, timeout);
if (completedTasks.size() <= index) return null;
}
return completedTasks.get(index);
}
public void cancelAll() {
// Grab the lock on tasks so that cancelled is visible everywhere
synchronized (tasks) {
cancelled = true;
}
for (QueueingFuture future : tasks) {
if (future != null) future.cancel(true);
}
}
}