
org.elasticsearch.persistent.PersistentTasksService Maven / Gradle / Ivy
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.persistent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionType;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest;
import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.OriginSettingClient;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.ClusterStateObserver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.node.NodeClosedException;
import org.elasticsearch.persistent.PersistentTasksCustomMetadata.PersistentTask;
import org.elasticsearch.tasks.TaskId;
import org.elasticsearch.threadpool.ThreadPool;
import java.util.function.Predicate;
/**
* This service is used by persistent tasks and allocated persistent tasks to communicate changes
* to the master node so that the master can update the cluster state and can track of the states
* of the persistent tasks.
*/
public class PersistentTasksService {
private static final Logger logger = LogManager.getLogger(PersistentTasksService.class);
public static final String PERSISTENT_TASK_ORIGIN = "persistent_tasks";
private final Client client;
private final ClusterService clusterService;
private final ThreadPool threadPool;
public PersistentTasksService(ClusterService clusterService, ThreadPool threadPool, Client client) {
this.client = new OriginSettingClient(client, PERSISTENT_TASK_ORIGIN);
this.clusterService = clusterService;
this.threadPool = threadPool;
}
/**
* Notifies the master node to create new persistent task and to assign it to a node.
*/
public void sendStartRequest(final String taskId,
final String taskName,
final Params taskParams,
final ActionListener> listener) {
@SuppressWarnings("unchecked")
final ActionListener> wrappedListener = listener.map(t -> (PersistentTask) t);
StartPersistentTaskAction.Request request = new StartPersistentTaskAction.Request(taskId, taskName, taskParams);
execute(request, StartPersistentTaskAction.INSTANCE, wrappedListener);
}
/**
* Notifies the master node about the completion of a persistent task.
*
* When {@code failure} is {@code null}, the persistent task is considered as successfully completed.
*/
public void sendCompletionRequest(final String taskId,
final long taskAllocationId,
final @Nullable Exception taskFailure,
final ActionListener> listener) {
CompletionPersistentTaskAction.Request request = new CompletionPersistentTaskAction.Request(taskId, taskAllocationId, taskFailure);
execute(request, CompletionPersistentTaskAction.INSTANCE, listener);
}
/**
* Cancels a locally running task using the Task Manager API
*/
void sendCancelRequest(final long taskId, final String reason, final ActionListener listener) {
CancelTasksRequest request = new CancelTasksRequest();
request.setTaskId(new TaskId(clusterService.localNode().getId(), taskId));
request.setReason(reason);
try {
client.admin().cluster().cancelTasks(request, listener);
} catch (Exception e) {
listener.onFailure(e);
}
}
/**
* Notifies the master node that the state of a persistent task has changed.
*
* Persistent task implementers shouldn't call this method directly and use
* {@link AllocatedPersistentTask#updatePersistentTaskState} instead
*/
void sendUpdateStateRequest(final String taskId,
final long taskAllocationID,
final PersistentTaskState taskState,
final ActionListener> listener) {
UpdatePersistentTaskStatusAction.Request request =
new UpdatePersistentTaskStatusAction.Request(taskId, taskAllocationID, taskState);
execute(request, UpdatePersistentTaskStatusAction.INSTANCE, listener);
}
/**
* Notifies the master node to remove a persistent task from the cluster state
*/
public void sendRemoveRequest(final String taskId, final ActionListener> listener) {
RemovePersistentTaskAction.Request request = new RemovePersistentTaskAction.Request(taskId);
execute(request, RemovePersistentTaskAction.INSTANCE, listener);
}
/**
* Executes an asynchronous persistent task action using the client.
*
* The origin is set in the context and the listener is wrapped to ensure the proper context is restored
*/
private
void execute(final Req request, final ActionType action, final ActionListener> listener) {
try {
client.execute(action, request, listener.map(PersistentTaskResponse::getTask));
} catch (Exception e) {
listener.onFailure(e);
}
}
/**
* Waits for a given persistent task to comply with a given predicate, then call back the listener accordingly.
*
* @param taskId the persistent task id
* @param predicate the persistent task predicate to evaluate
* @param timeout a timeout for waiting
* @param listener the callback listener
*/
public void waitForPersistentTaskCondition(final String taskId,
final Predicate> predicate,
final @Nullable TimeValue timeout,
final WaitForPersistentTaskListener> listener) {
final Predicate clusterStatePredicate = clusterState ->
predicate.test(PersistentTasksCustomMetadata.getTaskWithId(clusterState, taskId));
final ClusterStateObserver observer = new ClusterStateObserver(clusterService, timeout, logger, threadPool.getThreadContext());
final ClusterState clusterState = observer.setAndGetObservedState();
if (clusterStatePredicate.test(clusterState)) {
listener.onResponse(PersistentTasksCustomMetadata.getTaskWithId(clusterState, taskId));
} else {
observer.waitForNextChange(new ClusterStateObserver.Listener() {
@Override
public void onNewClusterState(ClusterState state) {
listener.onResponse(PersistentTasksCustomMetadata.getTaskWithId(state, taskId));
}
@Override
public void onClusterServiceClose() {
listener.onFailure(new NodeClosedException(clusterService.localNode()));
}
@Override
public void onTimeout(TimeValue timeout) {
listener.onTimeout(timeout);
}
}, clusterStatePredicate);
}
}
/**
* Waits for persistent tasks to comply with a given predicate, then call back the listener accordingly.
*
* @param predicate the predicate to evaluate
* @param timeout a timeout for waiting
* @param listener the callback listener
*/
public void waitForPersistentTasksCondition(final Predicate predicate,
final @Nullable TimeValue timeout,
final ActionListener listener) {
final Predicate clusterStatePredicate = clusterState ->
predicate.test(clusterState.metadata().custom(PersistentTasksCustomMetadata.TYPE));
final ClusterStateObserver observer = new ClusterStateObserver(clusterService, timeout, logger, threadPool.getThreadContext());
if (clusterStatePredicate.test(observer.setAndGetObservedState())) {
listener.onResponse(true);
} else {
observer.waitForNextChange(new ClusterStateObserver.Listener() {
@Override
public void onNewClusterState(ClusterState state) {
listener.onResponse(true);
}
@Override
public void onClusterServiceClose() {
listener.onFailure(new NodeClosedException(clusterService.localNode()));
}
@Override
public void onTimeout(TimeValue timeout) {
listener.onFailure(new IllegalStateException("Timed out when waiting for persistent tasks after " + timeout));
}
}, clusterStatePredicate, timeout);
}
}
public interface WaitForPersistentTaskListener extends ActionListener> {
default void onTimeout(TimeValue timeout) {
onFailure(new IllegalStateException("Timed out when waiting for persistent task after " + timeout));
}
}
}