12

さまざまな JBoss ノードで実行される cron ジョブ用のある種のセマフォを実装しようとしています。データベース (Oracle 11g) をロック メカニズムとして使用して、1 つのテーブルを使用して異なるノードの cron ジョブを同期しようとしています。表は非常に単純です。

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 に更新します。この最初の選択は、Hibernate と Pessimistic Lock を使用して JPA CriteriaApi で行われます。

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

これらの操作はすべて、1 つのトランザクション内で行われます。

1 つのプロセスが実行されると、次のクエリが作成されます。

[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 はこの行での他の選択操作をブロックする必要があります。しかし、それがポイントです。クエリはブロックされていないため、2 つのジョブが問題なく入力して選択および更新できます。ロックが機能していません。2 つの cron ジョブを同時に実行すると確認できます。

[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=?

2 つの接続を使用して SQL ツール (SQLWorkbenchJ) でこの更新を選択しようとしましたが、このツール内でブロッキングが正常に機能しています。しかし、SQL ツールでこれを更新用に選択して cron ジョブを起動すると、ブロックされず、問題なく実行されます。

問題はJPA、Hibernate、またはOracleドライバーに起因すると思いますが、よくわかりません。どこに問題があるかについて何か考えはありますか?別の戦略を使用する必要がありますか? 前もって感謝します。

4

3 に答える 3

4

最後に、私はそれを機能させることができましたが、いくつかの変更がありました。アイデアは、PESSIMISTIC_WRITE の代わりに LockModeType.PESSIMISTIC_FORCE_INCREMENT を使用することです。このロック モードを使用すると、Cron ジョブは次のように動作します。

  1. 最初のジョブが更新を選択すると、すべてが期待どおりに進みますが、オブジェクトのバージョンが変更されます。
  2. 最初のジョブがまだトランザクション中に別のジョブが同じ選択を行おうとすると、JPA は OptimisticLockException を起動するため、その例外をキャッチすると、読み取りロックのためにスローされたことを確認できます。

このソリューションには、さまざまな対応物があります。

  1. SynchronizedCronJobTask にはバージョン フィールドがあり、@Version でバージョン管理されている必要があります
  2. OptimisticLockException を処理する必要があり、de lock が発生したときにロールバックするために、トランザクション サービス メソッドの外部でキャッチする必要があります。
  3. 私見は洗練されていないソリューションであり、Cronジョブが前のジョブが完了するのを待つ単純なロックよりもはるかに悪い.
于 2015-04-30T14:13:48.553 に答える
0

データを変更する前に、2 番目のサーバーが最初のサーバーの変更を認識している必要があるため、ロック モードを PESSIMISTIC_READ に設定します。

于 2015-04-30T10:11:42.657 に答える