mystdeim
@mystdeim

Как работает блокировка у Singleton на кластере?

Есть вот такая штука:

@Singleton
public class RecoveryWorker {
    @Schedule(minute = "*/1", hour = "*", persistent = false)
    public void run() {
        // тут происходят тяжелые запросы к базе  ~ 2c
        ...
    }
    ...
}


Запущено несолько инстансов wildfly, настроенных на одну базу oracle, стал ловить такую ошибку:

ERROR [org.jboss.as.ejb3] (EJB default - 10) JBAS014120: Error invoking timeout for timer: [id=66ed65c4-3a2d-4343-870d-5f7a46a7742c timedObjectId=com.package.Worker auto-timer?:true persistent?:false timerService=org.jboss.as.ejb3.timerservice.TimerServiceImpl@222950e6 initialExpiration=null intervalDuration(in milli sec)=0 nextExpiration=Wed Jun 15 00:01:00 CEST 2016 timerState=IN_TIMEOUT info=null: javax.ejb.ConcurrentAccessTimeoutException: JBAS014373: EJB 3.1 PFD2 4.8.5.5.1 concurrent access timeout on org.jboss.invocation.InterceptorContext$Invocation@ead4bd3 - could not obtain lock within 5000MILLISECONDS

Есть какой-то встроенный механизм на уровне базы, что второй инстанс не может зайти в критическую секцию? Как решается проблема, когда нужно иметь Singleton на нескольких серверах?
  • Вопрос задан
  • 1293 просмотра
Решения вопроса 1
@bobzer
Java EE Developer
Как решается проблема, когда нужно иметь Singleton на нескольких серверах?

  1. Блокировка записи в БД. Если бизнес логика вносит какие-то изменения в БД, то перед этим осуществляйте чтение записи с блокировкой, SELECT ... FOR UPDATE (для JPA LockMode/LockModeType и прочее, в зависимости от реализации и версии). При этом, получаем актуальное состояние записи одновременно запрещая её изменять другим потокам (и другим узлам кластера) до тех пор, пока не завершится транзакция обработки записи. Сразу после чтения с блокировкой проверяем, подлежит ли она обработке или нет, на случай, если пока получали блокировку другой поток уже обработал запись. Обычно, сама запись содержит поле, сообщающее о том, следует ли её обрабатывать, например - некий статус. Если статус говорит о том, что запись уже обработана, то обработку не выполняем, завершаем транзакцию, переходим к следующей записи. Если не обработана - соответственно обрабатываем. Пока идёт обработка текущим потоком, другие потоки (узлы) останавливаются на моменте чтения с блокировкой, это поведение обеспечивает СУБД. После завершения обработки, смены статуса записи и подтверждения транзакции, запись высвобождается, и если другой поток пытался её обработать, то СУБД разрешает ему считать запись с блокировкой. Поток проверяет статус, "видит" что она уже обработана и не обрабатывает её. Плюсы: универсальность, независимость от контейнера, практически "непробиваемый". Минус: вероятно возникновение излишней нагрузки, в самом неприятном варианте - всю работу делает один узел, остальные потребляя те же мощности не делают ничего полезного. Если записей менее 100 тысяч в сутки, а узлов менее 5-10, то скорее всего, это не будет большой проблемой.
  2. Для Wildfly начиная с версии 10 есть альтернативный вариант - Singleton deployments. Этот сервис существовал в JBoss 5/6, но был "выпилен" в JBoss 7 (6 EAP) в угоду соответствия стандартам Java EE. Раньше работало так: вы разворачиваете свои кластерные синглтоны в отдельное приложение, и "ложите" в отдельную папку развертывания. Отличие Wildfy в том, что вместо отдельной папки, требуется добавить в приложение специфический дескриптор развёртывания. JBoss/Wildfly гарантирует, что все такие приложения будут запускаться только в одном экземпляре на весь кластер. Плюс: нет лишней нагрузки. Минус: надёжность под сомнением, например, "сломался" протокол UDP, все узлы потеряли друг друга и решили стать главными и развернули ваш синглтон. Вообще, во всём что касается атомарности, лучше СУБД (ИМХО) ещё ничего не придумано.
  3. Реализуете вариант 1 и разворачиваете его как вариант 2. При таком раскладе избавляетесь от минусов обоих подходов, получая все их плюсы.


PS Касательно варианта 1. Если работа синглтонов не связана с обработкой записей БД, их всё равно можно синхронизировать таким же образом. Создаёте в любой таблице (например, в таблице каких-нибудь системных настроек) запись и синхронизируете синглтоны на ней.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@sirs
Никак не работает. Смотрите тут и тут

Есть какой-то встроенный механизм на уровне базы, что второй инстанс не может зайти в критическую секцию?

Транзакция вам не подходит? Или распределенные транзакции аля XA Transactions?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
Bell Integrator Ульяновск
До 400 000 ₽
Bell Integrator Хабаровск
До 400 000 ₽
Bell Integrator Ижевск
До 400 000 ₽
19 апр. 2024, в 03:01
1000 руб./за проект
18 апр. 2024, в 21:56
2000 руб./за проект
18 апр. 2024, в 21:00
150 руб./за проект