Package com.opengamma.engine.view.cycle

Source Code of com.opengamma.engine.view.cycle.SingleComputationCycleExecutor$CalculationJobComplete

/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.view.cycle;

import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opengamma.engine.cache.CacheSelectHint;
import com.opengamma.engine.cache.ViewComputationCache;
import com.opengamma.engine.calcnode.CalculationJob;
import com.opengamma.engine.calcnode.CalculationJobItem;
import com.opengamma.engine.calcnode.CalculationJobResult;
import com.opengamma.engine.calcnode.CalculationJobResultItem;
import com.opengamma.engine.calcnode.MutableExecutionLog;
import com.opengamma.engine.depgraph.DependencyGraph;
import com.opengamma.engine.depgraph.DependencyNode;
import com.opengamma.engine.exec.DefaultAggregatedExecutionLog;
import com.opengamma.engine.exec.DependencyGraphExecutionFuture;
import com.opengamma.engine.exec.DependencyGraphExecutor;
import com.opengamma.engine.exec.DependencyNodeJobExecutionResult;
import com.opengamma.engine.exec.DependencyNodeJobExecutionResultCache;
import com.opengamma.engine.value.ComputedValueResult;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.engine.view.AggregatedExecutionLog;
import com.opengamma.engine.view.ExecutionLog;
import com.opengamma.engine.view.ExecutionLogMode;
import com.opengamma.engine.view.impl.ExecutionLogModeSource;
import com.opengamma.engine.view.impl.InMemoryViewComputationResultModel;
import com.opengamma.util.async.Cancelable;
import com.opengamma.util.log.LogLevel;
import com.opengamma.util.log.SimpleLogEvent;
import com.opengamma.util.tuple.Pair;

/**
* State required by {@link SingleComputationCycle} during its execution only.
* <p>
* An instance of this object, owned by the cycle, will only exist while the {@link SingleComputationCycle#execute} method is being called.
*/
/* package */class SingleComputationCycleExecutor implements DependencyGraphExecutionFuture.Listener {

  private static final Logger s_logger = LoggerFactory.getLogger(SingleComputationCycleExecutor.class);

  private abstract static class Event {

    public abstract void run(SingleComputationCycleExecutor executorComputation);

  }

  private static class GraphExecutionComplete extends Event {

    private final String _calculationConfiguration;

    public GraphExecutionComplete(final String calculationConfiguration) {
      _calculationConfiguration = calculationConfiguration;
    }

    @Override
    public void run(final SingleComputationCycleExecutor executor) {
      s_logger.info("Execution of {} complete", _calculationConfiguration);
      final ExecutingCalculationConfiguration calcConfig = executor._executing.remove(_calculationConfiguration);

      if (calcConfig != null) {
        SingleComputationCycle cycle = executor.getCycle();
        final InMemoryViewComputationResultModel fragmentResultModel = cycle.constructTemplateResultModel();
        calcConfig.buildResults(fragmentResultModel, cycle.getResultModel());
        cycle.notifyFragmentCompleted(fragmentResultModel);
      }
    }

  }

  private static class CalculationJobComplete extends Event {

    private final CalculationJob _job;
    private final CalculationJobResult _jobResult;

    public CalculationJobComplete(final CalculationJob job, final CalculationJobResult jobResult) {
      _job = job;
      _jobResult = jobResult;
    }

    @Override
    public void run(final SingleComputationCycleExecutor executor) {
      s_logger.debug("Execution of {} complete", _job);
      executor.buildResults(_job, _jobResult);
    }

  }

  private static class ExecutingCalculationConfiguration {

    private final Cancelable _handle;
    private final DependencyGraph _graph;
    private final DependencyNodeJobExecutionResultCache _resultCache;
    private final ViewComputationCache _computationCache;
    private final Set<ValueSpecification> _terminalOutputs = new HashSet<ValueSpecification>();

    public ExecutingCalculationConfiguration(final SingleComputationCycle cycle, final DependencyGraph graph, final Cancelable handle) {
      _handle = handle;
      _graph = graph;
      _resultCache = cycle.getJobExecutionResultCache(graph.getCalculationConfigurationName());
      _computationCache = cycle.getComputationCache(graph.getCalculationConfigurationName());
    }

    public void cancel() {
      _handle.cancel(true);
    }

    public DependencyGraph getDependencyGraph() {
      return _graph;
    }

    public DependencyNodeJobExecutionResultCache getResultCache() {
      return _resultCache;
    }

    public Set<ValueSpecification> getTerminalOutputs() {
      return _terminalOutputs;
    }

    public void buildResults(final InMemoryViewComputationResultModel fragmentResultModel, final InMemoryViewComputationResultModel fullResultModel) {
      if (_terminalOutputs.isEmpty()) {
        return;
      }
      final String calculationConfiguration = _graph.getCalculationConfigurationName();
      for (Pair<ValueSpecification, Object> value : _computationCache.getValues(_terminalOutputs, CacheSelectHint.allShared())) {
        final ValueSpecification valueSpec = value.getFirst();
        final Object calculatedValue = value.getSecond();
        if (calculatedValue != null) {
          final ComputedValueResult computedValueResult = SingleComputationCycle.createComputedValueResult(valueSpec, calculatedValue, _resultCache.get(valueSpec));
          fragmentResultModel.addValue(calculationConfiguration, computedValueResult);
          fullResultModel.addValue(calculationConfiguration, computedValueResult);
        }
      }
      _terminalOutputs.clear();
    }
  }

  private final BlockingQueue<Event> _events = new LinkedBlockingQueue<Event>();
  private final Map<String, ExecutingCalculationConfiguration> _executing = new HashMap<String, ExecutingCalculationConfiguration>();
  private final SingleComputationCycle _cycle;
  private boolean _issueFragmentResults;

  public SingleComputationCycleExecutor(final SingleComputationCycle cycle) {
    _cycle = cycle;
  }

  private SingleComputationCycle getCycle() {
    return _cycle;
  }

  public void execute() throws InterruptedException {
    final DependencyGraphExecutor executor = getCycle().getViewProcessContext().getDependencyGraphExecutorFactory().createExecutor(getCycle());
    for (final String calcConfigurationName : getCycle().getAllCalculationConfigurationNames()) {
      s_logger.info("Executing plans for calculation configuration {}", calcConfigurationName);
      final DependencyGraph depGraph = getCycle().createExecutableDependencyGraph(calcConfigurationName);
      s_logger.info("Submitting {} for execution by {}", depGraph, executor);
      final DependencyGraphExecutionFuture future = executor.execute(depGraph);
      _executing.put(calcConfigurationName, new ExecutingCalculationConfiguration(getCycle(), depGraph, future));
      future.setListener(this);
    }
    try {
      while (!_executing.isEmpty()) {
        // Block for the first event
        _events.take().run(this);
        // Then run through any others as quickly as possible before dispatching a notification
        Event e = _events.poll();
        while (e != null) {
          e.run(this);
          e = _events.poll();
        }
        if (_issueFragmentResults) {
          if (_executing.isEmpty()) {
            s_logger.info("Discarding fragment completion message - overall execution is complete");
          } else {
            s_logger.debug("Building result fragment");
            final InMemoryViewComputationResultModel fragmentResultModel = getCycle().constructTemplateResultModel();
            final InMemoryViewComputationResultModel fullResultModel = getCycle().getResultModel();
            for (ExecutingCalculationConfiguration calcConfig : _executing.values()) {
              calcConfig.buildResults(fragmentResultModel, fullResultModel);
            }
            s_logger.info("Fragment execution complete");
            getCycle().notifyFragmentCompleted(fragmentResultModel);
          }
          _issueFragmentResults = false;
        }
      }
    } catch (final InterruptedException e) {
      Thread.interrupted();
      // Cancel all outstanding jobs to free up resources
      for (ExecutingCalculationConfiguration execution : _executing.values()) {
        execution.cancel();
      }
      throw e;
    }
  }

  private String toString(final String prefix, final Collection<?> values) {
    final StringBuilder sb = new StringBuilder(prefix);
    if (values.size() > 1) {
      sb.append("s - { ");
      int count = 0;
      for (Object value : values) {
        count++;
        if (count > 1) {
          sb.append(", ");
          if (count > 10) {
            sb.append("...");
            break;
          }
        }
        sb.append(value.toString());
      }
      sb.append(" }");
    } else {
      sb.append(" - ").append(values.iterator().next().toString());
    }
    return sb.toString();
  }

  private void buildResults(final CalculationJob job, final CalculationJobResult jobResult) {
    final String calculationConfiguration = jobResult.getSpecification().getCalcConfigName();
    final ExecutingCalculationConfiguration calcConfig = _executing.get(calculationConfiguration);
    if (calcConfig == null) {
      s_logger.warn("Job fragment result for already completed configuration {}", calculationConfiguration);
      return;
    }
    final DependencyGraph graph = calcConfig.getDependencyGraph();
    final Iterator<CalculationJobItem> jobItemItr = job.getJobItems().iterator();
    final Iterator<CalculationJobResultItem> jobResultItr = jobResult.getResultItems().iterator();
    final ExecutionLogModeSource logModes = getCycle().getLogModeSource();
    final DependencyNodeJobExecutionResultCache jobExecutionResultCache = calcConfig.getResultCache();
    final Set<ValueSpecification> terminalOutputs = calcConfig.getTerminalOutputs();
    final String computeNodeId = jobResult.getComputeNodeId();
    while (jobItemItr.hasNext()) {
      assert jobResultItr.hasNext();
      final CalculationJobItem jobItem = jobItemItr.next();
      final CalculationJobResultItem jobResultItem = jobResultItr.next();
      // Mark the node that corresponds to this item
      final DependencyNode node = graph.getNodeProducing(jobItem.getOutputs()[0]);
      if (jobResultItem.isFailed()) {
        getCycle().markFailed(node);
      } else {
        getCycle().markExecuted(node);
      }
      // Process the streamed result fragment
      final ExecutionLogMode executionLogMode = logModes.getLogMode(node);
      final AggregatedExecutionLog aggregatedExecutionLog;
      if (executionLogMode == ExecutionLogMode.FULL) {
        final ExecutionLog log = jobResultItem.getExecutionLog();
        MutableExecutionLog logCopy = null;
        final Set<AggregatedExecutionLog> inputLogs = new LinkedHashSet<AggregatedExecutionLog>();
        Set<ValueSpecification> missing = jobResultItem.getMissingInputs();
        if (!missing.isEmpty()) {
          if (logCopy == null) {
            logCopy = new MutableExecutionLog(log);
          }
          logCopy.add(new SimpleLogEvent(log.hasException() ? LogLevel.WARN : LogLevel.INFO, toString("Missing input", missing)));
        }
        missing = jobResultItem.getMissingOutputs();
        if (!missing.isEmpty()) {
          if (logCopy == null) {
            logCopy = new MutableExecutionLog(log);
          }
          logCopy.add(new SimpleLogEvent(LogLevel.WARN, toString("Failed to produce output", missing)));
        }
        for (final ValueSpecification inputValueSpec : node.getInputValues()) {
          final DependencyNodeJobExecutionResult nodeResult = jobExecutionResultCache.get(inputValueSpec);
          if (nodeResult == null) {
            // Market data
            continue;
          }
          inputLogs.add(nodeResult.getAggregatedExecutionLog());
        }
        aggregatedExecutionLog = DefaultAggregatedExecutionLog.fullLogMode(node, (logCopy != null) ? logCopy : log, inputLogs);
      } else {
        EnumSet<LogLevel> logs = jobResultItem.getExecutionLog().getLogLevels();
        boolean copied = false;
        for (final ValueSpecification inputValueSpec : node.getInputValues()) {
          final DependencyNodeJobExecutionResult nodeResult = jobExecutionResultCache.get(inputValueSpec);
          if (nodeResult == null) {
            // Market data
            continue;
          }
          if (logs.containsAll(nodeResult.getAggregatedExecutionLog().getLogLevels())) {
            continue;
          }
          if (!copied) {
            copied = true;
            logs = EnumSet.copyOf(logs);
          }
          logs.addAll(nodeResult.getAggregatedExecutionLog().getLogLevels());
        }
        aggregatedExecutionLog = DefaultAggregatedExecutionLog.indicatorLogMode(logs);
      }
      final DependencyNodeJobExecutionResult jobExecutionResult = new DependencyNodeJobExecutionResult(computeNodeId, jobResultItem, aggregatedExecutionLog);
      node.gatherTerminalOutputValues(terminalOutputs);
      for (ValueSpecification output : node.getOutputValues()) {
        jobExecutionResultCache.put(output, jobExecutionResult);
      }
    }
    _issueFragmentResults |= !terminalOutputs.isEmpty();
  }

  /**
   * Receives a job result fragment. These will be streamed in by the execution framework. Only one notification per job will be received (for example the execution framework might have
   * repeated/duplicated jobs to handle node failures).
   *
   * @param job the job that was executed, not null
   * @param jobResult the job result, not null
   */
  public void jobCompleted(final CalculationJob job, final CalculationJobResult jobResult) {
    _events.add(new CalculationJobComplete(job, jobResult));
  }

  @Override
  public void graphCompleted(final String calculationConfiguration) {
    _events.add(new GraphExecutionComplete(calculationConfiguration));
  }

}
TOP

Related Classes of com.opengamma.engine.view.cycle.SingleComputationCycleExecutor$CalculationJobComplete

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.