HHH-8946 Optimize JdbcCoordinatorImpl for CPU performance

- unnecessary HashMap lookups
- size HashMap allocations appropriately
- a leak of tracked statements
- complex logging logic

Conflicts:
	hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java
This commit is contained in:
Sanne Grinovero 2014-02-11 15:18:41 -05:00 committed by Brett Meyer
parent d402c06170
commit 265963c33b
1 changed files with 32 additions and 20 deletions

View File

@ -30,6 +30,7 @@ import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -57,7 +58,6 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jdbc.WorkExecutor; import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.jdbc.WorkExecutorVisitable; import org.hibernate.jdbc.WorkExecutorVisitable;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level;
/** /**
* Standard Hibernate implementation of {@link JdbcCoordinator} * Standard Hibernate implementation of {@link JdbcCoordinator}
@ -66,6 +66,7 @@ import org.jboss.logging.Logger.Level;
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Brett Meyer * @author Brett Meyer
* @author Sanne Grinovero
*/ */
public class JdbcCoordinatorImpl implements JdbcCoordinator { public class JdbcCoordinatorImpl implements JdbcCoordinator {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( private static final CoreMessageLogger LOG = Logger.getMessageLogger(
@ -79,6 +80,14 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
private transient long transactionTimeOutInstant = -1; private transient long transactionTimeOutInstant = -1;
/**
* This is a marker value to insert instead of null values for when a Statement gets registered in xref
* but has no associations registered. This is useful to be able to efficiently check against duplicate
* registraction but you'll have to check against instance equality rather than null before attempting
* to add elements to this set.
*/
private static final Set<ResultSet> EMPTY_RESULTSET = Collections.emptySet();
private final HashMap<Statement,Set<ResultSet>> xref = new HashMap<Statement,Set<ResultSet>>(); private final HashMap<Statement,Set<ResultSet>> xref = new HashMap<Statement,Set<ResultSet>>();
private final Set<ResultSet> unassociatedResultSets = new HashSet<ResultSet>(); private final Set<ResultSet> unassociatedResultSets = new HashSet<ResultSet>();
private final SqlExceptionHelper exceptionHelper; private final SqlExceptionHelper exceptionHelper;
@ -317,10 +326,14 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
@Override @Override
public void register(Statement statement) { public void register(Statement statement) {
LOG.tracev( "Registering statement [{0}]", statement ); LOG.tracev( "Registering statement [{0}]", statement );
if ( xref.containsKey( statement ) ) { final Set<ResultSet> previousValue = xref.put( statement, EMPTY_RESULTSET );
if ( previousValue != null ) {
//we could check as a pre-condition but this is a very hot spot in terms of computation,
//so we optimize for the common scenario. At this point however we need to put the previous
//value back to undo the put:
xref.put( statement, previousValue );
throw new HibernateException( "statement already registered with JDBCContainer" ); throw new HibernateException( "statement already registered with JDBCContainer" );
} }
xref.put( statement, null );
} }
@Override @Override
@ -371,7 +384,6 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
@Override @Override
public void register(ResultSet resultSet, Statement statement) { public void register(ResultSet resultSet, Statement statement) {
LOG.tracev( "Registering result set [{0}]", resultSet );
if ( statement == null ) { if ( statement == null ) {
try { try {
statement = resultSet.getStatement(); statement = resultSet.getStatement();
@ -381,18 +393,20 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
} }
} }
if ( statement != null ) { if ( statement != null ) {
LOG.tracev( "Registering result set [{0}]", resultSet );
final Set<ResultSet> resultSets = xref.get( statement );
if ( resultSets == null ) {
// Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a // Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a
// proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210. // proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210.
if ( LOG.isEnabled( Level.DEBUG ) && !xref.containsKey( statement ) ) {
LOG.unregisteredStatement(); LOG.unregisteredStatement();
} }
Set<ResultSet> resultSets = xref.get( statement ); if ( resultSets == null || resultSets == EMPTY_RESULTSET ) {
if ( resultSets == null ) { xref.put( statement, new HashSet<ResultSet>() );
resultSets = new HashSet<ResultSet>();
xref.put( statement, resultSets );
} }
else {
resultSets.add( resultSet ); resultSets.add( resultSet );
} }
}
else { else {
unassociatedResultSets.add( resultSet ); unassociatedResultSets.add( resultSet );
} }
@ -410,13 +424,13 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
} }
} }
if ( statement != null ) { if ( statement != null ) {
final Set<ResultSet> resultSets = xref.get( statement );
if ( resultSets == null ) {
// Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a // Keep this at DEBUG level, rather than warn. Numerous connection pool implementations can return a
// proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210. // proxy/wrapper around the JDBC Statement, causing excessive logging here. See HHH-8210.
if ( LOG.isEnabled( Level.DEBUG ) && !xref.containsKey( statement ) ) {
LOG.unregisteredStatement(); LOG.unregisteredStatement();
} }
Set<ResultSet> resultSets = xref.get( statement ); else {
if ( resultSets != null ) {
resultSets.remove( resultSet ); resultSets.remove( resultSet );
if ( resultSets.isEmpty() ) { if ( resultSets.isEmpty() ) {
xref.remove( statement ); xref.remove( statement );
@ -455,9 +469,7 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
private void cleanup() { private void cleanup() {
for ( Map.Entry<Statement,Set<ResultSet>> entry : xref.entrySet() ) { for ( Map.Entry<Statement,Set<ResultSet>> entry : xref.entrySet() ) {
if ( entry.getValue() != null ) {
closeAll( entry.getValue() ); closeAll( entry.getValue() );
}
close( entry.getKey() ); close( entry.getKey() );
} }
xref.clear(); xref.clear();