package cz.cuni.mff.d3s.been.manager.action;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hazelcast.core.IMap;
import com.hazelcast.query.SqlPredicate;
import cz.cuni.mff.d3s.been.cluster.context.ClusterContext;
import cz.cuni.mff.d3s.been.cluster.context.TaskContexts;
import cz.cuni.mff.d3s.been.core.task.TaskContextEntry;
import cz.cuni.mff.d3s.been.core.task.TaskContextState;
import cz.cuni.mff.d3s.been.core.task.TaskEntry;
import cz.cuni.mff.d3s.been.core.task.TaskState;
/**
* @author Kuba Brecka
*/
final class TaskContextCheckerAction implements TaskAction {
/** logging */
private static final Logger log = LoggerFactory.getLogger(TaskContextCheckerAction.class);
/** connection to the cluster */
private final ClusterContext ctx;
/** task entry used for the context check */
private final TaskEntry entry;
/** format of sql query predicate for finding all unfinished tasks */
private static final String QUERY_FORMAT = "taskContextId = '%s' AND ((state == %s) OR (state == %s))";
/**
* Creates new context checker action.
*
* @param ctx
* connection to the cluster
* @param entry
* targeted task entry
*/
public TaskContextCheckerAction(ClusterContext ctx, TaskEntry entry) {
this.ctx = ctx;
this.entry = entry;
}
/**
* Checks if the finished context of the task is completely finished (all
* tasks from it are finished or aborted). If yes, calls the context cleanup
* procedure.
*
* Serialization for multiply cluster members is ensured by cluster locks.
*
* @throws TaskActionException
*/
@Override
public void execute() throws TaskActionException {
final String taskContextId = entry.getTaskContextId();
final TaskContexts contexts = ctx.getTaskContexts();
IMap<String, TaskContextEntry> contextsMap = ctx.getTaskContexts().getTaskContextsMap();
IMap<String, TaskEntry> tasksMap = ctx.getTasks().getTasksMap();
// fetch the entry
TaskContextEntry contextEntry = contexts.getTaskContext(taskContextId);
// optimization: fist check, and if needed then lock and check again
if (isContextDone(contextEntry)) {
return;
}
// benchmarks context should not be destroyed at all
if (contextEntry.isLingering()) {
return;
}
try {
contextsMap.lock(taskContextId); // LOCK BEGIN
// must fetch again
contextEntry = contexts.getTaskContext(taskContextId);
if (isContextDone(contextEntry)) {
return;
}
Collection<TaskEntry> values = tasksMap.values(getPredicate(taskContextId));
boolean isFinished = (values.size() == contextEntry.getContainedTask().size());
if (isFinished) {
TaskContextState finalState = TaskContextState.FINISHED;
if (isFailed(values)) {
finalState = TaskContextState.FAILED;
}
contextEntry.setContextState(finalState);
if (finalState == TaskContextState.FINISHED) {
ctx.getTaskContexts().cleanupTaskContext(contextEntry);
} else {
ctx.getTaskContexts().putContextEntry(contextEntry);
}
}
} finally {
contextsMap.unlock(taskContextId); // LOCK END
}
}
/**
* Creates SqlPredicate for finding all unfinished tasks.
*
* @param contextId
* ID of the contexts tasks belong to
*
* @return SqlPredicate for finding all unfinished tasks from the specified
* context
*/
private SqlPredicate getPredicate(String contextId) {
String sql = String.format(QUERY_FORMAT, contextId, TaskState.FINISHED, TaskState.ABORTED);
return new SqlPredicate(sql);
}
private boolean isFailed(Collection<TaskEntry> tasks) {
for (TaskEntry taskEntry : tasks) {
if (taskEntry.getState() == TaskState.ABORTED) {
return true;
}
}
return false;
}
private boolean isContextDone(TaskContextEntry entry) {
return entry.getContextState() == TaskContextState.FINISHED || entry.getContextState() == TaskContextState.FAILED;
}
}