2023年8月18日金曜日

リージョン属性のサーバー・キャッシュについて

 リージョン属性にサーバー・キャッシュというのがあるけど、これを有効にしたら表示が速くなるの?と聞かれたので、調べてみました。この機能を使った経験はありません。


マニュアルの記載は以下になります。

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

ページ属性にも同様にサーバー・キャッシュがあります。


ページ属性のサーバー・キャッシュについては、マニュアルに説明を見つけることができませんでした。本来であれば、7.4 ページ属性の管理で説明されているはずの属性です。

最近のWebページは、すべてのHTMLをサーバー側で生成するということはなく、クライアント側のJavaScriptによる画面の書き換えも一般的です。このサーバー・キャッシュの機能は、JavaScriptの使用がまだ一般的ではなかった頃(そしてサーバー側のリソースが今より弱かった頃)は有効だった、古の機能と言えます。

これより、リージョン属性のサーバー・キャッシュを中心に、その動作を確認してみます。

動作確認に使用するアプリケーションは、以下の手順で作成します。

アプリケーション作成ウィザードを起動します。アプリケーションの名前Server Cacheとします。

デフォルトで作成されているホーム・ページ削除し、表EMPをソースとしたフォーム付きのクラシック・レポートと、同様に表EMPをソースとした(フォームを付けない)クラシック・レポートのページを追加します。


以上でアプリケーションを作成します。

作成したアプリケーションのホーム・ページが以下になるよう、Deptnoのページ・アイテム、レポートをリフレッシュするボタン、キャッシュされたリージョンのデータをパージするボタンを作成します。


レポートの検索条件に使用する部門番号を保持するページ・アイテムを作成します。

識別名前P1_DEPTNOタイプテキスト・フィールドを選択します。ラベルDeptnoとし、設定[Enter]を押すと送信オンにします。


クラシック・レポートのリージョンのソースWHERE句deptno = :P1_DEPTNOを記述します。送信するページ・アイテムとしてP1_DEPTNOを設定します。これでクラシック・レポートは、指定された部門に所属する従業員のみを一覧表示します。

静的IDemployeesは、キャッシュされたリージョンのデータをパージする際に必要な、リージョンIDを取得するために使用します。


ボタンREFRESHを作成し、ボタンをクリックするとレポートをリフレッシュする動的アクションを作成します。

ラベルRefresh外観テンプレート・オプションWidthStretchを指定しています。動作アクション動的アクションで定義を選択します。


動的アクション名前レポートのリフレッシュタイミングイベントはボタンのデフォルトであるクリックです。


TRUEアクションとしてリフレッシュを選択します。影響を受ける要素選択タイプとしてリージョンを選び、リージョンとしてクラシック・レポートであるEmployeesを選択します。

以上でボタンREFRESHを押すと、クラシック・レポートが更新されるようになりました。


キャッシュされたリージョンのデータをパージするボタンPURGE_CACHEを作成します。

ラベルPurge Cacheレイアウト新規行の開始オフにして、ボタンREFRESHの右隣に配置します。外観テンプレート・オプションWidthを、ボタンREFRESHと同様にStretchに変更します。動作アクションはデフォルトのページの送信から変更しません。


このボタンPURGE_CACHEをクリックしたときに実行するプロセスを作成します。

左ペインでプロセス・ビューを開き、プロセスを新規作成します。

識別名前キャッシュのパージタイプとしてコードを実行を選択します。ソースPL/SQLコードとして以下を記述します。


サーバー側の条件ボタン押下時PURGE_CACHEを設定します。


以上でサーバー・キャッシュの動作を確認するアプリケーションは完成です。

キャッシュされているデータの確認は、ワークスペースの管理から行います。

ワークスペースの管理からサービスの管理を開きます。


画面右のアプリケーション・キャッシュを開きます。


リージョン単位でキャッシュされたデータを確認するには、キャッシュされたリージョン別リージョンのパージを開きます。


今の所何もキャッシュされていないので、何も表示されません。


リージョンEmployeesサーバー・キャッシュキャッシュ有効にします。有効の場合、すべてのアクセスで同じキャッシュを使用します。ユーザー別またはセッション別にキャッシュを持つことも可能です。

それ以外は特に設定せず、効果を確認してみます。


アプリケーションを実行します。

最初は部門番号が設定されていないため、以下のように一行も従業員が表示されません。


しかし、リージョンのキャッシュは有効なので、この時点でレポートのHTML出力はキャッシュされています。

アプリケーション・キャッシュの画面から確認できます。


Deptno10を入力し、Enterを入力します。P1_DEPTNOとして10が送信され、レポートが再表示されますが、データが見つかりませんと表示されます。

これはキャッシュにヒットしているためで、この時点ではDeptnoに何を入力しても、結果はデータが見つかりませんとなります。


しかし、ボタンRefreshをクリックすると、Deptno = 10(ACCOUNTING)に所属する従業員が一覧されます。これはクライアント側でレポートの描画を行なっているためです。


クライアント側で描画されたレポートはサーバー側でキャッシュされません。Deptnoに20を入力しEnterを押すと、キャッシュにヒットするためデータが見つかりませんと表示されます。


レポートの遅延ロードオンの場合も、動的アクションによるリフレッシュと同様にクライアント側で描画されるため、サーバー・キャッシュの影響を受けません。


Deptno30を入力しEnterを押します。遅延ロードオンの場合は、部門がSALESの従業員が一覧されます。


遅延ロードオフに戻し、データの更新を行ってみます。

Deptno20を入力し、Purge Cacheをクリックします。

今までキャッシュされていたリージョンのデータがパージされます。その後にDEPTNO = 20の条件でレポートが表示されます。このレポートは新たにキャッシュされます。(そのため、Deptnoに10や30を入れてEnterを押しても、部門RESEARCHの一覧が表示されます。)


従業員ADAMSの編集フォームを開き、Salaryを1100から2200に変更します。

変更の適用をクリックします。


フォームが閉じると、ADAMSのSalaryが2200に更新されています。


しかし、DeptnoでEnterを押してレポートを更新すると、ADAMSの給与は1100に戻ります。フォームを閉じたときにリージョンがリフレッシュ(つまり、クライアント側で描画)されるためです。データベースで保持されているSalaryは2200に更新されています。


まとめると、リフレッシュにより表示を更新できるリージョン・タイプでは、サーバー・キャッシュは役に立たないので設定すべきでない、といえます。

その点を考慮すると、クラシック・レポート(遅延ロードはオフ、リフレッシュが発生する処理は含めない)および動的コンテンツ(同様に遅延ロードはオフ、リフレッシュが発生する処理は含めない)であれば、サーバー・キャッシュを活用できそうです。

今回のようなケースではサーバー・キャッシュアイテムに依存P1_DEPTNOを設定します。

リージョンがキャッシュされるにあたり、指定したページ・アイテムの値が紐付けされます。


アイテムに依存P1_DEPTNOを設定すると、Deptnoに10、20、30と値を入れてEnterを押したとき、指定した部門の従業員が一覧されるようになります。


レポートの表示を行う際に、P1_DEPTNOが前回の表示と同じ値であればキャッシュされたHTMLを表示します。SELECT文は実行されません。P1_DEPTNOの値が異なる場合は、キャッシュを破棄し、SELECT文を実行してHTMLを生成します。その後、次の要求に備えて生成したHTMLをキャッシュします。

検索条件を変えずにレポートの再表示が行われるケースが多ければ、サーバー・キャッシュを設定することによりサーバーの負荷を軽減できる可能性があります。

キャッシュが使用される条件を設定することもできます。

タイプとしてアイテムはコロンで区切られたリストに含まれるを選択し、アイテムP1_DEPTNOリストとして10:20を指定します。


サーバー・キャッシュが有効なのはDeptnoが10と20のときに限る、という設定になります。

Deptno10を入力しボタンPurge Cacheをクリックして、Deptnoが10のレポートがキャッシュされた状態にします。


従業員MILLERの編集フォームを開き、Salary2600に変更します。


MILLERのSalaryはレポート上も2600に更新されますが、これはリージョンがリフレッシュされたため(クライアント側で再描画が行われたため)です。


Deptnoは10のまま変更せずEnterを入力すると、キャッシュされたデータで置き換えられるため、MILLERのSalaryは1300へ戻ります。


Deptnoの10および20はキャッシュの対象ですが、30と40は対象から外しています。Deptnoを30にして、同様の操作を行ってみます。

Deptno30を入力し、ボタンPurge Cacheをクリックします。


JAMESSalary2000に変更します。


フォームが閉じるとJAMESのSalaryは2000になります。また、Deptnoの30はキャッシュの対象ではないため、Deptnoを30としてEnterを入力してもSalaryはデータベースに保存されている2000を表示します。


今までの手順でクラシック・レポートより更新フォームを開いていますが、この操作ができるのは同じセッションでキャッシュされたHTMLに限定されます。異なるセッションよりキャッシュされたHTMLの場合、編集フォームを開こうとするとチェックサムに関するエラーが発生します。

編集リンクに含まれているcs=で与えられているチェックサムの値は、リージョンをキャッシュしたセッションで計算されています。検証に使うチェックサムはキャッシュを参照しているセッションで計算されるので、セッションが異なるためチェックサムが一致しません。


以上のように、サーバー・キャッシュが活用できる場面はかなり限定されています。とはいえ、活用できる条件に合致さえすれば、間違いなくサーバーの負荷を減らすことができるでしょう。

ページのサーバー・キャッシュは、さらに活用できる場面は限定されています。


ページ上で対話的なオペレーションはなく、データベースのデータを参照してページを生成しているが、内容の更新が発生しない場合は、ページのサーバー・キャッシュを活用できそうです。


上記のように表EMPの一覧を表示しているような状態で、表EMPに変更があったときにAPEX_PAGE.PURGE_CACHEを呼び出してキャッシュを削除する、といった運用をすると、このページのHTMLを生成する負荷を削減できます。

キャッシュされたページについては、アプリケーション・キャッシュキャッシュされたページ別ページのパージより確認できます。


キャッシュ・モード = 有効を条件として、ページを一覧します。


サーバー・キャッシュについての説明は以上になります。

今回の説明に使用したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/server-cache.zip

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