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 на нескольких серверах?
  • Вопрос задан
  • 1319 просмотров
Решения вопроса 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?
Ответ написан
Ваш ответ на вопрос

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

Похожие вопросы