/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.exec.plan; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.engine.depgraph.DependencyGraph; import com.opengamma.engine.function.FunctionParameters; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.engine.view.impl.ExecutionLogModeSource; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.ehcache.EHCacheUtils; /** * Caches the plans produced by other execution planners. */ public class CachingExecutionPlanner implements GraphExecutionPlanner { // NOTE: This class has been created for completeness, to preserve the previous behaviours of ExecutionPlanCache, even though those behaviours are unlikely to be correct. private static final Logger s_logger = LoggerFactory.getLogger(CachingExecutionPlanner.class); private static final String CACHE_NAME = "executionPlans"; /* package */static final class CacheKey implements Serializable { private static final long serialVersionUID = 1L; private DependencyGraph _graph; private long _functionInitId; private Set<ValueSpecification> _sharedValues; private Map<ValueSpecification, FunctionParameters> _parameters; public CacheKey(final DependencyGraph graph, final long functionInitId, final Set<ValueSpecification> sharedValues, final Map<ValueSpecification, FunctionParameters> parameters) { _graph = graph; _functionInitId = functionInitId; _sharedValues = new HashSet<ValueSpecification>(sharedValues); _parameters = new HashMap<ValueSpecification, FunctionParameters>(parameters); } @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof CacheKey)) { return false; } final CacheKey other = (CacheKey) o; if (_functionInitId != other._functionInitId) { return false; } if (!_sharedValues.equals(other._sharedValues)) { return false; } if (!_parameters.equals(other._parameters)) { return false; } return _graph.equals(other._graph); } @Override public int hashCode() { int hc = 0; hc += (hc << 4) + (int) (_functionInitId ^ (_functionInitId >>> 32)); hc += (hc << 4) + _graph.hashCode(); hc += (hc << 4) + _sharedValues.hashCode(); hc += (hc << 4) + _parameters.hashCode(); return hc; } } private final GraphExecutionPlanner _underlying; private final Cache _cache; /** * Constructs an instance. * * @param underlying the underlying execution planner, not null * @param manager the cache manager from which to obtain the execution plan cache not null */ public CachingExecutionPlanner(final GraphExecutionPlanner underlying, final CacheManager manager) { ArgumentChecker.notNull(underlying, "underlying"); ArgumentChecker.notNull(manager, "manager"); _underlying = underlying; EHCacheUtils.addCache(manager, CACHE_NAME); _cache = EHCacheUtils.getCacheFromManager(manager, CACHE_NAME); } public synchronized void invalidate() { if (_cache != null) { s_logger.info("Clearing execution plan cache of {} items", _cache.getSize()); _cache.removeAll(); } } // GraphExecutionPlanner @Override public GraphExecutionPlan createPlan(final DependencyGraph graph, final ExecutionLogModeSource logModeSource, final long functionInitId, final Set<ValueSpecification> sharedValues, final Map<ValueSpecification, FunctionParameters> parameters) { // NOTE: The logModeSource is not used as part of the key; this is wrong as the plan contains job items which embed the logging requirements s_logger.debug("Searching for cached execution plan for {}/{}", graph, functionInitId); CacheKey key = new CacheKey(graph, functionInitId, sharedValues, parameters); final Element element = _cache.get(key); if (element != null) { s_logger.debug("Cache hit"); return ((GraphExecutionPlan) element.getObjectValue()).withCalculationConfiguration(graph.getCalculationConfigurationName()); } else { s_logger.debug("Cache miss"); final GraphExecutionPlan plan = _underlying.createPlan(graph, logModeSource, functionInitId, sharedValues, parameters); if (plan != null) { _cache.put(new Element(key, plan)); } return plan; } } /** * Call this at the end of a unit test run to clear the state of EHCache. It should not be part of a generic lifecycle method. */ protected void shutdown() { _cache.getCacheManager().removeCache(CACHE_NAME); } // TODO [ENG-269] If the function costs change significantly, invalidate the execution plan cache. }