2015-04-30 1 views
10

Я пытаюсь реализовать какие-то семафоры для заданий cron, которые работают в разных узлах JBoss. Я пытаюсь использовать базу данных (Oracle 11g) в качестве механизма блокировки, используя одну таблицу, чтобы синхронизировать задания cron в разных узлах. таблица очень проста:Почему мой пессимистический Блокировка в JPA с Oracle не работает

CREATE TABLE SYNCHRONIZED_CRON_JOB_TASK 
(
    ID   NUMBER(10)   NOT NULL, 
    CRONJOBTYPE VARCHAR2(255 Byte), 
    CREATIONDATE TIMESTAMP(6)   NOT NULL, 
    RUNNING  NUMBER(1) 
); 

ALTER TABLE SYNCHRONIZED_CRON_JOB_TASK 
    ADD CONSTRAINT PK_SYNCHRONIZED_CRON_JOB_TASK 
    PRIMARY KEY (ID); 

Так что, когда работа начинается он ищет в таблице для записи своего cronjobtype, и проверяет, есть ли он уже запущен. Если он не обновляет флажок установки параметра, установленный в true. Этот первый выбор выполняется с помощью JPA CriteriaApi с использованием Hibernate и Pessimistic Lock.

query.setLockMode(javax.persistence.LockModeType.PESSIMISTIC_WRITE); 

Все эти операции производятся в течение одной транзакции.

Когда работает один процесс, то querys он делает следующий:

[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,048 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes 
[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) Hibernate: select * from (select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=?) where rownum <= ? 
[Server:server-two] 10:38:00,053 INFO [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update 
[Server:server-two] 10:38:00,056 INFO [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=? 

Там нет никаких проблем с этим предупреждением, вы можете увидеть первый выбор, а затем выбери для обновления, так что Oracle должна блокировать другую выберите операции над этой строкой. Но это точка, запросы не блокируются, поэтому можно ввести два задания и сделать выбор и обновление без проблем. Замок не работает, мы можем увидеть, если мы запустим два хрон рабочих мест одновременно:

[Server:server-one] 10:38:00,008 INFO [stdout] (scheduler-3) 2015-04-30 10:38:00,008 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes 
[Server:server-two] 10:38:00,008 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,008 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes 
[Server:server-two] 10:38:00,009 INFO [stdout] (scheduler-2) Hibernate: select * from (select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=?) where rownum <= ? 
[Server:server-one] 10:38:00,009 INFO [stdout] (scheduler-3) Hibernate: select * from (select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=?) where rownum <= ? 
[Server:server-two] 10:38:00,013 INFO [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update 
[Server:server-one] 10:38:00,014 INFO [stdout] (scheduler-3) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update 
[Server:server-two] 10:38:00,016 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,015 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false] 
[Server:server-two] 10:38:00,018 INFO [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=? 
[Server:server-one] 10:38:00,022 INFO [stdout] (scheduler-3) 2015-04-30 10:38:00,022 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false] 
[Server:server-one] 10:38:00,024 INFO [stdout] (scheduler-3) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=? 

Я попытался сделать это выбрать для обновления на инструмент SQL (SQLWorkbenchJ) с двумя соединениями и bloking работает в пределах этого инструмента. Но если я сделаю этот выбор для обновления на инструменте SQL и запустим задания cron, они не будут затухать и работать без проблем.

Я думаю, проблема связана с JPA, Hibernate или драйвером Oracle, но я не уверен. Любая идея о том, где проблема? Должен ли я использовать стратегию anotehr? Спасибо заранее.

+1

И вы реализуете «активные» ожидания внутри JBOSS? В тот момент, когда вы вернете соединение с пулом соединений, JBOSS выдает на него ROLLBACK и все блокировки теряются. Кроме того, ваши задания cron должны использовать SELECT FOR UPDATE, чистые чтения никогда не блокируются в Oracle. – ibre5041

+0

Я не уверен, что вы имеете в виду с активным ожиданием, я не использую никакой специальной функции JBOSS. Я хотел, чтобы функция ожидания выполнялась при операции выбора базы данных. –

+0

Я думаю, что речь идет не о пессимистической блокировке, а о транзакциях в целом. Даже чистый однократный UPDATE будет работать, пока транзакция открыта, а соединение сохраняется в потоке. В JPA, когда код оставляет транзакционный пограничный откат, все блокировки теряются. – ibre5041

ответ

4

Наконец-то мне удалось заставить его работать, но с некоторыми изменениями. Идея заключается в использовании LockModeType.PESSIMISTIC_FORCE_INCREMENT вместо PESSIMISTIC_WRITE. Используя этот режим блокировки, Cron Jobs ведут себя следующим образом:

  1. Когда первое задание делает выбор для обновления, все идет так, как ожидалось, но изменяется версия на объекте.
  2. Если другое задание пытается сделать тот же самый выбор, пока первый по-прежнему находится на его транзакции, JPA запускает исключение OptimisticLockException, поэтому, если вы поймаете это исключение, вы можете быть уверены, что его выбрали для блокировки чтения.

Это решение имеет различные аналоги:

  1. SynchronizedCronJobTask должны иметь поле версии и быть под контролем версий с @version
  2. Вы должны обращаться OptimisticLockException, и он должен быть улов вне транзакционной службы метод, чтобы сделать откат при блокировке.
  3. IMHO - это не элегантное решение, гораздо хуже, чем просто блокировка, где Cron Jobs ожидает завершения предыдущих заданий.
2

Установите режим блокировки на PESSIMISTIC_READ, поскольку вам необходимо, чтобы второй сервер знал об изменениях первого сервера перед сменой данных.

+0

Я пробовал PESSIMISTIC_READ, но он не работает. Кроме того, для этой ситуации режим правой блокировки - PESSIMISTIC_WRITE, который блокирует чтение и обновление. С PESSIMISTIC_READ блокируются только обновления. –

+0

У вас есть ошибка. Все наоборот: PESSIMISTIC_READ блокирует чтение и запись, но PESSIMISTIC_WRITE - только запись. –

+0

Привет, Александр. Ты уверен? В этой теме http://stackoverflow.com/questions/1657124/whats-the-difference-between-pessimistic-read-and-pessimistic-write Ответ Джозефа гласит: - PESSIMISTIC_READ. ... Этот режим блокировки не блокирует другие транзакции от чтения данных. Однако я попытался использовать PESSIMISTIC_READ, но не повезло. Может быть, я чего-то не хватает. –

 Смежные вопросы

  • Нет связанных вопросов^_^