@Test public void testCycleSimpleGraph() { ViewProcessorTestEnvironment env = new ViewProcessorTestEnvironment(); env.init(); CompiledViewDefinitionWithGraphsImpl compiledViewDefinition = env.compileViewDefinition(Instant.now(), VersionCorrection.LATEST); DependencyGraph graph = compiledViewDefinition.getDependencyGraph( ViewProcessorTestEnvironment.TEST_CALC_CONFIG_NAME); DependencyGraph cycledGraph = cycleObject(DependencyGraph.class, graph); assertEquals( graph.getCalculationConfigurationName(), cycledGraph.getCalculationConfigurationName()); assertEquals(graph.getAllComputationTargets(), cycledGraph.getAllComputationTargets()); assertEquals(graph.getOutputSpecifications(), cycledGraph.getOutputSpecifications()); assertEquals(graph.getSize(), cycledGraph.getSize()); assertEquals( graph.getTerminalOutputSpecifications(), cycledGraph.getTerminalOutputSpecifications()); for (DependencyNode node : graph.getDependencyNodes()) { boolean isRoot = graph.getRootNodes().contains(node); for (ValueSpecification spec : node.getOutputValues()) { DependencyNode equivalentNode = cycledGraph.getNodeProducing(spec); assertEquals(isRoot, cycledGraph.getRootNodes().contains(equivalentNode)); assertEquals(node.getInputValues(), equivalentNode.getInputValues()); assertEquals(node.getOutputValues(), equivalentNode.getOutputValues()); assertEquals(node.getTerminalOutputValues(), equivalentNode.getTerminalOutputValues()); } } }
// ------------------------------------------------------------------------- private EngineResourceReference<SingleComputationCycle> createCycle( ViewCycleExecutionOptions executionOptions, CompiledViewDefinitionWithGraphsImpl compiledViewDefinition, VersionCorrection versionCorrection) { // View definition was compiled based on compilation options, which might have only included an // indicative // valuation time. A further check ensures that the compiled view definition is still valid. if (!compiledViewDefinition.isValidFor(executionOptions.getValuationTime())) { throw new OpenGammaRuntimeException( "Compiled view definition " + compiledViewDefinition + " not valid for execution options " + executionOptions); } UniqueId cycleId = getViewProcess().generateCycleId(); ComputationResultListener streamingResultListener = new ComputationResultListener() { @Override public void resultAvailable(ViewComputationResultModel result) { cycleFragmentCompleted(result); } }; SingleComputationCycle cycle = new SingleComputationCycle( cycleId, getViewProcess().getUniqueId(), streamingResultListener, getProcessContext(), compiledViewDefinition, executionOptions, versionCorrection); return getCycleManager().manage(cycle); }
@Override public void valuesChanged(Collection<ValueRequirement> values) { if (!getExecutionOptions() .getFlags() .contains(ViewExecutionFlags.TRIGGER_CYCLE_ON_MARKET_DATA_CHANGED)) { return; } CompiledViewDefinitionWithGraphsImpl compiledView = getCachedCompiledViewDefinition(); if (compiledView == null) { return; } // Since this happens for every tick, for every job, we need to use the quick call here if (compiledView.hasAnyMarketDataRequirements(values)) { marketDataChanged(); } }
private CompiledViewDefinitionWithGraphsImpl getCompiledViewDefinition( Instant valuationTime, VersionCorrection versionCorrection) { long functionInitId = getProcessContext() .getFunctionCompilationService() .getFunctionCompilationContext() .getFunctionInitId(); CompiledViewDefinitionWithGraphsImpl compiledViewDefinition; updateViewDefinitionIfRequired(); if (_compilationDirty) { _compilationDirty = false; invalidateCachedCompiledViewDefinition(); compiledViewDefinition = null; } else { compiledViewDefinition = getCachedCompiledViewDefinition(); } if (compiledViewDefinition != null && compiledViewDefinition.isValidFor(valuationTime) && functionInitId == compiledViewDefinition.getFunctionInitId()) { // Existing cached model is valid (an optimisation for the common case of similar, increasing // valuation times) return compiledViewDefinition; } try { MarketDataAvailabilityProvider availabilityProvider = getMarketDataProvider().getAvailabilityProvider(); ViewCompilationServices compilationServices = getProcessContext().asCompilationServices(availabilityProvider); compiledViewDefinition = ViewDefinitionCompiler.compile( _viewDefinition, compilationServices, valuationTime, versionCorrection); if (isTerminated()) { return compiledViewDefinition; // [PLAT-1904] If we can't terminate the compilation at least // avoid doing the subscribe etc. } } catch (Exception e) { String message = MessageFormat.format( "Error compiling view definition {0} for time {1}", getViewProcess().getDefinitionId(), valuationTime); viewDefinitionCompilationFailed(valuationTime, new OpenGammaRuntimeException(message, e)); throw new OpenGammaRuntimeException(message, e); } setCachedCompiledViewDefinition(compiledViewDefinition); // [PLAT-984] // Assume that valuation times are increasing in real-time towards the expiry of the view // definition, so that we // can predict the time to expiry. If this assumption is wrong then the worst we do is trigger // an unnecessary // cycle. In the predicted case, we trigger a cycle on expiry so that any new market data // subscriptions are made // straight away. if (compiledViewDefinition.getValidTo() != null) { Duration durationToExpiry = getMarketDataProvider() .getRealTimeDuration(valuationTime, compiledViewDefinition.getValidTo()); long expiryNanos = System.nanoTime() + durationToExpiry.toNanosLong(); _compilationExpiryCycleTrigger.set(expiryNanos, ViewCycleTriggerResult.forceFull()); } else { _compilationExpiryCycleTrigger.reset(); } // Notify the view that a (re)compilation has taken place before going on to do any // time-consuming work. // This might contain enough for clients to e.g. render an empty grid in which results will // later appear. viewDefinitionCompiled(compiledViewDefinition); // Update the market data subscriptions to whatever is now required, ensuring the computation // cycle can find the // required input data when it is executed. setMarketDataSubscriptions(compiledViewDefinition.getMarketDataRequirements().keySet()); return compiledViewDefinition; }
/** * Determines whether to run, and runs if required, a single computation cycle using the following * rules: * * <ul> * <li>A computation cycle can only be triggered if the relevant minimum computation period has * passed since the start of the previous cycle. * <li>A computation cycle will be forced if the relevant maximum computation period has passed * since the start of the previous cycle. * <li>A full computation is preferred over a delta computation if both are possible. * <li>Performing a full computation also updates the times to the next delta computation; i.e. * a full computation is considered to be as good as a delta. * </ul> */ @Override protected void runOneCycle() { // Exception handling is important here to ensure that computation jobs do not just die quietly // while consumers are // potentially blocked, waiting for results. ViewCycleType cycleType; try { cycleType = waitForNextCycle(); } catch (InterruptedException e) { return; } ViewCycleExecutionOptions executionOptions = null; try { if (!getExecutionOptions().getExecutionSequence().isEmpty()) { executionOptions = getExecutionOptions() .getExecutionSequence() .getNext(getExecutionOptions().getDefaultExecutionOptions()); s_logger.debug("Next cycle execution options: {}", executionOptions); } if (executionOptions == null) { s_logger.info("No more view cycle execution options"); processCompleted(); return; } } catch (Exception e) { s_logger.error( "Error obtaining next view cycle execution options from sequence for view process " + getViewProcess(), e); return; } if (executionOptions.getMarketDataSpecification() == null) { s_logger.error("No market data specification for cycle"); cycleExecutionFailed( executionOptions, new OpenGammaRuntimeException("No market data specification for cycle")); return; } MarketDataSnapshot marketDataSnapshot; try { if (getMarketDataProvider() == null || !getMarketDataProvider().isCompatible(executionOptions.getMarketDataSpecification())) { // A different market data provider is required. We support this because we can, but // changing provider is not the // most efficient operation. if (getMarketDataProvider() != null) { s_logger.info("Replacing market data provider between cycles"); } replaceMarketDataProvider(executionOptions.getMarketDataSpecification()); } // Obtain the snapshot in case it is needed, but don't explicitly initialise it until the data // is required marketDataSnapshot = getMarketDataProvider().snapshot(executionOptions.getMarketDataSpecification()); } catch (Exception e) { s_logger.error("Error with market data provider", e); cycleExecutionFailed( executionOptions, new OpenGammaRuntimeException("Error with market data provider", e)); return; } Instant compilationValuationTime; try { if (executionOptions.getValuationTime() != null) { compilationValuationTime = executionOptions.getValuationTime(); } else { // Neither the cycle-specific options nor the defaults have overridden the valuation time so // use the time // associated with the market data snapshot. To avoid initialising the snapshot perhaps // before the required // inputs are known or even subscribed to, only ask for an indication at the moment. compilationValuationTime = marketDataSnapshot.getSnapshotTimeIndication(); if (compilationValuationTime == null) { throw new OpenGammaRuntimeException( "Market data snapshot " + marketDataSnapshot + " produced a null indication of snapshot time"); } } } catch (Exception e) { s_logger.error("Error obtaining compilation valuation time", e); cycleExecutionFailed( executionOptions, new OpenGammaRuntimeException("Error obtaining compilation valuation time", e)); return; } VersionCorrection versionCorrection = getResolvedVersionCorrection(); final CompiledViewDefinitionWithGraphsImpl compiledViewDefinition; try { compiledViewDefinition = getCompiledViewDefinition(compilationValuationTime, versionCorrection); if (isTerminated()) { return; // [PLAT-1904] } } catch (Exception e) { String message = MessageFormat.format( "Error obtaining compiled view definition {0} for time {1} at version-correction {2}", getViewProcess().getDefinitionId(), compilationValuationTime, versionCorrection); s_logger.error(message); cycleExecutionFailed(executionOptions, new OpenGammaRuntimeException(message, e)); return; } try { if (getExecutionOptions().getFlags().contains(ViewExecutionFlags.AWAIT_MARKET_DATA)) { marketDataSnapshot.init( compiledViewDefinition.getMarketDataRequirements().keySet(), MARKET_DATA_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } else { marketDataSnapshot.init(); } if (executionOptions.getValuationTime() == null) { executionOptions.setValuationTime(marketDataSnapshot.getSnapshotTime()); } } catch (Exception e) { s_logger.error("Error initializing snapshot {}", marketDataSnapshot); cycleExecutionFailed( executionOptions, new OpenGammaRuntimeException("Error initializing snapshot" + marketDataSnapshot, e)); } EngineResourceReference<SingleComputationCycle> cycleReference; try { cycleReference = createCycle(executionOptions, compiledViewDefinition, versionCorrection); } catch (Exception e) { s_logger.error("Error creating next view cycle for view process " + getViewProcess(), e); return; } if (_executeCycles) { try { final SingleComputationCycle singleComputationCycle = cycleReference.get(); final Set<String> configurationNames = singleComputationCycle.getAllCalculationConfigurationNames(); final HashMap<String, Collection<ComputationTarget>> configToComputationTargets = new HashMap<String, Collection<ComputationTarget>>(); for (String configName : configurationNames) { DependencyGraph dependencyGraph = singleComputationCycle.getExecutableDependencyGraph(configName); configToComputationTargets.put(configName, dependencyGraph.getAllComputationTargets()); } final HashMap<String, Map<ValueSpecification, Set<ValueRequirement>>> configToTerminalOutputs = new HashMap<String, Map<ValueSpecification, Set<ValueRequirement>>>(); for (String configName : configurationNames) { DependencyGraph dependencyGraph = singleComputationCycle.getExecutableDependencyGraph(configName); configToTerminalOutputs.put(configName, dependencyGraph.getTerminalOutputs()); } cycleInitiated( new SimpleCycleInfo( marketDataSnapshot.getUniqueId(), compiledViewDefinition.getViewDefinition().getUniqueId(), versionCorrection, executionOptions.getValuationTime(), configurationNames, configToComputationTargets, configToTerminalOutputs)); executeViewCycle( cycleType, cycleReference, marketDataSnapshot, getViewProcess().getCalcJobResultExecutorService()); } catch (InterruptedException e) { // Execution interrupted - don't propagate as failure s_logger.info("View cycle execution interrupted for view process {}", getViewProcess()); cycleReference.release(); return; } catch (Exception e) { // Execution failed s_logger.error("View cycle execution failed for view process " + getViewProcess(), e); cycleReference.release(); cycleExecutionFailed(executionOptions, e); return; } } // Don't push the results through if we've been terminated, since another computation job could // be running already // and the fact that we've been terminated means the view is no longer interested in the result. // Just die quietly. if (isTerminated()) { cycleReference.release(); return; } if (_executeCycles) { cycleCompleted(cycleReference.get()); } if (getExecutionOptions().getExecutionSequence().isEmpty()) { processCompleted(); } if (_executeCycles) { if (_previousCycleReference != null) { _previousCycleReference.release(); } _previousCycleReference = cycleReference; } }