В загрузочном проекте Spring с данными JPA с Hibernate в базе данных PostgreSQL несколько задач выполняются одновременно. Есть пул TaskExecutor и пул соединений с базой данных.
Иногда для выполнения этих задач требуются одни и те же объекты (обновление: объект означает объекты, хранящиеся в базе данных). В попытке убедиться, что задачи не конфликтуют (обновление: «не пытайтесь одновременно получать доступ к одним и тем же записям»), была создана служба блокировки. Задача получает блокировку нужных ей объектов и снимает блокировку только после ее завершения, после чего следующая задача может получить блокировку и начать свою работу.
На практике это не так. т работает. Один конкретный случай удаления записи в задаче A и ее видимости во время части задачи B продолжает появляться. Фактическим исключением является ограничение внешнего ключа, которое не выполняется: задача B сначала выбирает (удаленный) объект как объект, для которого должна быть создана связь (поэтому задача B по-прежнему видит удаленный объект в этой точке!), Но затем при создании отношения в задаче B не удается, потому что удаленный объект больше не присутствует.
После консультации с коллегой возникла идея, что очистка хранилища - это не то же самое, что фиксация. Следовательно, задача A разблокируется, когда ее лог c выполнен и изменения сброшены, но измененные данные еще не были зафиксированы в базе данных. В то же время задача B получает блокировку и начинает читать данные, и лишь немного позже происходит фиксация для задачи A.
Чтобы убедиться, что блокировка задачи A снята только после изменения в его базе данных были зафиксированы, я попробовал этот код:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
logDebugMessage("after completion method called");
// Release the locks
if(lockObtained) {
logDebugMessage("releasing locks");
lockService.releaseLocks(taskHolder.getId());
logDebugMessage("done releasing locks");
}
else
logDebugMessage("no locks to release");
}
});
Это само по себе не заставило проблему исчезнуть. Следующее волнение было в том, что следующая задача, задача B, уже имеет открытую транзакцию, пока она ожидает блокировки. Когда он получает блокировку, он читает, используя эту уже открытую транзакцию, а затем ? По какой-то причине читает данные перед фиксацией. Я признаю, что это не имеет особого смысла, но некоторое отчаяние начинает наступать. В любом случае, с этой дополнительной идеей, каждая задача теперь выполняется так:
@Override
public void run() {
try{
// Start the progress
taskStartedDateTime = LocalDateTime.now();
logDebugMessage("started");
// Let the task determine which objects need to be locked
List<LockableObjectId> objectsToLock = getObjectsToLock();
// Try to obtain a lock
lockObtained = locksObtained(objectsToLock);
if(lockObtained) {
// Do the actual task in a new transaction
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int status) {
logDebugMessage("after completion method called");
// Release the locks
if(lockObtained) {
logDebugMessage("releasing locks");
lockService.releaseLocks(taskHolder.getId());
logDebugMessage("done releasing locks");
}
else
logDebugMessage("no locks to release");
}
});
try{
// Run the actual task
runTask();
Но это все еще не решает проблема. Возможно ли, что фиксация в базе данных была сделана со стороны Java, и задача B считывает базу данных до того, как фиксация выполняется в самой базе данных? Метод afterCompletion вызывается после того, как Java отправил коммит, но до того, как база данных фактически выполнила его? Если да, есть ли способ получить подтверждение базы данных о том, что фиксация действительно была выполнена?
Или мы здесь совершенно не на том пути?