diff options
author | Eike Stepper | 2013-03-06 10:31:46 +0000 |
---|---|---|
committer | Eike Stepper | 2013-03-06 10:31:46 +0000 |
commit | 5314902e749c616cd4a838915dff32cf52f069be (patch) | |
tree | 522c1a455c9fae2ddb0e132eaa1a86e550d6693a | |
parent | 5a6cbe0175d11dd6e9582e3269157de9861b1904 (diff) | |
download | cdo-committers/estepper/net4j-db.tar.gz cdo-committers/estepper/net4j-db.tar.xz cdo-committers/estepper/net4j-db.zip |
[401763] Make CDO Server more robust against data dictionary changes committers/estepper/net4j-db
https://bugs.eclipse.org/bugs/show_bug.cgi?id=401763
4 files changed, 205 insertions, 35 deletions
diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java index a79bf1e563..f1c316eba4 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBDatabase.java @@ -11,6 +11,7 @@ package org.eclipse.net4j.db; import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.util.collection.Closeable; import org.eclipse.net4j.util.container.IContainer; /** @@ -19,7 +20,7 @@ import org.eclipse.net4j.util.container.IContainer; * @noextend This interface is not intended to be extended by clients. * @since 4.2 */ -public interface IDBDatabase extends IContainer<IDBTransaction> +public interface IDBDatabase extends IContainer<IDBTransaction>, Closeable { public static final int DEFAULT_STATEMENT_CACHE_CAPACITY = 200; diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBTransaction.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBTransaction.java index 5f2e1a3157..42b19fa0f4 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBTransaction.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBTransaction.java @@ -13,6 +13,8 @@ package org.eclipse.net4j.db; import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; import org.eclipse.net4j.util.collection.Closeable; +import java.sql.Connection; + /** * @since 4.2 * @author Eike Stepper @@ -24,7 +26,9 @@ public interface IDBTransaction extends Closeable { public IDBDatabase getDatabase(); - public IDBPreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability); + public Connection getConnection(); public IDBSchemaTransaction openSchemaTransaction(); + + public IDBPreparedStatement prepareStatement(String sql, ReuseProbability reuseProbability); } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java index ed44f62b30..4af7eb9d01 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java @@ -17,10 +17,12 @@ import org.eclipse.net4j.db.IDBDatabase; import org.eclipse.net4j.db.IDBTransaction; import org.eclipse.net4j.spi.db.DBAdapter; import org.eclipse.net4j.spi.db.DBSchema; +import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.container.SetContainer; import java.sql.Connection; import java.sql.SQLException; +import java.util.LinkedList; /** * @author Eike Stepper @@ -31,19 +33,21 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID private IDBConnectionProvider connectionProvider; + private int statementCacheCapacity = DEFAULT_STATEMENT_CACHE_CAPACITY; + private DBSchema schema; private DBSchemaTransaction schemaTransaction; - private int statementCacheCapacity = DEFAULT_STATEMENT_CACHE_CAPACITY; + private final LinkedList<SchemaAccess> schemaAccessQueue = new LinkedList<SchemaAccess>(); - public DBDatabase(final DBAdapter adapter, IDBConnectionProvider dbConnectionProvider, final String schemaName) + public DBDatabase(final DBAdapter adapter, IDBConnectionProvider connectionProvider, final String schemaName) { super(IDBTransaction.class); this.adapter = adapter; - connectionProvider = dbConnectionProvider; + this.connectionProvider = connectionProvider; - schema = DBUtil.execute(dbConnectionProvider, new RunnableWithConnection<DBSchema>() + schema = DBUtil.execute(connectionProvider, new RunnableWithConnection<DBSchema>() { public DBSchema run(Connection connection) throws SQLException { @@ -52,6 +56,7 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID }); schema.lock(); + activate(); } public DBAdapter getAdapter() @@ -59,6 +64,21 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID return adapter; } + public IDBConnectionProvider getConnectionProvider() + { + return connectionProvider; + } + + public int getStatementCacheCapacity() + { + return statementCacheCapacity; + } + + public void setStatementCacheCapacity(int statementCacheCapacity) + { + this.statementCacheCapacity = statementCacheCapacity; + } + public DBSchema getSchema() { return schema; @@ -66,6 +86,8 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID public DBSchemaTransaction openSchemaTransaction() { + beginSchemaAccess(true); + DBSchemaTransaction schemaTransaction = new DBSchemaTransaction(this); this.schemaTransaction = schemaTransaction; return schemaTransaction; @@ -73,7 +95,18 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID public void closeSchemaTransaction() { - schemaTransaction = null; + try + { + for (IDBTransaction transaction : getTransactions()) + { + ((DBTransaction)transaction).invalidateStatementCache(); + } + } + finally + { + schemaTransaction = null; + endSchemaAccess(); + } } public DBSchemaTransaction getSchemaTransaction() @@ -81,11 +114,6 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID return schemaTransaction; } - public IDBConnectionProvider getConnectionProvider() - { - return connectionProvider; - } - public DBTransaction openTransaction() { DBTransaction transaction = new DBTransaction(this); @@ -103,13 +131,126 @@ public final class DBDatabase extends SetContainer<IDBTransaction> implements ID return getElements(); } - public int getStatementCacheCapacity() + public boolean isClosed() { - return statementCacheCapacity; + return !isActive(); } - public void setStatementCacheCapacity(int statementCacheCapacity) + public void close() + { + deactivate(); + } + + @Override + protected void doDeactivate() throws Exception + { + for (IDBTransaction transaction : getTransactions()) + { + transaction.close(); + } + + super.doDeactivate(); + } + + public void beginSchemaAccess(boolean write) + { + SchemaAccess schemaAccess = null; + synchronized (schemaAccessQueue) + { + if (write) + { + schemaAccess = new WriteSchemaAccess(); + schemaAccessQueue.addLast(schemaAccess); + } + else + { + if (!schemaAccessQueue.isEmpty()) + { + schemaAccess = schemaAccessQueue.getFirst(); + if (schemaAccess instanceof ReadSchemaAccess) + { + ReadSchemaAccess readSchemaAccess = (ReadSchemaAccess)schemaAccess; + readSchemaAccess.incrementReaders(); + } + } + + if (schemaAccess == null) + { + schemaAccess = new ReadSchemaAccess(); + schemaAccessQueue.addLast(schemaAccess); + } + } + } + + for (;;) + { + synchronized (schemaAccessQueue) + { + if (schemaAccessQueue.getFirst() == schemaAccess) + { + return; + } + + try + { + schemaAccessQueue.wait(); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + } + } + + public void endSchemaAccess() + { + synchronized (schemaAccessQueue) + { + SchemaAccess schemaAccess = schemaAccessQueue.getFirst(); + if (schemaAccess instanceof ReadSchemaAccess) + { + ReadSchemaAccess readSchemaAccess = (ReadSchemaAccess)schemaAccess; + if (readSchemaAccess.decrementReaders()) + { + return; + } + } + + schemaAccessQueue.removeFirst(); + schemaAccessQueue.notifyAll(); + } + } + + /** + * @author Eike Stepper + */ + public interface SchemaAccess + { + } + + /** + * @author Eike Stepper + */ + public final class ReadSchemaAccess implements SchemaAccess + { + private int readers; + + public void incrementReaders() + { + ++readers; + } + + public boolean decrementReaders() + { + return --readers > 0; + } + } + + /** + * @author Eike Stepper + */ + public final class WriteSchemaAccess implements SchemaAccess { - this.statementCacheCapacity = statementCacheCapacity; } } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBTransaction.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBTransaction.java index 1e44b5a670..a8f2309416 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBTransaction.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBTransaction.java @@ -16,6 +16,7 @@ import org.eclipse.net4j.db.IDBPreparedStatement; import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; import org.eclipse.net4j.db.IDBSchemaTransaction; import org.eclipse.net4j.db.IDBTransaction; +import org.eclipse.net4j.util.CheckUtil; import java.sql.Connection; import java.sql.PreparedStatement; @@ -69,8 +70,17 @@ public final class DBTransaction implements IDBTransaction return connection; } - public IDBPreparedStatement getPreparedStatement(String sql, ReuseProbability reuseProbability) + public IDBSchemaTransaction openSchemaTransaction() + { + DBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); + schemaTransaction.setTransaction(this); + return schemaTransaction; + } + + public IDBPreparedStatement prepareStatement(String sql, ReuseProbability reuseProbability) { + database.beginSchemaAccess(false); + DBPreparedStatement preparedStatement = cache.remove(sql); if (preparedStatement == null) { @@ -91,32 +101,46 @@ public final class DBTransaction implements IDBTransaction public void releasePreparedStatement(DBPreparedStatement preparedStatement) { - if (preparedStatement == null) + try { - // Bug 276926: Silently accept preparedStatement == null and do nothing. - return; - } + if (preparedStatement == null) + { + // Bug 276926: Silently accept preparedStatement == null and do nothing. + return; + } - checkOuts.remove(preparedStatement); - preparedStatement.setTouch(++lastTouch); + checkOuts.remove(preparedStatement); + preparedStatement.setTouch(++lastTouch); - String sql = preparedStatement.getSQL(); - if (cache.put(sql, preparedStatement) != null) - { - throw new IllegalStateException(sql + " already in cache"); //$NON-NLS-1$ - } + String sql = preparedStatement.getSQL(); + if (cache.put(sql, preparedStatement) != null) + { + throw new IllegalStateException(sql + " already in cache"); //$NON-NLS-1$ + } - if (cache.size() > database.getStatementCacheCapacity()) + if (cache.size() > database.getStatementCacheCapacity()) + { + DBPreparedStatement old = cache.remove(cache.firstKey()); + DBUtil.close(old.getDelegate()); + } + } + finally { - DBPreparedStatement old = cache.remove(cache.firstKey()); - DBUtil.close(old.getDelegate()); + database.endSchemaAccess(); } } - public IDBSchemaTransaction openSchemaTransaction() + public void invalidateStatementCache() { - DBSchemaTransaction schemaTransaction = database.openSchemaTransaction(); - schemaTransaction.setTransaction(this); - return schemaTransaction; + 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); + } + + cache.clear(); } } |