Pega ロック競合を防ぐ設計|楽観ロックと悲観ロックの使い分けと回避策
「別のオペレータがこのケースを処理中です」——本番で頻発するこのエラーの多くは、ロック方式の選択とトランザクション設計に起因する。悲観ロックと楽観ロックはどちらかが常に正解ではなく、更新頻度とロック保持時間で使い分ける。本記事は競合が起きる構造から回避策までを整理する。
結論から:既定は悲観ロック、「長時間保持 × 低競合」だけ楽観に寄せる
「別のオペレータがこのケースを処理中です」——本番で頻発するこのエラーの大半は、ロック方式の選択とトランザクション設計に原因があります。Pega のロックは意識しなくても既定で働くため、設計時に「どちらのロック方式にするか」を明示的に判断しないまま本番に持ち込まれ、負荷が上がってから競合として表面化する、というのが典型的な流れです。
結論を先に言うと、判断の型はシンプルです。
既定は悲観ロックのままでよい。同時更新の頻度が低く、かつユーザー操作でロックを長時間抱えてしまう処理だけを、楽観ロックに切り替える。
悲観・楽観のどちらかが常に正解ということはありません。確実な排他が要る短時間の更新は悲観ロック、ロック保持時間が長くなりがちで競合が稀な処理は楽観ロック——この2軸で切り分けます。本記事では、Pega のロックの仕組みから、競合が起きる構造、そして回避策までを実装観点で整理します。
Pega のロックの基本 — 何が、いつ、どこに取られるか
まず、既定の**悲観ロック(Pessimistic Lock、ケースタイプ設定の「Allow one user」)**の挙動を押さえます。Pega ではケースを開いた時点で、そのケースに対する排他ロックが取得されます。ロックの実体はデータベースのロックテーブル(pr_sys_locks、システムクラス System-Locks)に書き込まれ、ロックが解放されるまで(保存・完了によるコミット、またはタイムアウトまで)保持されます。ロックを識別するキー(ハンドル)は、既定でそのケースの pzInsKey(インスタンスキー)です。
重要なのは「開いた時点で取る」という点です。ユーザーがケースを開いてフォームを表示した瞬間にロックが立ち、そのユーザーが保存・完了するか、離脱してタイムアウトするまで、他のユーザーや処理はそのケースを更新できません。これは「確実に一人しか触れない」状態を保証する強力な仕組みですが、裏を返せば開いている間ずっと他をブロックするということでもあります。
短時間で確定する更新であれば、この排他は理想的に働きます。問題が起きるのは、ロックの保持時間が長くなるケースや、多数の処理が同じ対象を取り合うケースです。
楽観ロックは何が違うのか
**楽観ロック(Optimistic Lock、ケースタイプ設定の「Allow multiple users」)**は、ケースタイプ単位で選択できる代替方式です。悲観ロックとの決定的な違いは、編集中はロックを保持しないという点にあります。
楽観ロックでは、ユーザーがケースを開いて編集している間、他のユーザーも同じケースを開けます。競合の検出は保存(コミット)時に行われます。具体的には、更新日時(pxSaveDateTime)を突き合わせ、「自分が読み込んだ後に、別の誰かが先に保存していないか」を確認します。もし先に更新されていれば、そのコミットは競合として弾かれ、ユーザーには「最新の内容を再読み込みして操作し直すか、変更を保存せずに閉じる」よう促されます。
ここが設計上の肝です。悲観ロックは「そもそも競合させない」方式ですが、楽観ロックは「競合が起きうる前提で、起きたら検出する」方式です。したがって楽観ロックを選ぶ場合は、競合が検出されたときのリカバリ処理を明示的に設計する必要があります。「保存に失敗しました。最新の内容を再読み込みして操作し直してください」とユーザーに促す、変更内容をマージする、リトライさせる——こうした後始末を実装しないと、ユーザーの入力が黙って失われる(更新喪失)事故につながります。この設計コストを払えるかどうかが、楽観ロック採用の分かれ目です。
判断軸:悲観か楽観か
2つの方式を、実務の判断ポイントで並べます。
| 判断ポイント | 悲観ロック(既定 / Allow one user) | 楽観ロック(任意設定 / Allow multiple users) |
|---|---|---|
| ロック取得のタイミング | ケースを開いた時点 | 取得しない(コミット時に検査) |
| 編集中の他ユーザー | 更新できない(排他) | 同じケースを開ける |
| 競合の扱い | そもそも起こさない | コミット時に検出して弾く |
| リカバリ処理 | 原則不要 | 必須(設計しないと更新喪失) |
| 向いている処理 | 短時間・確実な排他が要る更新 | 保持時間が長く・競合が稀な処理 |
| 主なリスク | ロックの抱え込み・競合待ち | リカバリ未設計時の更新喪失 |
| 設定単位 | ケースタイプ単位 | ケースタイプ単位 |
判断の型は次の通りです。確実な排他が要る短時間更新は悲観ロックのままにします。一方、ユーザーが画面を長時間開いたまま作業し、かつ同じケースを同時に更新する頻度が低い処理は、悲観ロックだと「一人が開いている間ずっと他がブロックされる」無駄が大きいので、楽観ロックに寄せます。楽観に寄せる判断とセットで、競合検出時のリカバリを必ず用意します。
ロック競合が起きる3つの典型パターン
本番で競合として噴出するのは、たいてい次の3パターンです。いずれも「悲観ロックがどこで、いつ、どれだけ長く取られるか」を設計時に見落としたことが原因です。
1. 多数の子ケースが単一の親ケースをロックする
Pega では既定で、子ケースを開くと親ケースにもロックがかかります(子の処理が親のデータ整合性に依存する前提の挙動)。子ケースが少数なら問題になりませんが、1つの親に対して多数の子ケースがぶら下がり、それらが並行して処理されると、子たちが親ロックを奪い合い、待ち行列と競合が発生します。処理量が増えるほど悪化するため、負荷試験や本番で初めて顕在化しがちです。
回避の起点は、親ロックの要否設定を見直すことです。子ケースの処理が本当に親のロックを必要としているのかを設計レベルで問い直し、不要であれば「子ケースを開いても他ユーザーが親ケースにアクセスできる」オプション(Allow other users to access parent case when the child case is opened)を有効にして、子の処理で親を巻き込まないようにします。なお、このオプションを使う場合、子ケースのロックタイムアウトは親のタイムアウトより短く設定する必要があります。
2. 長時間トランザクションでロックを抱え込む
1つの処理の中で、外部システム呼び出し・重い計算・ユーザーの長時間の入力待ちなどを挟むと、その間ずっと悲観ロックが保持されます。ロックを取ってから解放するまでの区間が長いほど、他の処理がブロックされる確率が上がります。特に、ユーザーが画面を開いたまま離席するような業務では、離席時間だけロックが居座ります。こうした処理こそ、楽観ロックへの切り替え候補です。
3. バックグラウンド処理とユーザー操作の衝突
キュープロセッサや Job Scheduler などのバックグラウンド処理が、ユーザーが操作中のケースを更新しにいくと、両者がロックを取り合います。バックグラウンド側がロックを取れずにリトライを繰り返したり、逆にユーザー操作が待たされたりします。バックグラウンド処理とユーザー操作が同じケースを触る導線がないかを設計時に洗い出し、必要なら処理対象や実行タイミングをずらします。
タイムアウト(ロックの有効時間)の落とし穴
Pega の悲観ロックには有効時間(タイムアウト)があり、設定で調整できます。これは「ロックを取ったまま応答がなくなった処理」を、いつまでも残さないための安全弁です。タイムアウトを過ぎたロックは**ソフトロック(期限切れロック)となり、別のリクエスタがそれを奪って(ブレークして再取得して)**更新できるようになります。ただし、この値の調整には両側のリスクがあります。
- 長すぎる → 異常終了やセッション切断で放置されたロックが、タイムアウトまで長時間残り続ける(孤立ロック)。その間、他のユーザーが「処理中です」と弾かれ続ける
- 短すぎる → まだ作業中のユーザーのロックが早期に期限切れとなり、その隙に別の処理がロックを奪って更新して、更新喪失につながりうる
つまりタイムアウトは「長くすれば安全」でも「短くすれば安全」でもなく、業務の実際の操作時間に合わせて調整する必要があります。既定のタイムアウトは一般に30分で、ケースタイプ単位(ロック設定の「アクセスタイムアウト」)またはシステム設定で延長・短縮できます。ただし設定箇所や挙動の細部はバージョン・UI(従来/Constellation)により異なる場合があるため、値をいじる前に、利用中の版のドキュメントと実機の挙動で必ず確認してください。
よくある失敗
「とりあえず既定のまま」で長時間トランザクションを放置する。 悲観ロックは既定で働くため、意識しなくても動いてしまいます。その結果、長時間画面を開く業務にまで悲観ロックが適用され、負荷が上がってから競合が噴き出します。設計時に「この処理はロックをどれだけ長く抱えるか」を一度は見積もるべきです。
リカバリを設計せずに楽観ロックへ切り替える。 楽観ロックは「競合しても止まらない」ように見えるため安易に選ばれがちですが、競合検出後の後始末を作らないと、ユーザーの入力が黙って消える更新喪失を招きます。楽観ロックの採用は、リカバリ設計とワンセットです。
親ロックの要否を検討せずに子ケースを量産する。 大量の子ケースが単一の親をロックする構造は、少数のうちは問題なく動くため見逃されます。子ケースの設計時に「親ロックは本当に要るのか」を問わないまま件数だけ増えると、後から競合の温床になります。
まとめ
- 既定は悲観ロック(Allow one user)のままでよい。 ケースを開いた時点で pr_sys_locks にロックが取られ、コミットかタイムアウトまで保持される(ロックキーは既定で pzInsKey)
- 楽観ロック(Allow multiple users)は編集中にロックを持たず、コミット時に更新日時(pxSaveDateTime)で競合を検出する。 採用するなら競合時のリカバリ処理を必ず設計する
- 判断軸は「短時間で確実な排他 → 悲観」「保持時間が長く低競合 → 楽観」の2つ
- 競合の典型は親子ケースのロック競合・長時間トランザクション・バックグラウンドとの衝突。回避の起点は親ロックの要否設定の見直し
- タイムアウトは長短どちらも副作用がある。既定は一般に30分・設定箇所は版依存なので実機で確認する
ロック競合は「動いているうちは見えず、負荷が上がると一気に噴き出す」性能課題です。設計の初期にロック方式とロック保持区間を言語化しておくことが、本番での手戻りを防ぐ最短ルートになります。
関連リンク
関連記事
Pega 宣言的処理のコスト設計|前方連鎖と後方連鎖の使い分け
Declare Expression は便利だが、前方連鎖のカスケードは1つのプロパティ変更が広範な再計算を引き起こす。宣言的処理は「書けば動く」半面、実行コストが見えにくい。本記事は前方連鎖と後方連鎖、宣言的と手続き的の境界線を、性能と保守性の観点で整理する。
記事を読むPega Insights と Report Definition の使い分け|セルフサービス分析と運用レポートの線引き
「レポートは Report Definition で作るべきか、Insights でユーザーに任せるべきか」——ダッシュボード設計で必ず出る分岐です。統制と再利用が要る定型レポートはルール化し、アドホックな探索はセルフサービスに開く、が基本方針。本記事では両者のガバナンス差とデータソースの前提から、線引きの判断軸を示します。
記事を読むPega 監査証跡設計:フィールド監査・履歴・セキュリティ監査の使い分け
「何を、どこまで監査するか」は Pega のセキュリティ設計で最も判断が割れるテーマです。フィールド監査・ケース履歴・セキュリティイベントの使い分けと、監査のかけ過ぎが招く性能・容量問題の回避策を、LSA の視点で解説します。
記事を読む