package io.cattle.platform.engine.server.impl; import io.cattle.platform.archaius.util.ArchaiusUtil; import io.cattle.platform.async.utils.TimeoutException; import io.cattle.platform.engine.manager.ProcessManager; import io.cattle.platform.engine.manager.ProcessNotFoundException; import io.cattle.platform.engine.manager.impl.ProcessRecordDao; import io.cattle.platform.engine.process.ExitReason; import io.cattle.platform.engine.process.ProcessInstance; import io.cattle.platform.engine.process.ProcessInstanceException; import io.cattle.platform.engine.process.impl.ProcessCancelException; import io.cattle.platform.engine.process.util.ProcessEngineUtils; import io.cattle.platform.engine.server.ProcessInstanceDispatcher; import io.cattle.platform.engine.server.ProcessInstanceExecutor; import io.cattle.platform.engine.server.ProcessInstanceReference; import io.cattle.platform.engine.server.ProcessServer; import io.cattle.platform.lock.LockCallback; import io.cattle.platform.lock.LockManager; import io.cattle.platform.metrics.util.MetricsUtil; import io.cattle.platform.util.exception.ExceptionUtils; import io.cattle.platform.util.exception.LoggableException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.codahale.metrics.Counter; public class ProcessInstanceDispatcherImpl implements ProcessInstanceDispatcher, ProcessInstanceExecutor { private static final Logger log = LoggerFactory.getLogger(ProcessInstanceDispatcherImpl.class); private static Counter EVENT = MetricsUtil.getRegistry().counter("process_execution.event"); private static Counter CANCELED = MetricsUtil.getRegistry().counter("process_execution.cancel"); private static Counter DONE = MetricsUtil.getRegistry().counter("process_execution.done"); private static Counter NOT_FOUND = MetricsUtil.getRegistry().counter("process_execution.not_found"); private static Counter EXCEPTION = MetricsUtil.getRegistry().counter("process_execution.unknown_exception"); private static Counter TIMEOUT = MetricsUtil.getRegistry().counter("process_execution.timeout"); @Inject @Named("ProcessEventExecutorService") ExecutorService eventExecutor; @Inject @Named("ProcessBlockingExecutorService") ExecutorService blockingExecutor; @Inject @Named("ProcessNonBlockingExecutorService") ExecutorService nonBlockingExecutor; @Inject ProcessRecordDao processRecordDao; @Inject LockManager lockManager; @Inject ProcessManager processManager; @Inject ProcessServer processServer; Map<ExitReason, Counter> counters = new HashMap<ExitReason, Counter>(); @Override public void dispatch(ProcessInstanceReference ref) { if (!ProcessEngineUtils.enabled()) { return; } ref.setExecutor(this); boolean submitted = false; if (ref.isEvent()) { try { eventExecutor.execute(ref); submitted = true; } catch (RejectedExecutionException e) { // } } if (!submitted) { if (isBlocking(ref)) { blockingExecutor.execute(ref); } else { nonBlockingExecutor.execute(ref); } } } protected boolean isBlocking(ProcessInstanceReference ref) { if (ArchaiusUtil.getBoolean("process." + ref.getName().split("[.]")[0] + ".blocking").get()) { return true; } return ArchaiusUtil.getBoolean("process." + ref.getName() + ".blocking").get(); } @Override public void execute(final long processId) { Long nextId = processId; while (nextId != null) { final long currentId = nextId; nextId = lockManager.tryLock(new ProcessDispatchLock(currentId), new LockCallback<Long>() { @Override public Long doWithLock() { return processExecuteWithLock(currentId); } }); } } public Long processExecuteWithLock(long processId) { EVENT.inc(); boolean runRemaining = false; ProcessInstance instance = null; try { instance = processManager.loadProcess(processId); instance.execute(); runRemaining = true; DONE.inc(); } catch (ProcessNotFoundException e) { NOT_FOUND.inc(); log.debug("Failed to find process for id [{}]", processId); } catch (ProcessInstanceException e) { counters.get(e.getExitReason()).inc(); if (e.getExitReason().isError()) { log.error("Process [{}:{}] on [{}] failed, exit [{}] : {}", instance.getName(), processId, instance.getResourceId(), e .getExitReason(), e.getMessage()); } } catch (TimeoutException e) { TIMEOUT.inc(); log.info("Timeout on process [{}:{}] on [{}] : {}", instance.getName(), processId, instance.getResourceId(), e .getMessage()); } catch (ProcessCancelException e) { CANCELED.inc(); runRemaining = true; log.debug("Process canceled [{}:{}] on [{}] : {}", instance.getName(), processId, instance.getResourceId(), e.getMessage()); } catch (Throwable e) { if (e instanceof LoggableException) { EXCEPTION.inc(); ((LoggableException) e).log(log); } else { Throwable cause = ExceptionUtils.getRootCause(e); if (cause instanceof ProcessCancelException) { CANCELED.inc(); log.error("Unknown exception running process [{}:{}] on [{}], canceled by [{}]", instance == null ? null : instance.getName(), processId, instance == null ? null : instance.getResourceId(), cause.getMessage()); } else { EXCEPTION.inc(); log.error("Unknown exception running process [{}:{}] on [{}]", instance == null ? null : instance.getName(), processId, instance == null ? null : instance.getResourceId(), e); } } } if (runRemaining) { return processServer.getNextTask(instance); } return null; } @PostConstruct public void init() { for (ExitReason e : ExitReason.values()) { switch (e) { case DONE: case ALREADY_DONE: break; default: counters.put(e, MetricsUtil.getRegistry().counter("process_execution." + e.toString().toLowerCase())); } } } }