diff options
author | Eike Stepper | 2012-10-30 12:18:10 +0000 |
---|---|---|
committer | Eike Stepper | 2012-10-30 12:18:10 +0000 |
commit | 6b6d28260f12b40eff4bb93d829d339c0c3fd0cb (patch) | |
tree | 0d7291405d062f777f2c80cf74ed0f97bab4f605 /plugins/org.eclipse.emf.cdo.server.db | |
parent | cd7c758a5c0887ea43c769c4aa276a3157debe6a (diff) | |
download | cdo-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')
-rw-r--r-- | plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/SmartPreparedStatementCache.java | 273 |
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 + "]"; } } } |