Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2012-10-30 12:18:10 +0000
committerEike Stepper2012-10-30 12:18:10 +0000
commit6b6d28260f12b40eff4bb93d829d339c0c3fd0cb (patch)
tree0d7291405d062f777f2c80cf74ed0f97bab4f605 /plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo
parentcd7c758a5c0887ea43c769c4aa276a3157debe6a (diff)
downloadcdo-6b6d28260f12b40eff4bb93d829d339c0c3fd0cb.tar.gz
cdo-6b6d28260f12b40eff4bb93d829d339c0c3fd0cb.tar.xz
cdo-6b6d28260f12b40eff4bb93d829d339c0c3fd0cb.zip
[364105] [DB] ImplementationError: "SELECT ..." already in cache
https://bugs.eclipse.org/bugs/show_bug.cgi?id=364105
Diffstat (limited to 'plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo')
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java273
1 files changed, 74 insertions, 199 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java
index d9c4204be3..253be8541b 100644
--- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java
+++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java
@@ -13,6 +13,7 @@ package org.eclipse.emf.cdo.server.internal.db;
import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBUtil;
import org.eclipse.net4j.util.ImplementationError;
import java.sql.Connection;
@@ -20,6 +21,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
+import java.util.TreeMap;
/**
* @author Stefan Winkler
@@ -27,266 +29,139 @@ import java.util.Map;
*/
public class SmartPreparedStatementCache extends AbstractPreparedStatementCache
{
- private Cache cache;
+ private final int capacity;
- private Map<PreparedStatement, CachedPreparedStatement> checkedOut = new HashMap<PreparedStatement, CachedPreparedStatement>();
+ private TreeMap<String, CachedPreparedStatement> cache = new TreeMap<String, CachedPreparedStatement>();
+
+ private Map<PreparedStatement, CachedPreparedStatement> checkOuts = new HashMap<PreparedStatement, CachedPreparedStatement>();
+
+ private long lastTouch;
public SmartPreparedStatementCache(int capacity)
{
- cache = new Cache(capacity);
+ this.capacity = capacity;
}
- public PreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability)
+ public final int getCapacity()
{
- CachedPreparedStatement cachedStatement = cache.remove(sql);
- if (cachedStatement == null)
- {
- cachedStatement = createCachedPreparedStatement(sql, reuseProbability);
- }
-
- PreparedStatement result = cachedStatement.getPreparedStatement();
- checkedOut.put(result, cachedStatement);
-
- return result;
+ return capacity;
}
- /**
- * @param ps
- * the prepared statement to be released to the cache, or <code>null</code>.
- */
- public void releasePreparedStatement(PreparedStatement ps)
+ public int size()
{
- if (ps != null) // Bug 276926: Silently accept ps == null and do nothing.
- {
- CachedPreparedStatement cachedStatement = checkedOut.remove(ps);
- cache.put(cachedStatement);
- }
+ return cache.size();
}
- @Override
- protected void doBeforeDeactivate() throws Exception
+ public PreparedStatement getPreparedStatement(String sql, ReuseProbability probability)
{
- if (!checkedOut.isEmpty())
- {
- OM.LOG.warn("Statement leak detected"); //$NON-NLS-1$
- }
- }
+ PreparedStatement preparedStatement;
- private CachedPreparedStatement createCachedPreparedStatement(String sql, ReuseProbability reuseProbability)
- {
- try
+ CachedPreparedStatement cachedStatement = cache.remove(sql);
+ if (cachedStatement == null)
{
- Connection connection = getConnection();
- PreparedStatement stmt = connection.prepareStatement(sql);
- return new CachedPreparedStatement(sql, reuseProbability, stmt);
+ try
+ {
+ Connection connection = getConnection();
+ preparedStatement = connection.prepareStatement(sql);
+ cachedStatement = new CachedPreparedStatement(sql, preparedStatement, probability);
+ }
+ catch (SQLException ex)
+ {
+ throw new DBException(ex);
+ }
}
- catch (SQLException ex)
+ else
{
- throw new DBException(ex);
+ preparedStatement = cachedStatement.getPreparedStatement();
}
+
+ checkOuts.put(preparedStatement, cachedStatement);
+ return preparedStatement;
}
- /**
- * @author Stefan Winkler
- */
- private static final class Cache
+ public void releasePreparedStatement(PreparedStatement preparedStatement)
{
- private CacheList lists[];
-
- private HashMap<String, CachedPreparedStatement> lookup;
-
- private int capacity;
-
- public Cache(int capacity)
+ if (preparedStatement == null)
{
- this.capacity = capacity;
-
- lookup = new HashMap<String, CachedPreparedStatement>(capacity);
-
- lists = new CacheList[ReuseProbability.values().length];
- for (ReuseProbability prob : ReuseProbability.values())
- {
- lists[prob.ordinal()] = new CacheList();
- }
+ // Bug 276926: Silently accept preparedStatement == null and do nothing.
+ return;
}
- public void put(CachedPreparedStatement cachedStatement)
- {
- // refresh age
- cachedStatement.touch();
-
- // put into appripriate list
- lists[cachedStatement.getProbability().ordinal()].add(cachedStatement);
-
- // put into lookup table
- if (lookup.put(cachedStatement.getSQL(), cachedStatement) != null)
- {
- throw new ImplementationError(cachedStatement.getSQL() + " already in cache"); //$NON-NLS-1$
- }
+ CachedPreparedStatement cachedStatement = checkOuts.remove(preparedStatement);
+ cachedStatement.setTouch(++lastTouch);
- // handle capacity overflow
- if (lookup.size() > capacity)
- {
- evictOne();
- }
- }
-
- private void evictOne()
+ String sql = cachedStatement.getSQL();
+ if (cache.put(sql, cachedStatement) != null)
{
- long maxAge = -1;
- int ordinal = -1;
-
- for (ReuseProbability prob : ReuseProbability.values())
- {
- if (!lists[prob.ordinal()].isEmpty())
- {
- long age = lists[prob.ordinal()].tail().getAge();
- if (maxAge < age)
- {
- maxAge = age;
- ordinal = prob.ordinal();
- }
- }
- }
-
- remove(lists[ordinal].tail().getSQL());
+ throw new ImplementationError(sql + " already in cache"); //$NON-NLS-1$
}
- public CachedPreparedStatement remove(String sql)
+ if (cache.size() > capacity)
{
- CachedPreparedStatement result = lookup.remove(sql);
- if (result == null)
- {
- return null;
- }
-
- lists[result.getProbability().ordinal()].remove(result);
- return result;
+ CachedPreparedStatement old = cache.remove(cache.firstKey());
+ DBUtil.close(old.getPreparedStatement());
}
+ }
- /**
- * @author Stefan Winkler
- */
- private class CacheList
+ @Override
+ protected void doBeforeDeactivate() throws Exception
+ {
+ if (!checkOuts.isEmpty())
{
- private CachedPreparedStatement first;
-
- private CachedPreparedStatement last;
-
- public CacheList()
- {
- }
-
- public void add(CachedPreparedStatement s)
- {
- if (first == null)
- {
- first = s;
- last = s;
- s.previous = null;
- s.next = null;
- }
- else
- {
- first.previous = s;
- s.next = first;
- first = s;
- }
- }
-
- public void remove(CachedPreparedStatement s)
- {
- if (s == first)
- {
- first = s.next;
- }
-
- if (s.next != null)
- {
- s.next.previous = s.previous;
- }
-
- if (s == last)
- {
- last = s.previous;
- }
-
- if (s.previous != null)
- {
- s.previous.next = s.next;
- }
-
- s.previous = null;
- s.next = null;
- }
-
- public CachedPreparedStatement tail()
- {
- return last;
- }
-
- public boolean isEmpty()
- {
- return first == null;
- }
+ OM.LOG.warn("Statement leak detected"); //$NON-NLS-1$
}
}
/**
* @author Stefan Winkler
*/
- private static final class CachedPreparedStatement
+ private static final class CachedPreparedStatement implements Comparable<CachedPreparedStatement>
{
- private long timeStamp;
-
private String sql;
- private ReuseProbability probability;
-
- private PreparedStatement statement;
+ private PreparedStatement preparedStatement;
- /**
- * DL field
- */
- private CachedPreparedStatement previous;
+ private ReuseProbability probability;
- /**
- * DL field
- */
- private CachedPreparedStatement next;
+ private long touch;
- public CachedPreparedStatement(String sql, ReuseProbability prob, PreparedStatement stmt)
+ public CachedPreparedStatement(String sql, PreparedStatement preparedStatement, ReuseProbability probability)
{
this.sql = sql;
- probability = prob;
- statement = stmt;
- timeStamp = System.currentTimeMillis();
+ this.preparedStatement = preparedStatement;
+ this.probability = probability;
}
- public PreparedStatement getPreparedStatement()
+ public String getSQL()
{
- return statement;
+ return sql;
}
- public long getAge()
+ public PreparedStatement getPreparedStatement()
{
- long currentTime = System.currentTimeMillis();
- return (currentTime - timeStamp) * probability.ordinal();
+ return preparedStatement;
}
- public void touch()
+ public void setTouch(long touch)
{
- timeStamp = System.currentTimeMillis();
+ this.touch = touch;
}
- public String getSQL()
+ public int compareTo(CachedPreparedStatement o)
{
- return sql;
+ int result = probability.compareTo(o.probability);
+ if (result == 0)
+ {
+ result = (int)(o.touch - touch);
+ }
+
+ return result;
}
- public ReuseProbability getProbability()
+ @Override
+ public String toString()
{
- return probability;
+ return "CachedPreparedStatement[sql=" + sql + ", probability=" + probability + ", touch=" + touch + "]";
}
}
}

Back to the top