HHH-10654 - Fix mariadb and pgsql failing tests

This commit is contained in:
Andrea Boriero 2017-04-03 20:27:03 +01:00 committed by Vlad Mihalcea
parent 19c03e0c5a
commit 5fd186a010
6 changed files with 186 additions and 44 deletions

View File

@ -23,6 +23,7 @@ import javax.persistence.PessimisticLockException;
import javax.persistence.Query;
import javax.persistence.QueryTimeoutException;
import org.hibernate.LockOptions;
import org.hibernate.Session;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.HSQLDialect;
@ -40,6 +41,8 @@ import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.transaction.TransactionUtil;
import org.hibernate.testing.util.ExceptionUtil;
import org.junit.Test;
import org.jboss.logging.Logger;
@ -156,19 +159,23 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
try {
Map<String, Object> properties = new HashMap<>();
properties.put( org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT, -2L );
properties.put( org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT, LockOptions.SKIP_LOCKED );
em2.find( Lock.class, lock.getId(), LockModeType.PESSIMISTIC_READ, properties );
try {
doInJPA( this::entityManagerFactory, entityManager -> {
TransactionUtil.setJdbcTimeout( entityManager.unwrap( Session.class ) );
entityManager.createNativeQuery( updateStatement() )
.setParameter( "name", "changed" )
.setParameter( "id", lock.getId() )
.executeUpdate();
} );
fail("Should throw LockTimeoutException");
fail("Should throw Exception");
}
catch (LockTimeoutException expected) {
catch (Exception e) {
if ( !ExceptionUtil.isSqlLockTimeout( e) ) {
fail( "Unknown exception thrown: " + e.getMessage() );
}
}
}
finally {
@ -202,13 +209,16 @@ public class LockTest extends BaseEntityManagerFunctionalTestCase {
try {
doInJPA( this::entityManagerFactory, entityManager -> {
try {
TransactionUtil.setJdbcTimeout( entityManager.unwrap( Session.class ) );
entityManager.createNativeQuery( updateStatement() )
.setParameter( "name", "changed" )
.setParameter( "id", lock.getId() )
.executeUpdate();
}
catch (LockTimeoutException | PessimisticLockException expected) {
failureExpected.set( true );
catch (Exception e) {
if ( ExceptionUtil.isSqlLockTimeout( e ) ) {
failureExpected.set( true );
}
}
} );
}

View File

@ -9,6 +9,8 @@ package org.hibernate.query.criteria.internal.expression;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@ -16,6 +18,11 @@ import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Assert;
import org.junit.Test;
@ -29,7 +36,8 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
public class SearchedCaseExpressionTest extends BaseCoreFunctionalTestCase {
@Test
public void testCaseClause() {
@RequiresDialect(H2Dialect.class)
public void testCaseClause() {
doInHibernate( this::sessionFactory, session -> {
CriteriaBuilder cb = session.getCriteriaBuilder();
@ -87,6 +95,7 @@ public class SearchedCaseExpressionTest extends BaseCoreFunctionalTestCase {
private Long id;
@Column
@Enumerated(EnumType.STRING)
private EventType type;
protected Event() {

View File

@ -7,19 +7,17 @@
package org.hibernate.test.locking;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.PessimisticLockException;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.SybaseASE15Dialect;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.transaction.TransactionUtil;
import org.hibernate.testing.util.ExceptionUtil;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
@ -164,43 +162,38 @@ public class LockModeTest extends BaseCoreFunctionalTestCase {
// until the txn in the calling method completed.
// To be able to cater to the second type, we run this block in a separate thread to be able to "time it out"
executeSync( () -> {
doInHibernate( this::sessionFactory, _session -> {
_session.doWork( connection -> {
try {
executeSync( () -> {
doInHibernate( this::sessionFactory, _session -> {
TransactionUtil.setJdbcTimeout( _session );
try {
connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), 1000);
} catch (Throwable ignore) {
ignore.fillInStackTrace();
// load with write lock to deal with databases that block (wait indefinitely) direct attempts
// to write a locked row
A it = _session.get(
A.class,
id,
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT )
);
_session.createNativeQuery( updateStatement() )
.setParameter( "value", "changed" )
.setParameter( "id", it.getId() )
.executeUpdate();
fail( "Pessimistic lock not obtained/held" );
}
catch ( Exception e ) {
if ( !ExceptionUtil.isSqlLockTimeout( e) ) {
fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() );
}
}
} );
try {
// load with write lock to deal with databases that block (wait indefinitely) direct attempts
// to write a locked row
A it = _session.get(
A.class,
id,
new LockOptions( LockMode.PESSIMISTIC_WRITE ).setTimeOut( LockOptions.NO_WAIT )
);
_session.createNativeQuery( updateStatement() )
.setParameter( "value", "changed" )
.setParameter( "id", it.getId() )
.executeUpdate();
fail( "Pessimistic lock not obtained/held" );
}
catch ( Exception e ) {
// grr, exception can be any number of types based on database
// see HHH-6887
if ( LockAcquisitionException.class.isInstance( e )
|| GenericJDBCException.class.isInstance( e )
|| PessimisticLockException.class.isInstance( e ) ) {
// "ok"
}
else {
fail( "Unexpected error type testing pessimistic locking : " + e.getClass().getName() );
}
}
} );
} );
}
catch (Exception e) {
//MariaDB throws a time out nd closes the underlying connection
if( !ExceptionUtil.isConnectionClose(e)) {
fail("Unknown exception thrown: " + e.getMessage());
}
}
}
protected String updateStatement() {

View File

@ -91,7 +91,7 @@ public abstract class BaseUnitTestCase {
Thread.currentThread().interrupt();
}
catch (ExecutionException e) {
throw new RuntimeException( e );
throw new RuntimeException( e.getCause() );
}
}
}

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.testing.transaction;
import java.sql.Statement;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -13,16 +15,25 @@ import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.jboss.logging.Logger;
/**
* @author Vlad Mihalcea
*/
public class TransactionUtil {
private static final Logger log = Logger.getLogger( TransactionUtil.class );
/**
* Hibernate transaction function
*
@ -322,4 +333,38 @@ public class TransactionUtil {
}
}
}
/**
* Set Session or Statement timeout
* @param session Hibernate Session
*/
public static void setJdbcTimeout(Session session) {
session.doWork( connection -> {
if ( Dialect.getDialect() instanceof PostgreSQL81Dialect ) {
try (Statement st = connection.createStatement()) {
st.execute( "SET statement_timeout TO 1000" );
}
}
else if( Dialect.getDialect() instanceof MySQLDialect ) {
try (Statement st = connection.createStatement()) {
st.execute( "SET GLOBAL innodb_lock_wait_timeout = 1" );
}
}
else if( Dialect.getDialect() instanceof H2Dialect ) {
try (Statement st = connection.createStatement()) {
st.execute( "SET LOCK_TIMEOUT 100" );
}
}
else {
try {
connection.setNetworkTimeout( Executors.newSingleThreadExecutor(), 1000 );
}
catch (Throwable ignore) {
ignore.fillInStackTrace();
}
}
} );
}
}

View File

@ -0,0 +1,85 @@
package org.hibernate.testing.util;
import javax.persistence.LockTimeoutException;
import org.hibernate.PessimisticLockException;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
/**
* @author Vlad Mihalcea
*/
public class ExceptionUtil {
private static final ExceptionUtil INSTANCE = new ExceptionUtil();
public static ExceptionUtil getInstance() {
return INSTANCE;
}
private ExceptionUtil() {
}
/**
* Get the root cause of a particular {@code Throwable}
*
* @param t exception
*
* @return exception root cause
*/
public static Throwable rootCause(Throwable t) {
Throwable cause = t.getCause();
if ( cause != null && cause != t ) {
return rootCause( cause );
}
return t;
}
/**
* Was the given exception caused by a SQL lock timeout?
*
* @param e exception
*
* @return is caused by a SQL lock timeout
*/
public static boolean isSqlLockTimeout(Exception e) {
// grr, exception can be any number of types based on database
// see HHH-6887
if ( LockAcquisitionException.class.isInstance( e )
|| LockTimeoutException.class.isInstance( e )
|| GenericJDBCException.class.isInstance( e )
|| PessimisticLockException.class.isInstance( e )
|| javax.persistence.PessimisticLockException.class.isInstance( e )
|| JDBCConnectionException.class.isInstance( e ) ) {
return true;
}
else {
Throwable rootCause = ExceptionUtil.rootCause( e );
if ( rootCause != null && (
rootCause.getMessage().contains( "timeout" ) ||
rootCause.getMessage().contains( "timed out" ) )
) {
return true;
}
}
return false;
}
/**
* Was the given exception caused by a SQL connection close
*
* @param e exception
*
* @return is caused by a SQL connection close
*/
public static boolean isConnectionClose(Exception e) {
Throwable rootCause = ExceptionUtil.rootCause( e );
if ( rootCause != null && (
rootCause.getMessage().toLowerCase().contains( "connection is close" )
) ) {
return true;
}
return false;
}
}