#java #spring #spring-data-jpa #synchronization #locking
Вопрос:
Контекст
У нас есть приложение Spring boot (API, используемый угловым интерфейсом).
Он работает в контейнере docker. Он использует один экземпляр базы данных PostgreSQL.
У нашего приложения были некоторые проблемы с загрузкой, поэтому мы попросили нас масштабировать его. Мы сказали нам запустить наш API на нескольких контейнерах docker для этого.
У нас есть несколько вопросов / проблем, связанных с синхронизацией кода в нескольких экземплярах docker, выполняющих наш код.
Проблема 1
У нас есть несколько @Scheduled
заданий, интегрированных и развернутых с помощью нашего кода API.
Мы не хотим, чтобы эти запланированные задания выполнялись всеми экземплярами контейнеров, а только одним.
Я думаю, что мы можем просто справиться с этим, отключив задания в других контейнерах с помощью переменных среды со "-"
значением для отключения запланированного cron весной.
Звучит ли это правильно?
Проблема 2
Другая проблема заключается в том, что мы используем @Lock
аннотации Spring для некоторых методов репозитория.
public interface IncrementRepository extends JpaRepository<IncrementEntity, UUID> {
@Lock(LockModeType.PESSIMISTIC_FORCE_INCREMENT)
Optional<IncrementEntity> findByAnnee(String pAnneeAA);
@Lock(LockModeType.PESSIMISTIC_WRITE)
IncrementEntity save(IncrementEntity pIncrementEntity);
}
Это очень важно для нас, чтобы зафиксировать это, поскольку мы получаем / вычисляем приращение, используемое в качестве уникального идентификатора некоторых наших данных.
Если я правильно понял этот механизм блокировки :
- if a process execute this code, the Spring JPA
@Transaction
will acquire a lock on theIncrementEntity
(lock the database table). - when another process tries do do the same thing before the first lock has been released by the first transaction, it should have a
PessimisticLockException
and the second transaction will rollback - this is managed by Spring at application level, NOT directly at database level (right??)
So what will happen if we’re running our code on several containers ?
- app running in container 1 sets a lock
- app running in container 2 execute the same code and tries to set the same lock while the first one has not been released yet
- each Spring application running in different containers will probably acquire the lock without problems as they don’t share the same information?
Please tell me if I correctly understood how it works, and if we will effectively have a problem running such code on several docker containers.
I guess that solution would be to set a lock directly on the database table, as we have only one instance of it?
Is there a way to easily set / release the lock at database level using Spring JPA code ?
Или, может быть, я неправильно понял и установка блокировки с помощью @Lock
аннотации Spring устанавливает реальную блокировку базы данных ?
В этом случае, возможно, у нас вообще нет никаких проблем, так как блокировка правильно установлена в самой базе данных, разделяемой всеми экземплярами контейнеров??
Проблема 3
Чтобы избежать слишком большого количества исключений и отклонить некоторые запросы, пытающиеся получить блокировку одновременно, мы также добавили синхронизированный блок вокруг приведенного выше кода.
String numIncrement;
synchronized (this.mutex) {
try {
numIncrement = this.incrementService.getIncrement(var);
} catch (Exception e) {
// rethrow custom technical exception
}
}
Таким образом, одновременные запросы должны быть отложены и поставлены в очередь, что лучше для наших пользователей.
Я предполагаю, что здесь у нас также возникнут проблемы, поскольку экземпляры docker не используют одну и ту же JVM, поэтому синхронизация может работать только в области самого контейнера… верно?
Вывод
Для решения всех этих проблем, пожалуйста, скажите мне, есть ли у вас какие-либо решения для обхода / адаптации нашего кода, чтобы он был совместим с масштабированием приложений.
Ответ №1:
После ряда тестов я могу подтвердить эти моменты в отношении моего первоначального вопроса
Проблема 1
Мы можем отключить пружинный CRON со -
значением
@Scheduled(cron = "-")
Проблема 2
Аннотация JPa Spring @Lock
устанавливает блокировку самой базы данных. Он не управляется программным обеспечением Spring.
Поэтому при дублировании контейнеров, если приложение Spring в первом контейнере устанавливает блокировку, база данных блокируется, и когда второе приложение в другом контейнере пытается получить данные, у него есть PessimisticLockException
.
Проблема 3
Синхронизированный код с использованием synchronized
ключевого слова JAVA, очевидно, управляется JVM, поэтому взаимное исключение кода между контейнерами отсутствует.