Skip to content

Commit

Permalink
refactor cached prepared statement to become ThreadLocal (#5635) (#5874)
Browse files Browse the repository at this point in the history
Co-authored-by: Aleksey Daryin <aleksey.daryin@raiffeisen.ru>
Co-authored-by: rberezen <ruslan.berezenskyi@gmail.com>
  • Loading branch information
3 people committed May 9, 2024
1 parent 55188e8 commit 77f1384
Showing 1 changed file with 19 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ public abstract class ExecutablePreparedStatementBase implements ExecutablePrepa
//Ideally the creation of the prepared statements would happen at the spot where we know we should be re-using it and that code can close it.
// But that will have to wait for a refactoring of this code.
// So for now we're trading leaving at most one prepared statement unclosed at the end of the liquibase run for better re-using statements to avoid overhead
private static PreparedStatement lastPreparedStatement;
private static String lastPreparedStatementSql;
private static final ThreadLocal<PreparedStatement> LAST_PREPARED_STATEMENT = new ThreadLocal<>();
private static final ThreadLocal<String> LAST_PREPARED_STATEMENT_SQL = new ThreadLocal<>();

//Cache the executeWithFlags method to avoid reflection overhead
private static Method executeWithFlagsMethod;
private static final ThreadLocal<Method> EXECUTE_WITH_FLAGS_METHOD = new ThreadLocal<>();

private final Map<String, Object> snapshotScratchPad = new HashMap<>();

Expand Down Expand Up @@ -103,8 +103,8 @@ public void execute(PreparedStatementFactory factory) throws DatabaseException {
// create prepared statement
stmt = factory.create(sql);

lastPreparedStatement = stmt;
lastPreparedStatementSql = sql;
LAST_PREPARED_STATEMENT.set(stmt);
LAST_PREPARED_STATEMENT_SQL.set(sql);
} else {
try {
stmt.clearParameters();
Expand Down Expand Up @@ -142,18 +142,18 @@ protected String applyVisitors(String sql, List<SqlVisitor> sqlVisitors) {
}

protected PreparedStatement getCachedStatement(String sql) {
if (lastPreparedStatement == null || lastPreparedStatementSql == null) {
if (LAST_PREPARED_STATEMENT.get() == null || LAST_PREPARED_STATEMENT_SQL.get() == null) {
return null;
}

boolean statementIsValid = true;
if (lastPreparedStatementSql.equals(sql)) {
if (LAST_PREPARED_STATEMENT_SQL.get().equals(sql)) {
try {
if (lastPreparedStatement.isClosed()) {
if (LAST_PREPARED_STATEMENT.get().isClosed()) {
statementIsValid = false;
}
if (statementIsValid) {
final Connection connection = lastPreparedStatement.getConnection();
final Connection connection = LAST_PREPARED_STATEMENT.get().getConnection();
if (connection == null || connection.isClosed()) {
statementIsValid = false;
}
Expand All @@ -167,24 +167,26 @@ protected PreparedStatement getCachedStatement(String sql) {
}

if (!statementIsValid) {
JdbcUtil.closeStatement(lastPreparedStatement);
lastPreparedStatement = null;
lastPreparedStatementSql = null;
JdbcUtil.closeStatement(LAST_PREPARED_STATEMENT.get());
LAST_PREPARED_STATEMENT.remove();
LAST_PREPARED_STATEMENT_SQL.remove();
EXECUTE_WITH_FLAGS_METHOD.remove();
}

return lastPreparedStatement;
return LAST_PREPARED_STATEMENT.get();
}

protected void executePreparedStatement(PreparedStatement stmt) throws SQLException {
if (database instanceof PostgresDatabase) {
//postgresql's default prepared statement setup is slow for normal liquibase usage. Calling with QUERY_ONESHOT seems faster, even when we keep re-calling the same prepared statement for many rows in loadData
try {
if (executeWithFlagsMethod == null) {
executeWithFlagsMethod = stmt.getClass().getMethod("executeWithFlags", int.class);
executeWithFlagsMethod.setAccessible(true);
if (EXECUTE_WITH_FLAGS_METHOD.get() == null) {
Method executeWithFlags = stmt.getClass().getMethod("executeWithFlags", int.class);
executeWithFlags.setAccessible(true);
EXECUTE_WITH_FLAGS_METHOD.set(executeWithFlags);
}

executeWithFlagsMethod.invoke(stmt, 1); //QueryExecutor.QUERY_ONESHOT
EXECUTE_WITH_FLAGS_METHOD.get().invoke(stmt, 1); //QueryExecutor.QUERY_ONESHOT
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
stmt.execute();
}
Expand Down

0 comments on commit 77f1384

Please sign in to comment.