※ 本記事は、Martin Bachによる”Dealing securely with state changes in Multilingual Engine for Oracle Database 23c“を翻訳したものです。

2023年12月15日


ほとんどのPL/SQL開発者は、Connor McDonaldによって説明されているエラー「ORA-04068: パッケージの既存の状態は廃棄されました」を、このトピックに関する優れた投稿で認識しています。また、この記事では、新しい初期化パラメータであるSESSION_EXIT_ON_PACKAGE_STATE_ERRORを使用してORA-04068が発生した場合に、ステートフル・セッションをオプションで終了させる方法についても説明します。

Multilingual Engine (MLE)によって提供されるJavaScriptは、Oracle Database 23cでのPL/SQLと同様に動作します。この記事では、詳細をご紹介します。

ステートフル vs ステートレス・コード

ステートフル・アプリケーションは、多層/マイクロサービスの世界ではあまり一般的ではありませんが、作成することは可能です。JavaScriptモジュールに状態を格納する必要がある場合(おそらく実行すべきではないもの)、この投稿は、状態が破棄された後にクライアントに不正なデータを返すなどの問題を回避するのに役立ちます。トピックに関する背景については、https://connor-mcdonald.com/2022/11/09/from-stateful-to-stateless-pl-sql/ をお読みください。

JavaScriptコード

次のJavaScriptモジュールは、モジュールに状態を格納する1つの方法を示しています。関数のセットは、グローバル変数を増分し、それぞれその値を返します。

create or replace mle module session_state_mod
language javascript as

let counter = 0;

export function incrementCounter() {
    counter++;
}

export function getCounter() {
    return counter;
}
/

SQLおよびPL/SQLでJavaScriptコードを起動するには、コール仕様が必要です:

create or replace package session_state_pkg as

    procedure increment_counter
        as mle module session_state_mod
        signature 'incrementCounter()';

    function get_counter return number
        as mle module session_state_mod
        signature 'getCounter()';

end session_state_pkg;
/

Oracle Database 23c Freeを使用して、スキーマ EMILY に両方のスキーマ・オブジェクトを作成しました。

デフォルトの動作: ORA-04106の紹介

2つのセッションでコール仕様を起動するとどうなるかを見てみましょう。

セッション1

セッション1の画面出力を次に示します:

Connected to:
Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.3.0.23.09

SQL> exec session_state_pkg.increment_counter;

PL/SQL procedure successfully completed.

SQL> select
   2     session_state_pkg.get_counter;

GET_COUNTER
-----------
          1

ここには何も見えず、カウンタは1に増加します。

セッション2

セッション2の画面出力を次に示します:

SQL> exec session_state_pkg.increment_counter;

PL/SQL procedure successfully completed.

SQL> exec session_state_pkg.increment_counter;

PL/SQL procedure successfully completed.

SQL> select
   2     session_state_pkg.get_counter;

GET_COUNTER
-----------
          2

セッション1と同じ結果になりますが、カウンタは2に増分されます。

基礎となるJavaScriptモジュールの変更

JSDocコメントを追加するのを忘れたので、これを修正しましょう:

create or replace mle module session_state_mod
language javascript as

let counter = 0;

/**
 * this function increments the global variable
 */
export function incrementCounter() {
    counter++;
}

/**
 * this function gets the value of the global variable, counter
 * @returns {number} the value of the counter
 */
export function getCounter() {
    return counter;
}
/

JavaScriptモジュールのコードを変更すると、次の影響があります:

  1. MLEモジュールの再作成に使用されるセッションの実行コンテキストがリセットされます
  2. session_state_pkg からメンバー関数を以前に実行した他のすべてのセッションに対して、再度実行するとすぐにエラーがスローされます

両方のセッションでグローバル変数の値を取得してみましょう。セッション1は次を返します:

SQL> create or replace mle module session_state_mod 

...

MLE module created.

SQL> select
  2      session_state_pkg.get_counter;

GET_COUNTER
-----------
          0

予想どおり、実行コンテキストがリセットされ、session_state_pkg.get_counter へのコールは0を返します。JavaScriptコードを見ると、counter の値が0に初期化されていることがわかります。

セッション2のエラーは、特に以前にPL/SQLを使用したことがある場合にも驚くことではありません。

SQL> select
   2     session_state_pkg.get_counter;
select
*
ERROR at line 1:
ORA-04106: Module EMILY.SESSION_STATE_MOD referred to by SESSION_STATE_PKG.GET_COUNTER has been modified since the execution context was created.
Help: https://docs.oracle.com/error-help/db/ora-04106/

ORA-04106は、PL/SQLのORA-04068と同等のMLEです。これは、呼び出し仕様 yによって参照されるMLEモジュール xが、最初に呼び出されてから変更されたことを示します。MLE環境にも同様のエラーORA-4107があります。セッション・コンテキストの概念についてさらに学習したい場合は、ドキュメントの関連セクションに進んでください。

SESSION_EXIT_ON_PACKAGE_STATE_ERRORの影響

PL/SQLと同様に、SESSION_EXIT_ON_PACKAGE_STATE_ERROR をtrueに設定して、変更されたMLEモジュールを参照する実行コンテキストを持つセッションを自動的に終了するようにOracleに指示できます。これを実際に見てみましょう。初期化パラメータの値を変更した後、セッション1とセッション2の2つの新しいセッションを作成しました。

セッション1

データベースに接続した後、カウンタをもう一度増やしました。

Connected to:
Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.3.0.23.09

SQL> show parameter SESSION_EXIT_ON_PACKAGE_STATE_ERROR

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
session_exit_on_package_state_error  boolean     TRUE
SQL> exec session_state_pkg.increment_counter

PL/SQL procedure successfully completed.

SQL> select
  2      session_state_pkg.get_counter;

GET_COUNTER
-----------
          1

セッション2

セッション1と同様に、カウンタを2回増分しました。

Connected to:
Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.3.0.23.09

SQL> show parameter SESSION_EXIT_ON_PACKAGE_STATE_ERROR

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
session_exit_on_package_state_error  boolean     TRUE
SQL> exec session_state_pkg.increment_counter

PL/SQL procedure successfully completed.

SQL> exec session_state_pkg.increment_counter

PL/SQL procedure successfully completed.

SQL> select
  2      session_state_pkg.get_counter;

GET_COUNTER
-----------
          2

ステージが決まりました!

基礎となるコードの変更

セッション1でMLEモジュールを変更し、何が起こるかを見てみましょう。更新されたコードは次のとおりです(実際には追加のコメントにすぎませんが、これは問題ではありません)

SQL> create or replace mle module session_state_mod
  2  language javascript as
  3
  4  let counter = 0;
  5
  6  /**
  7   * this function increments the global variable. You cannot access
  8   * the counter directly, please use getCounter() instead.
  9   */
 10  export function incrementCounter() {
 11      counter++;
 12  }
 13
 14  /**
 15   * this function gets the value of the global variable, counter
 16   * @returns {number} the value of the counter
 17   */
 18  export function getCounter() {
 19      return counter;
 20  }
 21  /

MLE module created.

セッション1では、カウンタが0にリセットされますが、データベースに接続されたままになります:

SQL> select
  2       session_state_pkg.get_counter;

GET_COUNTER
-----------
          0

SQL> select
  2      user;

USER
---------------------------------
EMILY

ただし、セッション2は終了します。

SQL> select
  2       session_state_pkg.get_counter;
select
*
ERROR at line 1:
ORA-04106: Module EMILY.SESSION_STATE_MOD referred to by SESSION_STATE_PKG.GET_COUNTER has been modified since the execution context was created.
Help: https://docs.oracle.com/error-help/db/ora-04106/


SQL> select
  2       user;
ERROR:
ORA-03114: not connected to ORACLE
Help: https://docs.oracle.com/error-help/db/ora-03114/

まとめ

Oracle Database Free 23cでは、PL/SQLとJavaScriptの両方に適用可能な新しい初期化パラメータSESSION_EXIT_ON_PACKAGE_STATE_ERRORが導入されています。コードでパッケージまたはMLEモジュールに状態が格納されている場合は、SESSION_EXIT_ON_PACKAGE_STATE_ERROR をtrueに設定して(サイレント)データ破損を回避すると有益です。通常、停止したプログラムは、機能不全に陥ったプログラムよりもダメージがはるかに少なくなるという格言に私は完全に同意します。(「The Pragmatic Programmer」から引用)。この記事に対応する前述のPL/SQLと、読者による追加の考えやコメントを必ずお読みください。