/******************************************************************************* * Copyright (c) 2008 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * File: $Source$ * Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * Created on: Dec 17, 2008 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.datasource.services; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.openanzo.analysis.RequestAnalysis; import org.openanzo.cache.ICache; import org.openanzo.cache.ICacheListener; import org.openanzo.cache.ICacheProvider; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.LogUtils; import org.openanzo.glitter.dataset.QueryDataset; import org.openanzo.glitter.query.QueryResults; import org.openanzo.rdf.URI; import org.openanzo.rdf.utils.CopyOnWriteMultiHashMap; import org.openanzo.rdf.utils.UriGenerator; import org.openanzo.services.IOperationContext; import org.openanzo.services.IUpdateResultListener; import org.openanzo.services.IUpdateTransaction; import org.openanzo.services.IUpdates; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The query cache * * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>) * */ public class QueryCache implements IUpdateResultListener, IQueryCache { static final Logger log = LoggerFactory.getLogger(QueryCache.class); ICache<String, QueryResults> cache; CopyOnWriteMultiHashMap<URI, String> graphCache = new CopyOnWriteMultiHashMap<URI, String>(); final Lock lock = new ReentrantLock(); /** * Query cache for the query service * * @param name * name of datasource * @param provider * cache provider */ public QueryCache(String name, ICacheProvider provider) { this.cache = provider.openCache(name + "_" + "QueryService", 1000, false); this.cache.registerListener(new ICacheListener<String, QueryResults>() { public void elementRemoved(String queryString, QueryResults result) { Set<URI> allGraphs = new HashSet<URI>(); org.openanzo.rdf.utils.Collections.addAllIfNotNull(result.getQueryDataset().getDefaultGraphURIs(), allGraphs); org.openanzo.rdf.utils.Collections.addAllIfNotNull(result.getQueryDataset().getNamedGraphURIs(), allGraphs); for (URI uri : allGraphs) { graphCache.remove(uri, queryString); } } }); for (String key : this.cache.keySet()) { QueryResults result = this.cache.get(key); Set<URI> allGraphs = new HashSet<URI>(); org.openanzo.rdf.utils.Collections.addAllIfNotNull(result.getQueryDataset().getDefaultGraphURIs(), allGraphs); org.openanzo.rdf.utils.Collections.addAllIfNotNull(result.getQueryDataset().getNamedGraphURIs(), allGraphs); for (URI uri : allGraphs) { graphCache.put(uri, key); } } } public void reset() { lock.lock(); try { if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "QueryCache reset"); } cache.clear(); graphCache.clear(); } finally { lock.unlock(); } } public QueryResults findResults(String queryString, QueryDataset dataset) { lock.lock(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); try { if (dataset.allGraphs || dataset.defaultAllGraphs || dataset.allMetadataGraphs || dataset.defaultAllMetadataGraphs || dataset.allNamedGraphs || dataset.defaultAllNamedGraphs) { if (log.isDebugEnabled()) log.debug(LogUtils.DATASOURCE_MARKER, "Query Cache MISS (allDefault): " + queryString); return null; } boolean isEnabled = RequestAnalysis.getAnalysisLogger().isDebugEnabled(); long start = 0; if (log.isDebugEnabled() || isEnabled) { start = System.currentTimeMillis(); } QueryResults result = cache.get(queryString); if (log.isDebugEnabled() || isEnabled) { if (log.isDebugEnabled()) { long end = System.currentTimeMillis(); log.debug(LogUtils.DATASOURCE_MARKER, "Lookup cache time: " + (end - start)); } if (isEnabled) { RequestAnalysis.getAnalysisLogger().debug(LogUtils.TIMING_MARKER, "glitter_queryCacheLookupCache,{}", Long.toString(System.currentTimeMillis() - start)); } start = System.currentTimeMillis(); } if (result != null) { if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "QueryCache HIT:" + queryString); } return result; } if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "Query Cache MISS (string): " + queryString); } return null; } finally { lock.unlock(); Thread.currentThread().setContextClassLoader(cl); } } public void cacheResults(String queryString, QueryResults result, QueryDataset dataset) { lock.lock(); try { if (result.isConstructResult() || result.isDescribeResult()) { if ((result.isConstructResult() && result.getConstructResults().size() > 10000) || (result.isDescribeResult() && result.getDescribeResults().size() > 10000)) { if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "Not caching large results:" + queryString); } return; } } else if (result.isSelectResult()) { if (result.getSelectResults().size() > 10000) { if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "Not caching large results:" + queryString + " results(" + result.getSelectResults().size() + ")"); } return; } } if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "QueryCache CACHING:" + queryString); } cache.put(queryString, result); Set<URI> allGraphs = new HashSet<URI>(); org.openanzo.rdf.utils.Collections.addAllIfNotNull(dataset.getDefaultGraphURIs(), allGraphs); org.openanzo.rdf.utils.Collections.addAllIfNotNull(dataset.getNamedGraphURIs(), allGraphs); for (URI uri : allGraphs) { graphCache.put(uri, queryString); } } finally { lock.unlock(); } } public void updateComplete(IOperationContext context, IUpdates results) throws AnzoException { long start = 0; if (log.isDebugEnabled()) { start = System.currentTimeMillis(); } lock.lock(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); try { for (IUpdateTransaction transaction : results.getTransactions()) { for (URI ngUri : transaction.getUpdatedNamedGraphs().keySet()) { URI mdGraph = UriGenerator.generateMetadataGraphUri(ngUri); Collection<String> queries = graphCache.get(ngUri); if (queries != null) { for (String query : queries) { QueryResults result = cache.get(query); if (result != null) { if (null != cache.remove(query)) { if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "QueryCache Removed:" + query); } } for (URI g : result.getQueryDataset().getDefaultGraphURIs()) { graphCache.remove(g, query); } for (URI g : result.getQueryDataset().getNamedGraphURIs()) { graphCache.remove(g, query); } } } } graphCache.remove(ngUri); queries = graphCache.get(mdGraph); if (queries != null) { for (String query : queries) { QueryResults result = cache.get(query); if (result != null) { if (null != cache.remove(query)) { if (log.isDebugEnabled()) { log.debug(LogUtils.DATASOURCE_MARKER, "QueryCache Removed:" + query); } } for (URI g : result.getQueryDataset().getDefaultGraphURIs()) { graphCache.remove(g, query); } for (URI g : result.getQueryDataset().getNamedGraphURIs()) { graphCache.remove(g, query); } } } } graphCache.remove(mdGraph); } } } finally { if (log.isDebugEnabled()) { log.debug(LogUtils.TIMING_MARKER, "QUERY CACHE UPDATE,{}", (System.currentTimeMillis() - start)); } lock.unlock(); Thread.currentThread().setContextClassLoader(cl); } } }