2021年4月17日土曜日

自動DMLプロセスの行のロックに関するマニュアルの記述を確認する

以前から少し気になっていたので、以下のマニュアルの記述について、実際の動作を確認してみました。

アプリケーション・ビルダー・ユーザーズ・ガイド Release 20.2

18.8 DMLロックについて

翻訳の問題ではないことを確認するため、英語の方も参照しています。

App Builder User's Guide

18.8 About DML Locking

調べた結果としていえることは、マニュアルのDMLロックについてとして記載されている内容は、以前の静的コンテンツのリージョンを使ったフォームでのプロセス、タイプ行の自動処理(DML)[レガシー]にのみ有効な記述で、新しいフォーム・リージョンや対話グリッドはこの設定を参照していません。

動作を確認するために、SQLワークショップのサンプル・データセットからEMP/DEPTをインストールし、そのままアプリケーションを作成しました。

言語Japaneseを選んでいると、作成されるアプリケーションの名前デモ - 従業員/部門となるでしょう。何も変更せず、アプリケーションの作成を実行します。

これで動作確認に使用するアプリケーションは完成しました。

APEX_DML_LOCK_WAIT_TIMEの動作について確認します。

マニュアルの記載によると、この値が定義されていないときは無期限にロックの取得を待つ、となっています。

NULL (the default), results in the same behavior as previous versions of Oracle Application Express, that is, wait indefinitely.

 アプリケーションは作成したばかりなので、APEX_DML_LOCK_WAIT_TIMEは設定していません。

アプリケーションを実行します。従業員のレポートを開きます。

任意の従業員を選んで、編集フォームを開きます。


フォームが開くので、そのままにしておきます。


データベースにsqlplusで接続し、表EMPに排他ロックをかけます。

SQL> select * from emp for update;

フォームで変更の適用をクリックすると、ORA-00054 リソース・ビジー。NOWAITが指定されているか、タイムアウトしました、とメッセージが表示され、変更の適用は行われません。

実際にデバッグ・レベルを完全トレースまであげ、発行されているSQLを確認しても、for update nowaitとなっています。

アプリケーション置換文字列、アプリケーション・アイテムまたはページ・アイテムとして、APEX_DML_LOCK_WAIT_TIMEを設定することができる、と記載されているので、アプリケーション置換文字列として10秒を設定してみます。

同じ操作を行って動作の確認をします。その前にAPEX_DML_LOCK_WAIT_TIMEに設定した値がプロセスに渡っていることを、エラーが発生したときに確認できるよう、エラー・メッセージを以下に変更します。

待ち時間は&APEX_DML_LOCK_WAIT_TIME.秒にしています。

結果は変わらず、ORA-54が発生します。エラー・メッセージには待ち時間は10秒にしています。と表示されているため、APEX_DML_LOCK_TIME_WAITの値はプロセスに渡っています。

レガシーのフォームでの動作を確認するために、アプリケーション・ギャラリよりSample Database Applicationをインストールします。

サンプルとなるアプリケーションにレガシーな機能を使っているのか、というのはさておき、インストールしたアプリケーションの置換文字列として、APEX_DML_LOCK_TIME_WAITを10に設定しておきます。

アプリケーションを実行し、顧客の一覧から任意の顧客を選んで編集画面を開きます。

sqlplusより表DEMO_CUSTOMERSの排他ロックを取得します。

SQL> select * from demo_customers for update;

フォームのApply Changesをクリックすると10秒待ち、エラー・メッセージとしてresource busy; acquire with WAIT timeout expiredが表示されます。

マニュアルに記載されているとおりの動作です。

FSP_DML_LOCK_ROWについては、レガシーのプロセスであっても、行のロックとしてGUIから設定できるため、まったく使う機会はないでしょう。

Oracle APEXでは、ロストアップデートの保護のために、行のチェックサムやバージョンを取得する際にSELECT文を発行します。以前はFSP_DML_LOCK_ROWやAPEX_DML_LOCK_TIME_WAITの指定を参照して、for update wait 10といった調整ができましたが、新しいフォームおよび対話グリッドではfor udpate nowaitを発行し、待機せず、取得できなければエラーとする動作になっているようです。

どちらの設定でも一長一短があり、アプリケーションの用途に応じて設定を変える内容かと思います。オンライン操作でそれほど処理の競合が発生するとは思えず、また、バッチ処理などが同時に実行されているような場合は、明らかにnowaitが有利だと思います。

そうでない場合は、例えば、今回の例のように待ち時間10秒でロック待機するPL/SQLコードを行のロックに記述すればいいのかな?と思ったのですが、、、

declare
r emp%rowtype;
begin
select * into r
from emp
where empno = :APEX$PK1
for update wait 10;
end;

実際に行のロックに設定すると、ずっと待機します。

待機中のSQLを確認したところ、自動DMLプロセスが発行したUPDATE文で待機が発生していて、行のロックとして記述したPL/SQLコードではありません。

SQL> select sql_text from v$sql

  2  where sql_id in

  3  (

  4   select sql_id from v$session

  5   where sid in

  6   (

  7       select waiting_session from dba_waiters

  8   )

  9  );


SQL_TEXT

--------------------------------------------------------------------------------

update "EMP" set "ENAME"=:APEX$VAL2,"JOB"=:APEX$VAL3,"MGR"=:APEX$VAL4,"HIREDATE"

=:APEX$VAL5,"SAL"=:APEX$VAL6,"COMM"=:APEX$VAL7,"DEPTNO"=:APEX$VAL8 where "EMPNO"

=:APEX$PK1


update "EMP" set "ENAME"=:APEX$VAL2,"JOB"=:APEX$VAL3,"MGR"=:APEX$VAL4,"HIREDATE"

=:APEX$VAL5,"SAL"=:APEX$VAL6,"COMM"=:APEX$VAL7,"DEPTNO"=:APEX$VAL8 where "EMPNO"

=:APEX$PK1



SQL> 


海外のブログ記事でも、ずっと以前ですが同様の指摘をしている人を見つけました。

今までに調べた範囲では、行のロックにPL/SQL Codeを選択し、コードを記述しても、それは実行されないようです。結果としてPL/SQL CodeとNoの効果が同じになっています。

この件については、また別に時間をとって確認しようと考えています。

以上で今回の確認作業は終了です。

Oracle APEXのアプリケーション開発の参考になれば幸いです。