All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.persistent.AllocatedPersistentTask 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.ActionListener;
import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.tasks.CancellableTask;
import org.elasticsearch.tasks.TaskId;
import org.elasticsearch.tasks.TaskManager;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;

import static org.elasticsearch.core.Strings.format;

/**
 * Represents a executor node operation that corresponds to a persistent task
 */
public class AllocatedPersistentTask extends CancellableTask {

    private static final Logger logger = LogManager.getLogger(AllocatedPersistentTask.class);
    private final AtomicReference state;

    private volatile String persistentTaskId;
    private volatile long allocationId;
    private volatile @Nullable Exception failure;
    private volatile PersistentTasksService persistentTasksService;
    private volatile TaskManager taskManager;

    public AllocatedPersistentTask(
        long id,
        String type,
        String action,
        String description,
        TaskId parentTask,
        Map headers
    ) {
        super(id, type, action, description, parentTask, headers);
        this.state = new AtomicReference<>(State.STARTED);
    }

    @Override
    public Status getStatus() {
        return new PersistentTasksNodeService.Status(state.get());
    }

    /**
     * Updates the persistent state for the corresponding persistent task.
     * 

* This doesn't affect the status of this allocated task. */ public void updatePersistentTaskState( final PersistentTaskState state, final ActionListener> listener ) { persistentTasksService.sendUpdateStateRequest(persistentTaskId, allocationId, state, listener); } public String getPersistentTaskId() { return persistentTaskId; } protected void init( PersistentTasksService persistentTasksService, TaskManager taskManager, String persistentTaskId, long allocationId ) { this.persistentTasksService = persistentTasksService; this.taskManager = taskManager; this.persistentTaskId = persistentTaskId; this.allocationId = allocationId; } public Exception getFailure() { return failure; } public long getAllocationId() { return allocationId; } /** * Waits for a given persistent task to comply with a given predicate, then call back the listener accordingly. * * @param predicate the persistent task predicate to evaluate * @param timeout a timeout for waiting * @param listener the callback listener */ public void waitForPersistentTask( final Predicate> predicate, final @Nullable TimeValue timeout, final PersistentTasksService.WaitForPersistentTaskListener listener ) { persistentTasksService.waitForPersistentTaskCondition(persistentTaskId, predicate, timeout, listener); } /** * For external purposes, locally aborted and completed are the same. * @return Is this task completed on the current node? */ protected final boolean isCompleted() { return state.get() == State.COMPLETED || state.get() == State.LOCAL_ABORTED; } protected boolean markAsCancelled() { return state.compareAndSet(State.STARTED, State.PENDING_CANCEL); } public void markAsCompleted() { completeAndNotifyIfNeeded(null, null); } public void markAsFailed(Exception e) { if (CancelTasksRequest.DEFAULT_REASON.equals(getReasonCancelled())) { completeAndNotifyIfNeeded(null, null); } else { completeAndNotifyIfNeeded(e, null); } } /** * Indicates that this persistent task is no longer going to run on the local node. * This will cause the local task to be terminated, and the associated persistent * task to be reassigned by the master node. The persistent task may be * reassigned to the same node unless separate measures have been taken to prevent * this. The task should complete any graceful shutdown actions before calling this * method. * @param localAbortReason Reason for the task being aborted on this node. This * will be recorded as the reason for unassignment of the * persistent task. */ public void markAsLocallyAborted(String localAbortReason) { completeAndNotifyIfNeeded(null, Objects.requireNonNull(localAbortReason)); } private void completeAndNotifyIfNeeded(@Nullable Exception failure, @Nullable String localAbortReason) { assert failure == null || localAbortReason == null : "completion notification has both exception " + failure + " and local abort reason " + localAbortReason; final State desiredState = (localAbortReason == null) ? State.COMPLETED : State.LOCAL_ABORTED; final State prevState = state.getAndUpdate( currentState -> (currentState != State.COMPLETED && currentState != State.LOCAL_ABORTED) ? desiredState : currentState ); if (prevState == State.COMPLETED || prevState == State.LOCAL_ABORTED) { // To preserve old behaviour completing a task twice is not an error. // However, any combination of local abort with completion or failure // is an error, as is issuing two local aborts for the same task. if (desiredState == State.COMPLETED) { if (prevState == State.COMPLETED) { logger.warn( "attempt to complete task [{}] with id [{}] in the [{}] state", getAction(), getPersistentTaskId(), prevState ); } else { throw new IllegalStateException( "attempt to " + (failure != null ? "fail" : "complete") + " task [" + getAction() + "] with id [" + getPersistentTaskId() + "] which has been locally aborted" ); } } else { throw new IllegalStateException( "attempt to locally abort task [" + getAction() + "] with id [" + getPersistentTaskId() + "] which has already been " + (prevState == State.COMPLETED ? "completed" : "locally aborted") ); } } else { if (failure != null) { logger.warn(() -> "task [" + getPersistentTaskId() + "] failed with an exception", failure); } else if (localAbortReason != null) { logger.debug("task [{}] aborted locally: [{}]", getPersistentTaskId(), localAbortReason); } try { this.failure = failure; if (prevState == State.STARTED) { logger.trace("sending notification for completed task [{}] with id [{}]", getAction(), getPersistentTaskId()); persistentTasksService.sendCompletionRequest( getPersistentTaskId(), getAllocationId(), failure, localAbortReason, new ActionListener>() { @Override public void onResponse(PersistentTasksCustomMetadata.PersistentTask persistentTask) { logger.trace("notification for task [{}] with id [{}] was successful", getAction(), getPersistentTaskId()); } @Override public void onFailure(Exception e) { logger.warn( () -> format("notification for task [%s] with id [%s] failed", getAction(), getPersistentTaskId()), e ); } } ); } } finally { taskManager.unregister(this); } } } public enum State { STARTED, // the task is currently running PENDING_CANCEL, // the task is cancelled on master, cancelling it locally COMPLETED, // the task is done running and trying to notify caller LOCAL_ABORTED // the task is aborted on the local node - master should reassign } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy