Explicitly correspond cluster state tasks and execution results

This commit is contained in:
Jason Tedor 2015-11-25 12:54:48 -05:00
parent c3f97e7642
commit 7caee2fa4d
6 changed files with 95 additions and 40 deletions

View File

@ -18,15 +18,17 @@
*/ */
package org.elasticsearch.cluster; package org.elasticsearch.cluster;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public interface ClusterStateTaskExecutor<T> { public interface ClusterStateTaskExecutor<T> {
/** /**
* Update the cluster state based on the current state and the given tasks. Return the *same instance* if no state * Update the cluster state based on the current state and the given tasks. Return the *same instance* if no state
* should be changed. * should be changed.
*/ */
Result execute(ClusterState currentState, List<T> tasks) throws Exception; Result<T> execute(ClusterState currentState, List<T> tasks) throws Exception;
/** /**
* indicates whether this task should only run if current node is master * indicates whether this task should only run if current node is master
@ -35,19 +37,66 @@ public interface ClusterStateTaskExecutor<T> {
return true; return true;
} }
class Result { /**
* Represents the result of a batched execution of cluster state update tasks
* @param <T> the type of the cluster state update task
*/
class Result<T> {
final public ClusterState resultingState; final public ClusterState resultingState;
final public List<Throwable> failures; final public Map<T, ClusterStateTaskExecutionResult> executionResults;
public Result(ClusterState resultingState, int numberOfTasks) { /**
this.resultingState = resultingState; * Construct an execution result instance for which every cluster state update task succeeded
failures = Arrays.asList(new Throwable[numberOfTasks]); * @param resultingState the resulting cluster state
* @param tasks the cluster state update tasks
*/
public Result(ClusterState resultingState, List<T> tasks) {
this(resultingState, tasks.stream().collect(Collectors.toMap(task -> task, task -> ClusterStateTaskExecutionResult.success())));
} }
public Result(ClusterState resultingState, List<Throwable> failures) { /**
* Construct an execution result instance with a correspondence between the tasks and their execution result
* @param resultingState the resulting cluster state
* @param executionResults the correspondence between tasks and their outcome
*/
public Result(ClusterState resultingState, Map<T, ClusterStateTaskExecutionResult> executionResults) {
this.resultingState = resultingState; this.resultingState = resultingState;
this.failures = failures; this.executionResults = executionResults;
} }
} }
final class ClusterStateTaskExecutionResult {
private final Throwable failure;
private static final ClusterStateTaskExecutionResult SUCCESS = new ClusterStateTaskExecutionResult(null);
public static ClusterStateTaskExecutionResult success() {
return SUCCESS;
}
public static ClusterStateTaskExecutionResult failure(Throwable failure) {
return new ClusterStateTaskExecutionResult(failure);
}
private ClusterStateTaskExecutionResult(Throwable failure) {
this.failure = failure;
}
public boolean isSuccess() {
return failure != null;
}
/**
* Handle the execution result with the provided consumers
* @param onSuccess handler to invoke on success
* @param onFailure handler to invoke on failure; the throwable passed through will not be null
*/
public void handle(Runnable onSuccess, Consumer<Throwable> onFailure) {
if (failure == null) {
onSuccess.run();
} else {
onFailure.accept(failure);
}
}
}
} }

View File

@ -28,7 +28,7 @@ import java.util.List;
/** /**
* A task that can update the cluster state. * A task that can update the cluster state.
*/ */
abstract public class ClusterStateUpdateTask implements ClusterStateTaskConfig, ClusterStateTaskExecutor<Void>, ClusterStateTaskListener { abstract public class ClusterStateUpdateTask implements ClusterStateTaskConfig, ClusterStateTaskExecutor<ClusterStateUpdateTask>, ClusterStateTaskListener {
final private Priority priority; final private Priority priority;
@ -41,9 +41,9 @@ abstract public class ClusterStateUpdateTask implements ClusterStateTaskConfig,
} }
@Override @Override
final public Result execute(ClusterState currentState, List<Void> tasks) throws Exception { final public Result<ClusterStateUpdateTask> execute(ClusterState currentState, List<ClusterStateUpdateTask> tasks) throws Exception {
ClusterState result = execute(currentState); ClusterState result = execute(currentState);
return new Result(result, tasks.size()); return new Result<>(result, tasks);
} }
/** /**

View File

@ -81,9 +81,9 @@ public class MetaDataMappingService extends AbstractComponent {
class RefreshTaskExecutor implements ClusterStateTaskExecutor<RefreshTask> { class RefreshTaskExecutor implements ClusterStateTaskExecutor<RefreshTask> {
@Override @Override
public Result execute(ClusterState currentState, List<RefreshTask> tasks) throws Exception { public Result<RefreshTask> execute(ClusterState currentState, List<RefreshTask> tasks) throws Exception {
ClusterState newClusterState = executeRefresh(currentState, tasks); ClusterState newClusterState = executeRefresh(currentState, tasks);
return new Result(newClusterState, tasks.size()); return new Result<>(newClusterState, tasks);
} }
} }
@ -221,9 +221,9 @@ public class MetaDataMappingService extends AbstractComponent {
class PutMappingExecutor implements ClusterStateTaskExecutor<PutMappingClusterStateUpdateRequest> { class PutMappingExecutor implements ClusterStateTaskExecutor<PutMappingClusterStateUpdateRequest> {
@Override @Override
public Result execute(ClusterState currentState, List<PutMappingClusterStateUpdateRequest> tasks) throws Exception { public Result<PutMappingClusterStateUpdateRequest> execute(ClusterState currentState, List<PutMappingClusterStateUpdateRequest> tasks) throws Exception {
List<String> indicesToClose = new ArrayList<>(); List<String> indicesToClose = new ArrayList<>();
ArrayList<Throwable> failures = new ArrayList<>(tasks.size()); Map<PutMappingClusterStateUpdateRequest, ClusterStateTaskExecutionResult> executionResults = new HashMap<>();
try { try {
// precreate incoming indices; // precreate incoming indices;
for (PutMappingClusterStateUpdateRequest request : tasks) { for (PutMappingClusterStateUpdateRequest request : tasks) {
@ -250,13 +250,13 @@ public class MetaDataMappingService extends AbstractComponent {
for (PutMappingClusterStateUpdateRequest request : tasks) { for (PutMappingClusterStateUpdateRequest request : tasks) {
try { try {
currentState = applyRequest(currentState, request); currentState = applyRequest(currentState, request);
failures.add(null); executionResults.put(request, ClusterStateTaskExecutionResult.success());
} catch (Throwable t) { } catch (Throwable t) {
failures.add(t); executionResults.put(request, ClusterStateTaskExecutionResult.failure(t));
} }
} }
return new Result(currentState, failures); return new Result<>(currentState, executionResults);
} finally { } finally {
for (String index : indicesToClose) { for (String index : indicesToClose) {
indicesService.removeIndex(index, "created for mapping processing"); indicesService.removeIndex(index, "created for mapping processing");

View File

@ -269,7 +269,7 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
@Override @Override
public void submitStateUpdateTask(final String source, final ClusterStateUpdateTask updateTask) { public void submitStateUpdateTask(final String source, final ClusterStateUpdateTask updateTask) {
submitStateUpdateTask(source, null, updateTask, updateTask, updateTask); submitStateUpdateTask(source, updateTask, updateTask, updateTask, updateTask);
} }
@ -395,7 +395,7 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
toExecute.stream().forEach(task -> task.listener.onNoLongerMaster(task.source)); toExecute.stream().forEach(task -> task.listener.onNoLongerMaster(task.source));
return; return;
} }
ClusterStateTaskExecutor.Result result; ClusterStateTaskExecutor.Result<T> result;
long startTimeNS = System.nanoTime(); long startTimeNS = System.nanoTime();
try { try {
List<T> inputs = toExecute.stream().map(tUpdateTask -> tUpdateTask.task).collect(Collectors.toList()); List<T> inputs = toExecute.stream().map(tUpdateTask -> tUpdateTask.task).collect(Collectors.toList());
@ -410,21 +410,26 @@ public class InternalClusterService extends AbstractLifecycleComponent<ClusterSe
logger.trace(sb.toString(), e); logger.trace(sb.toString(), e);
} }
warnAboutSlowTaskIfNeeded(executionTime, source); warnAboutSlowTaskIfNeeded(executionTime, source);
result = new ClusterStateTaskExecutor.Result(previousClusterState, Collections.nCopies(toExecute.size(), e)); Map<T, ClusterStateTaskExecutor.ClusterStateTaskExecutionResult> executionResults =
toExecute
.stream()
.collect(Collectors.toMap(
updateTask -> updateTask.task,
updateTask -> ClusterStateTaskExecutor.ClusterStateTaskExecutionResult.failure(e)
));
result = new ClusterStateTaskExecutor.Result<>(previousClusterState, executionResults);
} }
assert result.failures.size() == toExecute.size();
assert result.executionResults != null;
ClusterState newClusterState = result.resultingState; ClusterState newClusterState = result.resultingState;
final ArrayList<UpdateTask<T>> proccessedListeners = new ArrayList<>(); final ArrayList<UpdateTask<T>> proccessedListeners = new ArrayList<>();
// fail all tasks that have failed and extract those that are waiting for results // fail all tasks that have failed and extract those that are waiting for results
for (int i = 0; i < toExecute.size(); i++) { for (UpdateTask<T> updateTask : toExecute) {
final UpdateTask<T> task = toExecute.get(i); assert result.executionResults.containsKey(updateTask.task) : "missing " + updateTask.task.toString();
final Throwable failure = result.failures.get(i); final ClusterStateTaskExecutor.ClusterStateTaskExecutionResult executionResult =
if (failure == null) { result.executionResults.get(updateTask.task);
proccessedListeners.add(task); executionResult.handle(() -> proccessedListeners.add(updateTask), ex -> updateTask.listener.onFailure(updateTask.source, ex));
} else {
task.listener.onFailure(task.source, failure);
}
} }
if (previousClusterState == newClusterState) { if (previousClusterState == newClusterState) {

View File

@ -755,10 +755,10 @@ public class ClusterServiceIT extends ESIntegTestCase {
private AtomicInteger counter = new AtomicInteger(); private AtomicInteger counter = new AtomicInteger();
@Override @Override
public Result execute(ClusterState currentState, List<Task> tasks) throws Exception { public Result<Task> execute(ClusterState currentState, List<Task> tasks) throws Exception {
tasks.forEach(task -> task.execute()); tasks.forEach(task -> task.execute());
counter.addAndGet(tasks.size()); counter.addAndGet(tasks.size());
return new Result(currentState, tasks.size()); return new Result<>(currentState, tasks);
} }
@Override @Override

View File

@ -191,18 +191,19 @@ public class TestClusterService implements ClusterService {
logger.debug("failed [{}], no longer master", source); logger.debug("failed [{}], no longer master", source);
return; return;
} }
ClusterStateTaskExecutor.Result result; ClusterStateTaskExecutor.Result<T> result;
ClusterState previousClusterState = state; ClusterState previousClusterState = state;
try { try {
result = executor.execute(previousClusterState, Arrays.asList(task)); result = executor.execute(previousClusterState, Arrays.asList(task));
} catch (Exception e) { } catch (Exception e) {
result = new ClusterStateTaskExecutor.Result(previousClusterState, Arrays.asList(e)); result = new ClusterStateTaskExecutor.Result<>(previousClusterState, Collections.singletonMap(task, ClusterStateTaskExecutor.ClusterStateTaskExecutionResult.failure(e)));
}
if (result.failures.get(0) != null) {
listener.onFailure(source, new ElasticsearchException("failed to process cluster state update task [" + source + "]",
result.failures.get(0)));
return;
} }
result.executionResults.get(task).handle(
() -> {},
ex -> listener.onFailure(source, new ElasticsearchException("failed to process cluster state update task [" + source + "]", ex))
);
setStateAndNotifyListeners(result.resultingState); setStateAndNotifyListeners(result.resultingState);
listener.clusterStateProcessed(source, previousClusterState, result.resultingState); listener.clusterStateProcessed(source, previousClusterState, result.resultingState);
logger.debug("finished [{}]", source); logger.debug("finished [{}]", source);