com.yahoo.maha.parrequest2.future.ParallelServiceExecutor Maven / Gradle / Ivy
// Copyright 2017, Yahoo Holdings Inc.
// Licensed under the terms of the Apache License 2.0. Please see LICENSE file in project root for terms.
package com.yahoo.maha.parrequest2.future;
import java.util.function.Function;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.yahoo.maha.parrequest2.CustomRejectPolicy;
import com.yahoo.maha.parrequest2.ParCallable;
import scala.util.Either;
import scala.util.Right;
import com.yahoo.maha.parrequest2.GeneralError;
import scala.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
public class ParallelServiceExecutor {
private int threadPoolSize = 100;
private int defaultTimeoutMillis = 10000;
private String poolName = "pse";
private int queueSize = 100;
private RejectedExecutionHandler rejectedExecutionHandler = new CustomRejectPolicy();
private static Logger logger = LoggerFactory.getLogger(ParallelServiceExecutor.class);
private ListeningExecutorService threadService;
public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) {
Preconditions.checkArgument(rejectedExecutionHandler != null, "rejectedExecutionHandler cannot be null");
this.rejectedExecutionHandler = rejectedExecutionHandler;
}
public void setPoolName(String poolName) {
Preconditions.checkArgument(poolName != null, "Pool name cannot be null");
this.poolName = poolName;
}
public void setDefaultTimeoutMillis(int defaultTimeoutMillis) {
this.defaultTimeoutMillis = defaultTimeoutMillis;
}
public void setThreadPoolSize(int threadPoolSize) {
Preconditions.checkArgument(threadPoolSize > 1, "Pool size must be > 1");
this.threadPoolSize = threadPoolSize;
}
public void setQueueSize(int queueSize) {
Preconditions.checkArgument(queueSize > 1, "Queue size must be > 1");
this.queueSize = queueSize;
}
public int getThreadPoolSize() {
return this.threadPoolSize;
}
public void init() throws Exception {
ExecutorService executorService = new ThreadPoolExecutor(this.threadPoolSize, this.threadPoolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(queueSize),
new ThreadFactoryBuilder()
.setNameFormat(poolName + "-%d").build(),
rejectedExecutionHandler
);
threadService = MoreExecutors.listeningDecorator(executorService);
}
public void destroy() throws Exception {
threadService.shutdown();
}
public List execute(List> callables) throws Exception {
return execute(callables, defaultTimeoutMillis);
}
public List execute(List> callables, int timeoutMillis) throws Exception {
return execute(callables, timeoutMillis, true, null);
}
public List execute(List> callables, Function generateDefaultValue)
throws Exception {
return execute(callables, defaultTimeoutMillis, false, generateDefaultValue);
}
public List execute(List> callables, boolean failFast,
Function generateDefaultValue) throws Exception {
return execute(callables, defaultTimeoutMillis, failFast, generateDefaultValue);
}
public List execute(List> callables, int timeoutMillis, boolean failFast,
Function generateDefaultValue) throws Exception {
List> futureResultList = null;
List resultList = new ArrayList();
try {
if (callables != null && !callables.isEmpty()) {
futureResultList = threadService.invokeAll(callables, timeoutMillis, TimeUnit.MILLISECONDS);
}
if (futureResultList != null && !futureResultList.isEmpty()) {
for (Future future : futureResultList) {
try {
T output = future.get();
if (output != null) {
resultList.add(output);
} else {
logger.warn("The result of a future was null!");
}
logger.info("is future done: " + future.isDone() + " is cancelled: " + future.isCancelled());
logger.info("future get response: " + future.get());
} catch (CancellationException | ExecutionException | InterruptedException e) {
if (failFast) {
throw e;
} else {
logger.warn("CancellationException/Interrupted/Execution exception: ", e);
if (generateDefaultValue != null) {
resultList.add(generateDefaultValue.apply(e));
}
}
}
}
} else {
logger.error("futureResultList is empty");
}
return resultList;
} catch (Exception e) {
logger.error("Execution exception: ", e);
throw new Exception("UNKNOWN_SERVER_ERROR", e);
}
}
@Deprecated
public Either produceResult(ParCallable> callable) {
try {
ListenableFuture> results = threadService.submit(callable);
return results.get();
} catch (Exception e) {
logger.error("Parallel execution error", e);
return GeneralError.either("produceResult", "execution failed", e);
}
}
@Deprecated
public ListenableFuture> asyncProduceResult(
ParCallable> callable) {
try {
ListenableFuture> results = threadService.submit(callable);
return results;
} catch (Exception e) {
logger.error("Parallel execution error", e);
return Futures.>immediateFuture(
GeneralError.either("asyncProduceResult", "execution failed", e));
}
}
ListenableFuture> submitParCallable(ParCallable> callable) {
try {
ListenableFuture> results = threadService.submit(callable);
return results;
} catch (Exception e) {
logger.error("Parallel execution error", e);
return Futures.>immediateFuture(
GeneralError.either("asyncProduceResult", "execution failed", e));
}
}
/**
* Add a listener to given future which executes the given runnable, backed by internal executor
*/
public void addListener(ListenableFuture future, Runnable runnable) {
future.addListener(runnable, threadService);
}
/**
* Create a builder for ParRequest backed by internal executor
*/
public ParRequest.Builder parRequestBuilder() {
return new ParRequest.Builder(this);
}
/**
* Create a builder for ParRequest2 backed by internal executor
*/
public ParRequest2.Builder parRequest2Builder() {
return new ParRequest2.Builder(this);
}
/**
* Create a builder for ParRequest2 backed by internal executor
*/
public ParRequest2Option.Builder parRequest2OptionBuilder() {
return new ParRequest2Option.Builder(this);
}
/**
* Create a builder for ParRequest3 backed by internal executor
*/
public ParRequest3.Builder parRequest3Builder() {
return new ParRequest3.Builder(this);
}
/**
* Create a builder for ParRequest3Option backed by internal executor
*/
public ParRequest3Option.Builder parRequest3OptionBuilder() {
return new ParRequest3Option.Builder(this);
}
/**
* Create a builder for ParRequest4 backed by internal executor
*/
public ParRequest4.Builder parRequest4Builder() {
return new ParRequest4.Builder(this);
}
/**
* Create a builder for ParRequest5 backed by internal executor
*/
public ParRequest5.Builder parRequest5Builder() {
return new ParRequest5.Builder(this);
}
/**
* Create a builder for ParRequest6 backed by internal executor
*/
public ParRequest6.Builder parRequest6Builder() {
return new ParRequest6.Builder(this);
}
public ParRequestListOption.Builder parRequestListOptionBuilder() {
return new ParRequestListOption.Builder(this);
}
public ParRequestListEither.Builder parRequestListEitherBuilder() {
return new ParRequestListEither.Builder(this);
}
/**
* Combine list of combinable requests, meaning the result ParRequestListOption will finish when all combinable requests in the list finish
*/
public ParRequestListOption combineList(
final List> requestList) {
return combineList(requestList, false);
}
/**
* Combine list of combinable requests, meaning the result ParRequestListOption will finish when all combinable requests in the list finish
*/
public ParRequestListOption combineList(
final List> requestList, boolean allMustSucceed) {
String joinedLabel = requestList.stream().map(x -> x.label).collect(Collectors.joining("-"));
return new ParRequestListOption(joinedLabel, this, new ArrayList>(requestList), allMustSucceed);
}
/**
* Combine list of combinable requests, meaning the result ParRequestListEither will finish when all combinable requests in the list finish
*/
public ParRequestListEither combineListEither(
final List> requestList) {
return combineListEither(requestList, false);
}
/**
* Combine list of combinable requests, meaning the result ParRequestListOption will finish when all combinable requests in the list finish
*/
public ParRequestListEither combineListEither(
final List> requestList, boolean allMustSucceed) {
String joinedLabel = requestList.stream().map(x -> x.label).collect(Collectors.joining("-"));
return new ParRequestListEither(joinedLabel, this, new ArrayList>(requestList), allMustSucceed);
}
/**
* Combine 2 combinable requests, meaning the result ParRequest2 will finish when the 2 combinable requests finish
*/
public ParRequest2 combine2(
final CombinableRequest firstRequest,
final CombinableRequest secondRequest) {
return new ParRequest2(firstRequest.label + secondRequest.label, this, firstRequest, secondRequest);
}
/**
* Combine 2 combinable requests meaning the result ParRequest2Option will finish when the 2 combinable requests
* finish. The result is optionally available, meaning available if future completed without error.
*/
public ParRequest2Option optionalCombine2(
final CombinableRequest firstRequest,
final CombinableRequest secondRequest) {
return new ParRequest2Option(firstRequest.label + secondRequest.label, this, firstRequest, secondRequest,
false);
}
/**
* Combine 3 combinable requests, meaning the result ParRequest3 will finish when the 3 combinable requests finish
*/
public ParRequest3 combine3(
final CombinableRequest firstRequest,
final CombinableRequest secondRequest,
final CombinableRequest thirdRequest) {
return new ParRequest3(firstRequest.label + secondRequest.label + thirdRequest.label, this,
firstRequest, secondRequest, thirdRequest);
}
/**
* Combine 3 combinable requests meaning the result ParRequest3Option will finish when the 3 combinable requests
* finish. The result is optionally available, meaning available if future completed without error.
*/
public ParRequest3Option optionalCombine3(
final CombinableRequest firstRequest,
final CombinableRequest secondRequest,
final CombinableRequest thirdRequest) {
return new ParRequest3Option(firstRequest.label + secondRequest.label + thirdRequest.label, this,
firstRequest, secondRequest, thirdRequest, false);
}
/**
* Combine 4 combinable requests, meaning the result ParRequest4 will finish when the 4 combinable requests finish
*/
public ParRequest4 combine4(
final CombinableRequest firstRequest,
final CombinableRequest secondRequest,
final CombinableRequest thirdRequest,
final CombinableRequest fourthRequest) {
return new ParRequest4(firstRequest.label + secondRequest.label + thirdRequest.label + fourthRequest.label, this,
firstRequest, secondRequest, thirdRequest, fourthRequest);
}
/**
* Combine 5 combinable requests, meaning the result ParRequest4 will finish when the 5 combinable requests finish
*/
public ParRequest5 combine5(
final CombinableRequest firstRequest,
final CombinableRequest secondRequest,
final CombinableRequest thirdRequest,
final CombinableRequest fourthRequest,
final CombinableRequest fifthRequest) {
return new ParRequest5(firstRequest.label + secondRequest.label + thirdRequest.label + fourthRequest.label + fifthRequest.label, this,
firstRequest, secondRequest, thirdRequest, fourthRequest, fifthRequest);
}
/**
* Combine 6 combinable requests, meaning the result ParRequest6 will finish when the 6 combinable requests finish
*/
public ParRequest6 combine6(
final CombinableRequest firstRequest,
final CombinableRequest secondRequest,
final CombinableRequest thirdRequest,
final CombinableRequest fourthRequest,
final CombinableRequest fifthRequest,
final CombinableRequest sixthRequest) {
return new ParRequest6(firstRequest.label + secondRequest.label + thirdRequest.label + fourthRequest.label + fifthRequest.label + sixthRequest.label, this,
firstRequest, secondRequest, thirdRequest, fourthRequest, fifthRequest, sixthRequest);
}
/**
* Given a future which is holding an Either, block on result and return an Either.
* All expected exceptions are caught and returns as GeneralError inside an Either
*/
public Either getEitherSafely(String label, ListenableFuture> future,
long timeoutMillis) {
try {
return future.get(timeoutMillis, TimeUnit.MILLISECONDS);
} catch (CancellationException e) {
return GeneralError
.either("getEitherSafely", String.format("%s request cancelled : %s", label, e.getMessage()), e);
} catch (TimeoutException e) {
return GeneralError
.either("getEitherSafely", String.format("%s request timeout : %s", label, e.getMessage()), e);
} catch (ExecutionException e) {
return GeneralError
.either("getEitherSafely", String.format("%s execution failed : %s", label, e.getMessage()), e);
} catch (InterruptedException e) {
return GeneralError
.either("getEitherSafely", String.format("%s execution interrupted : %s", label, e.getMessage()), e);
}
}
/**
* Given a future which is holding an Either, block on result and return an Either.
* All expected exceptions are caught and returns as GeneralError inside an Either
*/
public Either getEitherSafely(String label, ListenableFuture> future) {
return getEitherSafely(label, future, defaultTimeoutMillis);
}
public Option> getSafely(String label,
Option>> optionalFuture) {
if (optionalFuture.isDefined()) {
return Option.apply(getEitherSafely(label, optionalFuture.get()));
} else {
return Option.empty();
}
}
/**
* Given a future which is holding a type T, block on result and return an Either. All expected
* exceptions are caught and returns as GeneralError inside an Either
*/
public Either getSafely(String label, ListenableFuture future, long timeoutMillis) {
try {
return new Right<>(future.get(timeoutMillis, TimeUnit.MILLISECONDS));
} catch (CancellationException e) {
return GeneralError.either("getSafely", label + " request cancelled", e);
} catch (TimeoutException e) {
return GeneralError.either("getSafely", label + " request timeout", e);
} catch (ExecutionException e) {
return GeneralError.either("getSafely", label + " execution failed", e);
} catch (InterruptedException e) {
return GeneralError.either("getSafely", label + " execution interrupted", e);
}
}
/**
* Given a future which is holding a type T, block on result and return an Either. All expected
* exceptions are caught and returns as GeneralError inside an Either
*/
public Either getSafely(String label, ListenableFuture future) {
return getSafely(label, future, defaultTimeoutMillis);
}
public ParRequest immediateResult(String label, Either t) {
checkNotNull(t, "result is null");
return new ParRequest(label, this, Futures.immediateFuture(t));
}
public ParRequest fromFuture(String label, ListenableFuture> future) {
checkNotNull(future, "future is null");
return new ParRequest(label, this, future);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy