diff options
5 files changed, 167 insertions, 12 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java index d56e94911c..ab3396f098 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalMappingStrategy.java @@ -77,6 +77,11 @@ public abstract class AbstractHorizontalMappingStrategy extends AbstractMappingS { } + public IObjectTypeMapper getObjectTypeMapper() + { + return objectTypeMapper; + } + public CDOClassifierRef readObjectType(IDBStoreAccessor accessor, CDOID id) { return objectTypeMapper.getObjectType(accessor, id); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_534438_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_534438_Test.java new file mode 100644 index 0000000000..837d2ad32b --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_534438_Test.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 Eike Stepper (Berlin, Germany) and others. + * 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 + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.tests.bugzilla; + +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.server.internal.db.DBStore; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.AbstractHorizontalMappingStrategy; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.tests.AbstractCDOTest; +import org.eclipse.emf.cdo.tests.model1.Supplier; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.view.CDOQuery; + +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +import org.eclipse.emf.ecore.resource.Resource; + +import java.util.List; + +/** + * Bug 534438: Too many errors like java.lang.IllegalStateException: SELECT CDO_CLASS FROM CDO_OBJECTS WHERE CDO_ID=? already in cache. + * + * @author Eike Stepper + */ +public class Bugzilla_534438_Test extends AbstractCDOTest +{ + public void testDBConnectionUsedByMultipleThreads() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + for (int i = 0; i < 1000; i++) + { + CDOResource resource = transaction.createResource(getResourcePath("resource-" + i + "-fill.transformation")); + resource.getContents().add(getModel1Factory().createSupplier()); + } + + transaction.commit(); + + // Clear revision cache to force ObjectTypeTable to be used. + clearCache(getRepository().getRevisionManager()); + + // Clear object type cache to force ObjectTypeTable to be used. + AbstractHorizontalMappingStrategy mappingStrategy = (AbstractHorizontalMappingStrategy)((DBStore)getRepository().getStore()).getMappingStrategy(); + LifecycleUtil.deactivate(mappingStrategy.getObjectTypeMapper()); + LifecycleUtil.activate(mappingStrategy.getObjectTypeMapper()); + + CDOQuery query = transaction.createQuery("ocl", "Supplier.allInstances()->select( o | o.oclAsType(ecore::EObject).eResource()." + + "oclAsType(eresource::CDOResource).name.endsWith('-fill.transformation'))", getModel1Package().getSupplier()); + + List<Supplier> result = query.getResult(); + msg(result.size()); + + for (Supplier supplier : result) + { + Resource resource = supplier.eResource(); + resource.delete(null); + } + + transaction.commit(); + } +} diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBConnection.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBConnection.java index 13e66b454d..1024b9e6a0 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBConnection.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBConnection.java @@ -18,13 +18,13 @@ import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; import org.eclipse.net4j.db.IDBSchemaTransaction; import org.eclipse.net4j.db.jdbc.DelegatingConnection; import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.collection.HashBag; +import org.eclipse.net4j.util.om.OMPlatform; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.HashSet; -import java.util.Set; import java.util.TreeMap; /** @@ -32,12 +32,16 @@ import java.util.TreeMap; */ public final class DBConnection extends DelegatingConnection implements IDBConnection { + private static final boolean VALIDATE_CHECKOUTS = OMPlatform.INSTANCE.isProperty("org.eclipse.net4j.internal.db.DBConnection.VALIDATE_CHECKOUTS"); + private final TreeMap<String, DBPreparedStatement> cache = new TreeMap<String, DBPreparedStatement>(); - private final Set<DBPreparedStatement> checkOuts = new HashSet<DBPreparedStatement>(); + private HashBag<DBPreparedStatement> checkOuts; private final DBDatabase database; + private int cacheSize; + private int lastTouch; private boolean closed; @@ -47,6 +51,11 @@ public final class DBConnection extends DelegatingConnection implements IDBConne super(delegate); this.database = database; + if (VALIDATE_CHECKOUTS) + { + checkOuts = new HashBag<DBPreparedStatement>(); + } + try { delegate.setAutoCommit(false); @@ -141,8 +150,22 @@ public final class DBConnection extends DelegatingConnection implements IDBConne throw new DBException(ex); } } + else + { + --cacheSize; - checkOuts.add(preparedStatement); + DBPreparedStatement nextCached = preparedStatement.getNextCached(); + if (nextCached != null) + { + cache.put(sql, nextCached); + preparedStatement.setNextCached(null); + } + } + + if (VALIDATE_CHECKOUTS) + { + checkOuts.add(preparedStatement); + } } return preparedStatement; @@ -172,19 +195,33 @@ public final class DBConnection extends DelegatingConnection implements IDBConne synchronized (this) { - checkOuts.remove(preparedStatement); + if (VALIDATE_CHECKOUTS) + { + checkOuts.remove(preparedStatement); + } + preparedStatement.setTouch(++lastTouch); String sql = preparedStatement.getSQL(); - if (cache.put(sql, preparedStatement) != null) + DBPreparedStatement cached = cache.put(sql, preparedStatement); + if (cached != null) { - throw new IllegalStateException(sql + " already in cache"); //$NON-NLS-1$ + preparedStatement.setNextCached(cached); } - if (cache.size() > database.getStatementCacheCapacity()) + if (++cacheSize > database.getStatementCacheCapacity()) { - DBPreparedStatement old = cache.remove(cache.firstKey()); + String firstKey = cache.firstKey(); + DBPreparedStatement old = cache.remove(firstKey); + DBPreparedStatement nextCached = old.getNextCached(); + DBUtil.close(old.getDelegate()); + --cacheSize; + + if (nextCached != null) + { + cache.put(firstKey, nextCached); + } } } } @@ -198,16 +235,25 @@ public final class DBConnection extends DelegatingConnection implements IDBConne { synchronized (this) { - CheckUtil.checkState(checkOuts.isEmpty(), "Statements are checked out: " + checkOuts); + if (VALIDATE_CHECKOUTS) + { + CheckUtil.checkState(checkOuts.isEmpty(), "Statements are checked out: " + checkOuts); + } // Close all statements in the cache, then clear the cache. for (DBPreparedStatement preparedStatement : cache.values()) { - PreparedStatement delegate = preparedStatement.getDelegate(); - DBUtil.close(delegate); + while (preparedStatement != null) + { + PreparedStatement delegate = preparedStatement.getDelegate(); + DBUtil.close(delegate); + + preparedStatement = preparedStatement.getNextCached(); + } } cache.clear(); + cacheSize = 0; } } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java index 19fceabfd1..92252658c4 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java @@ -30,6 +30,8 @@ public class DBPreparedStatement extends DelegatingPreparedStatement implements private int touch; + private DBPreparedStatement nextCached; + public DBPreparedStatement(DBConnection transaction, String sql, ReuseProbability reuseProbability, PreparedStatement delegate) { super(delegate, transaction); @@ -71,6 +73,16 @@ public class DBPreparedStatement extends DelegatingPreparedStatement implements this.touch = touch; } + public DBPreparedStatement getNextCached() + { + return nextCached; + } + + public void setNextCached(DBPreparedStatement nextCached) + { + this.nextCached = nextCached; + } + public int compareTo(IDBPreparedStatement o) { int result = reuseProbability.compareTo(o.getReuseProbability()); diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java index 3b4814eb8c..ceb96f9f90 100644 --- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java @@ -226,6 +226,28 @@ public final class ReflectUtil } } + /** + * @since 3.8 + */ + public static String dumpThread() + { + StringBuilder builder = new StringBuilder(); + + Thread thread = Thread.currentThread(); + builder.append(thread); + builder.append(StringUtil.NL); + + StackTraceElement[] stackTrace = thread.getStackTrace(); + for (int i = 2; i < stackTrace.length; i++) + { + StackTraceElement stackTraceElement = stackTrace[i]; + builder.append("\tat " + stackTraceElement); //$NON-NLS-1$ + builder.append(StringUtil.NL); + } + + return builder.toString(); + } + public static void printStackTrace(PrintStream out, StackTraceElement[] stackTrace) { for (int i = 2; i < stackTrace.length; i++) |