code.onehippo.org is currently readonly. We are migrating to code.bloomreach.com, please continue working there on Monday 14/12. See: https://docs.bloomreach.com/display/engineering/GitLab

Commit 92561891 authored by Ate Douma's avatar Ate Douma

[Backport 11.2] REPO-1955 Ensure jdbc resources are always closed after use in...

[Backport 11.2] REPO-1955 Ensure jdbc resources are always closed after use in DbLockManager and friends

(cherry picked from commit 4b64f92e)
parent 2832b2b8
/*
* Copyright 2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2017-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -45,12 +45,12 @@ public class DbLockCleanupJanitor implements Runnable {
originalAutoCommit = connection.getAutoCommit();
connection.setAutoCommit(true);
final PreparedStatement removeStatement = connection.prepareStatement(dbLockManager.getRemoveOutdatedStatement());
long dayAgoTime = System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS);
removeStatement.setLong(1, dayAgoTime);
int updated = removeStatement.executeUpdate();
log.info("Removed {} outdated locks", updated);
removeStatement.close();
try (final PreparedStatement removeStatement = connection.prepareStatement(dbLockManager.getRemoveOutdatedStatement())) {
long dayAgoTime = System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS);
removeStatement.setLong(1, dayAgoTime);
int updated = removeStatement.executeUpdate();
log.info("Removed {} outdated locks", updated);
}
} catch (SQLException e) {
log.error("Error while trying remove outdated locks", e);
} finally {
......
/*
* Copyright 2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2017-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -45,15 +45,15 @@ public class DbLockRefresher implements Runnable {
connection = dbLockManager.getConnection();
originalAutoCommit = connection.getAutoCommit();
connection.setAutoCommit(true);
final PreparedStatement refreshStatement = connection.prepareStatement(dbLockManager.getRefreshLockStatement());
long currentTime = System.currentTimeMillis();
refreshStatement.setLong(1, currentTime);
refreshStatement.setString(2, dbLockManager.getClusterNodeId());
// select all rows that have less than 20 seconds to live
refreshStatement.setLong(3, currentTime + 20000);
int updated = refreshStatement.executeUpdate();
log.info("Refreshed {} locks", updated);
refreshStatement.close();
try (final PreparedStatement refreshStatement = connection.prepareStatement(dbLockManager.getRefreshLockStatement())) {
long currentTime = System.currentTimeMillis();
refreshStatement.setLong(1, currentTime);
refreshStatement.setString(2, dbLockManager.getClusterNodeId());
// select all rows that have less than 20 seconds to live
refreshStatement.setLong(3, currentTime + 20000);
int updated = refreshStatement.executeUpdate();
log.info("Refreshed {} locks", updated);
}
} catch (SQLException e) {
log.error("Error while trying to refresh locks", e);
} finally {
......
/*
* Copyright 2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2017-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -43,13 +43,13 @@ public class DbResetExpiredLocksJanitor implements Runnable {
connection = dbLockManager.getConnection();
originalAutoCommit = connection.getAutoCommit();
connection.setAutoCommit(true);
final PreparedStatement resetStatement = connection.prepareStatement(dbLockManager.getResetExpiredStatement());
long currentTime = System.currentTimeMillis();
resetStatement.setLong(1, currentTime);
resetStatement.setLong(2, currentTime);
int updated = resetStatement.executeUpdate();
log.info("Expired {} locks", updated);
resetStatement.close();
try (final PreparedStatement resetStatement = connection.prepareStatement(dbLockManager.getResetExpiredStatement())) {
long currentTime = System.currentTimeMillis();
resetStatement.setLong(1, currentTime);
resetStatement.setLong(2, currentTime);
int updated = resetStatement.executeUpdate();
log.info("Expired {} locks", updated);
}
} catch (SQLException e) {
log.error("Error while trying to reset locks", e);
} finally {
......
/*
* Copyright 2017 Hippo B.V. (http://www.onehippo.com)
* Copyright 2017-2018 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -45,56 +45,57 @@ public class LockThreadInterrupter implements Runnable {
connection = dbLockManager.getConnection();
originalAutoCommit = connection.getAutoCommit();
connection.setAutoCommit(true);
final PreparedStatement selectAbortStatement = connection.prepareStatement(dbLockManager.getSelectAbortStatement());
selectAbortStatement.setString(1, dbLockManager.getClusterNodeId());
ResultSet resultSet = selectAbortStatement.executeQuery();
while (resultSet.next()) {
// interrupt the thread for this lock (if still present). Otherwise ignore.
final String lockKey = resultSet.getString("lockKey");
final String lockThread = resultSet.getString("lockThread");
if (lockThread == null) {
log.error("Illegal database row state: cannot abort db entry '{}' for which lockThread is null.", lockKey);
continue;
}
boolean lockThreadForAbortFound = false;
for (MutableLock lock : dbLockManager.getLocalLocks().values()) {
Thread thread = lock.getThread().get();
if (thread == null || !thread.isAlive()) {
// ignore since will be picked up by org.onehippo.services.lock.AbstractLockManager.UnlockStoppedThreadJanitor
} else if (lockKey.equals(lock.getLockKey())){
if (!lockThread.equals(thread.getName())) {
log.error("Lock thread in JVM is other one than in database for lock '{}' which is an illegal state.",
lockKey);
try (final PreparedStatement selectAbortStatement = connection.prepareStatement(dbLockManager.getSelectAbortStatement())) {
selectAbortStatement.setString(1, dbLockManager.getClusterNodeId());
try (final ResultSet resultSet = selectAbortStatement.executeQuery()) {
while (resultSet.next()) {
// interrupt the thread for this lock (if still present). Otherwise ignore.
final String lockKey = resultSet.getString("lockKey");
final String lockThread = resultSet.getString("lockThread");
if (lockThread == null) {
log.error("Illegal database row state: cannot abort db entry '{}' for which lockThread is null.", lockKey);
continue;
}
// best effort : thread interrupt : As a result, the Thread containing the lock should invoke
// #unlock at some point in time
// There are no guarantees beyond best-effort attempts to stop
// processing actively executing Thread holding a lock. For example, typical
// implementations will cancel via {@link Thread#interrupt}, so any
// task that fails to respond to interrupts may never terminate.
try {
lockThreadForAbortFound = true;
log.info("Found Thread '{}' to be interrupted for Lock '{}'", thread.getName(), lockKey);
if (thread.isInterrupted()) {
log.info("Thread '{}' has already been interrupted. Not interrupting again.", thread.getName());
} else {
log.info("Interrupting thread '{}'", thread.getName());
thread.interrupt();
boolean lockThreadForAbortFound = false;
for (MutableLock lock : dbLockManager.getLocalLocks().values()) {
Thread thread = lock.getThread().get();
if (thread == null || !thread.isAlive()) {
// ignore since will be picked up by org.onehippo.services.lock.AbstractLockManager.UnlockStoppedThreadJanitor
} else if (lockKey.equals(lock.getLockKey())){
if (!lockThread.equals(thread.getName())) {
log.error("Lock thread in JVM is other one than in database for lock '{}' which is an illegal state.",
lockKey);
continue;
}
// best effort : thread interrupt : As a result, the Thread containing the lock should invoke
// #unlock at some point in time
// There are no guarantees beyond best-effort attempts to stop
// processing actively executing Thread holding a lock. For example, typical
// implementations will cancel via {@link Thread#interrupt}, so any
// task that fails to respond to interrupts may never terminate.
try {
lockThreadForAbortFound = true;
log.info("Found Thread '{}' to be interrupted for Lock '{}'", thread.getName(), lockKey);
if (thread.isInterrupted()) {
log.info("Thread '{}' has already been interrupted. Not interrupting again.", thread.getName());
} else {
log.info("Interrupting thread '{}'", thread.getName());
thread.interrupt();
}
} catch (SecurityException e) {
String msg = String.format("Thread '%s' is not allowed to be interrupted. Can't abort '%s'",
thread.getName(), lock.getLockKey());
log.error(msg);
}
}
} catch (SecurityException e) {
String msg = String.format("Thread '%s' is not allowed to be interrupted. Can't abort '%s'",
thread.getName(), lock.getLockKey());
log.error(msg);
}
if (!lockThreadForAbortFound) {
log.warn("There has not been found an alive Thread for key '{}' which has status 'ABORT'. After the " +
"lock has been expired it will be removed by DbLockResetJanitor", lockKey);
}
}
}
if (!lockThreadForAbortFound) {
log.warn("There has not been found an alive Thread for key '{}' which has status 'ABORT'. After the " +
"lock has been expired it will be removed by DbLockResetJanitor", lockKey);
}
}
selectAbortStatement.close();
} catch (Exception e) {
log.error("Exception in {} happened:", this.getClass().getName(), e);
} finally {
......
......@@ -16,7 +16,6 @@
package org.onehippo.repository.lock.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
......@@ -47,10 +46,9 @@ public class MySqlDbLockManager extends DbLockManager {
if (schemaCheckEnabled && !connectionHelper.tableExists(tableName)) {
createTable(dataSource, connectionHelper, createTableStatement, tableName, uniqueIndexes);
} else {
try (Connection connection = dataSource.getConnection()) {
final Statement indicesStatement = connection.createStatement();
final ResultSet resultSet = indicesStatement.executeQuery(String.format("SHOW INDEX FROM %s", tableName));
try (final Connection connection = dataSource.getConnection();
final Statement indicesStatement = connection.createStatement();
final ResultSet resultSet = indicesStatement.executeQuery(String.format("SHOW INDEX FROM %s", tableName))) {
boolean lockKeyIndexExists = false;
while (resultSet.next()) {
final String columnName = resultSet.getString("Column_name");
......@@ -60,6 +58,7 @@ public class MySqlDbLockManager extends DbLockManager {
break;
}
}
// explicit early close to release resources
resultSet.close();
if (!lockKeyIndexExists) {
// Incorrectly the lockKey does not have an index! This is a bug typically manifesting itself with
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment