HHH-6780 - Wrong Query timeout calculation
This commit is contained in:
parent
d00c9c85d8
commit
9a7924d9bc
|
@ -32,6 +32,7 @@ import java.sql.SQLException;
|
|||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.TransactionException;
|
||||
import org.hibernate.engine.jdbc.batch.spi.Batch;
|
||||
import org.hibernate.engine.jdbc.batch.spi.BatchBuilder;
|
||||
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
|
||||
|
@ -56,15 +57,17 @@ import org.hibernate.jdbc.WorkExecutorVisitable;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class JdbcCoordinatorImpl implements JdbcCoordinator {
|
||||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, JdbcCoordinatorImpl.class.getName());
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
|
||||
CoreMessageLogger.class, JdbcCoordinatorImpl.class.getName()
|
||||
);
|
||||
|
||||
private transient TransactionCoordinatorImpl transactionCoordinator;
|
||||
|
||||
private final transient LogicalConnectionImpl logicalConnection;
|
||||
|
||||
private transient Batch currentBatch;
|
||||
|
||||
private transient long transactionTimeOutInstant = -1;
|
||||
|
||||
public JdbcCoordinatorImpl(
|
||||
Connection userSuppliedConnection,
|
||||
TransactionCoordinatorImpl transactionCoordinator) {
|
||||
|
@ -153,6 +156,14 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
|
|||
return currentBatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeBatch() {
|
||||
if ( currentBatch != null ) {
|
||||
currentBatch.execute();
|
||||
currentBatch.release(); // needed?
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortBatch() {
|
||||
if ( currentBatch != null ) {
|
||||
|
@ -171,20 +182,26 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionTimeOut(int timeOut) {
|
||||
getStatementPreparer().setTransactionTimeOut( timeOut );
|
||||
public void setTransactionTimeOut(int seconds) {
|
||||
transactionTimeOutInstant = System.currentTimeMillis() + ( seconds * 1000 );
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called after local transaction completion. Used to conditionally
|
||||
* release the JDBC connection aggressively if the configured release mode
|
||||
* indicates.
|
||||
*/
|
||||
@Override
|
||||
public int determineRemainingTransactionTimeOutPeriod() {
|
||||
if ( transactionTimeOutInstant < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
final int secondsRemaining = (int) ((transactionTimeOutInstant - System.currentTimeMillis()) / 1000);
|
||||
if ( secondsRemaining <= 0 ) {
|
||||
throw new TransactionException( "transaction timeout expired" );
|
||||
}
|
||||
return secondsRemaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTransaction() {
|
||||
logicalConnection.afterTransaction();
|
||||
if ( statementPreparer != null ) {
|
||||
statementPreparer.unsetTransactionTimeOut();
|
||||
}
|
||||
transactionTimeOutInstant = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -210,13 +227,6 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
|
|||
}
|
||||
}
|
||||
|
||||
public void executeBatch() {
|
||||
if ( currentBatch != null ) {
|
||||
currentBatch.execute();
|
||||
currentBatch.release(); // needed?
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelLastQuery() {
|
||||
logicalConnection.getResourceRegistry().cancelLastQuery();
|
||||
|
|
|
@ -188,9 +188,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
|
|||
return buildConnectionProxy();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Connection close() {
|
||||
LOG.trace( "Closing logical connection" );
|
||||
Connection c = isUserSuppliedConnection ? physicalConnection : null;
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.sql.SQLException;
|
|||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.TransactionException;
|
||||
import org.hibernate.cfg.Settings;
|
||||
import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor;
|
||||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||
|
@ -41,7 +40,6 @@ import org.hibernate.engine.jdbc.spi.StatementPreparer;
|
|||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
class StatementPreparerImpl implements StatementPreparer {
|
||||
private long transactionTimeOut = -1;
|
||||
private JdbcCoordinatorImpl jdbcCoordinator;
|
||||
|
||||
StatementPreparerImpl(JdbcCoordinatorImpl jdbcCoordinator) {
|
||||
|
@ -156,16 +154,6 @@ class StatementPreparerImpl implements StatementPreparer {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionTimeOut(int timeOut) {
|
||||
transactionTimeOut = timeOut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsetTransactionTimeOut() {
|
||||
transactionTimeOut = -1;
|
||||
}
|
||||
|
||||
private abstract class StatementPreparationTemplate {
|
||||
protected final String sql;
|
||||
|
||||
|
@ -191,17 +179,11 @@ class StatementPreparerImpl implements StatementPreparer {
|
|||
}
|
||||
|
||||
private void setStatementTimeout(PreparedStatement preparedStatement) throws SQLException {
|
||||
if ( transactionTimeOut > 0 ) {
|
||||
int timeout = (int) ( transactionTimeOut - ( System.currentTimeMillis() / 1000 ) );
|
||||
if ( timeout <= 0 ) {
|
||||
throw new TransactionException( "transaction timeout expired" );
|
||||
}
|
||||
else {
|
||||
preparedStatement.setQueryTimeout( timeout );
|
||||
}
|
||||
final int remainingTransactionTimeOutPeriod = jdbcCoordinator.determineRemainingTransactionTimeOutPeriod();
|
||||
if ( remainingTransactionTimeOutPeriod > 0 ) {
|
||||
preparedStatement.setQueryTimeout( remainingTransactionTimeOutPeriod );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private abstract class QueryStatementPreparationTemplate extends StatementPreparationTemplate {
|
||||
|
|
|
@ -60,6 +60,14 @@ public interface JdbcCoordinator extends Serializable {
|
|||
*/
|
||||
public Batch getBatch(BatchKey key);
|
||||
|
||||
/**
|
||||
* Execute the currently managed batch (if any)
|
||||
*/
|
||||
public void executeBatch();
|
||||
|
||||
/**
|
||||
* Abort the currently managed batch (if any)
|
||||
*/
|
||||
public void abortBatch();
|
||||
|
||||
/**
|
||||
|
@ -82,16 +90,51 @@ public interface JdbcCoordinator extends Serializable {
|
|||
*/
|
||||
public void flushEnding();
|
||||
|
||||
/**
|
||||
* Close this coordinator and release and resources.
|
||||
*
|
||||
* @return The {@link Connection} associated with the managed {@link #getLogicalConnection() logical connection}
|
||||
*
|
||||
* @see {@link LogicalConnection#close()}
|
||||
*/
|
||||
public Connection close();
|
||||
|
||||
/**
|
||||
* Signals the end of transaction.
|
||||
* <p/>
|
||||
* Intended for use from the transaction coordinator, after local transaction completion. Used to conditionally
|
||||
* release the JDBC connection aggressively if the configured release mode indicates.
|
||||
*/
|
||||
public void afterTransaction();
|
||||
|
||||
/**
|
||||
* Perform the requested work handling exceptions, coordinating and handling return processing.
|
||||
*
|
||||
* @param work The work to be performed.
|
||||
* @param <T> The result type.
|
||||
* @return The work result.
|
||||
*/
|
||||
public <T> T coordinateWork(WorkExecutorVisitable<T> work);
|
||||
|
||||
public void executeBatch();
|
||||
|
||||
/**
|
||||
* Attempt to cancel the last query sent to the JDBC driver.
|
||||
*/
|
||||
public void cancelLastQuery();
|
||||
|
||||
public void setTransactionTimeOut(int timeout);
|
||||
/**
|
||||
* Set the effective transaction timeout period for the current transaction, in seconds.
|
||||
*
|
||||
* @param seconds The number of seconds before a time out should occur.
|
||||
*/
|
||||
public void setTransactionTimeOut(int seconds);
|
||||
|
||||
/**
|
||||
* Calculate the amount of time, in seconds, still remaining before transaction timeout occurs.
|
||||
*
|
||||
* @return The number of seconds remaining until until a transaction timeout occurs. A negative value indicates
|
||||
* no timeout was requested.
|
||||
*
|
||||
* @throws org.hibernate.TransactionException Indicates the time out period has already been exceeded.
|
||||
*/
|
||||
public int determineRemainingTransactionTimeOutPeriod();
|
||||
}
|
||||
|
|
|
@ -80,11 +80,14 @@ public interface LogicalConnection extends Serializable {
|
|||
* Release the underlying connection and clean up any other resources associated
|
||||
* with this logical connection.
|
||||
* <p/>
|
||||
* This leaves the logical connection in a "no longer useable" state.
|
||||
* This leaves the logical connection in a "no longer usable" state.
|
||||
*
|
||||
* @return The physical connection which was being used.
|
||||
* @return The application-supplied connection, or {@code null} if Hibernate was managing connection.
|
||||
*/
|
||||
public Connection close();
|
||||
|
||||
/**
|
||||
* Signals the end of current transaction in which this logical connection operated.
|
||||
*/
|
||||
public void afterTransaction();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.sql.PreparedStatement;
|
|||
import org.hibernate.ScrollMode;
|
||||
|
||||
/**
|
||||
* Contracting for preparing SQL statements
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface StatementPreparer {
|
||||
|
@ -91,7 +93,4 @@ public interface StatementPreparer {
|
|||
* @return the prepared statement
|
||||
*/
|
||||
public PreparedStatement prepareQueryStatement(String sql, boolean isCallable, ScrollMode scrollMode);
|
||||
|
||||
public void setTransactionTimeOut(int timeout);
|
||||
public void unsetTransactionTimeOut();
|
||||
}
|
||||
|
|
|
@ -66,8 +66,8 @@ public class TransactionCoordinatorImpl implements TransactionCoordinator {
|
|||
|
||||
private final transient TransactionContext transactionContext;
|
||||
private final transient JdbcCoordinatorImpl jdbcCoordinator;
|
||||
private final transient TransactionFactory transactionFactory;
|
||||
private final transient TransactionEnvironment transactionEnvironment;
|
||||
private final transient TransactionFactory transactionFactory;
|
||||
private final transient TransactionEnvironment transactionEnvironment;
|
||||
|
||||
private final transient List<TransactionObserver> observers;
|
||||
private final transient SynchronizationRegistryImpl synchronizationRegistry;
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.tm;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.TransactionException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction;
|
||||
|
||||
import org.hibernate.test.jdbc.Person;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-6780")
|
||||
public class TransactionTimeoutTest extends BaseCoreFunctionalTestCase {
|
||||
@Override
|
||||
public String[] getMappings() {
|
||||
return new String[] {"jdbc/Mappings.hbm.xml"};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdbcCoordinatorTransactionTimeoutCheck() {
|
||||
Session session = openSession();
|
||||
Transaction transaction = session.getTransaction();
|
||||
transaction.setTimeout( 2 );
|
||||
assertEquals( -1, ((SessionImplementor)session).getTransactionCoordinator().getJdbcCoordinator().determineRemainingTransactionTimeOutPeriod() );
|
||||
transaction.begin();
|
||||
assertNotSame( -1, ((SessionImplementor)session).getTransactionCoordinator().getJdbcCoordinator().determineRemainingTransactionTimeOutPeriod() );
|
||||
transaction.commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test(expected = TransactionException.class)
|
||||
public void testTransactionTimeoutFailure() throws InterruptedException {
|
||||
Session session = openSession();
|
||||
Transaction transaction = session.getTransaction();
|
||||
transaction.setTimeout( 1 );
|
||||
assertEquals( -1, ((SessionImplementor)session).getTransactionCoordinator().getJdbcCoordinator().determineRemainingTransactionTimeOutPeriod() );
|
||||
transaction.begin();
|
||||
Thread.sleep( 1000 );
|
||||
session.persist( new Person( "Lukasz", "Antoniak" ) );
|
||||
transaction.commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionTimeoutSuccess() {
|
||||
Session session = openSession();
|
||||
Transaction transaction = session.getTransaction();
|
||||
transaction.setTimeout( 2 );
|
||||
transaction.begin();
|
||||
session.persist( new Person( "Lukasz", "Antoniak" ) );
|
||||
transaction.commit();
|
||||
session.close();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue