※ 本記事は、Ulrike Schwinnによる”Sessionless Transactions in 23ai“を翻訳したものです。

2025年4月16日


23aiでは、重要度に応じたトランザクションの優先順位付けを可能にする優先トランザクション、頻繁に更新されるデータのホットスポットを減らすためのロックフリー列予約、ETAGを使用するステートレス・アプリケーション向けに設計された値ベース同時実行性制御など、データベース・トランザクションに関連するいくつかの新しいテクノロジを導入しました。これらの機能についてさらに学習する場合は、優先トランザクション(YouTubeブログ)、ロックフリー列予約(YouTubeブログ)およびETAGを使用した値ベースの同時実行性制御(YouTubeブログ)のビデオおよび投稿を参照してください。

さらに、23aiでのトランザクションに関するさらなる機能強化があります。23aiでのリリース更新機能について同僚の1人とのディスカッションで、セッションレス・トランザクションと呼ばれる新機能がデータベース・トランザクションに関して「ゲームチェンジャー」であると結論付けました。Oracleデータベース・リリース update 23.6では、FREEエディション、Exadata、ODA、またはOracle Cloudからアクセスできます。

簡単に説明します。ご存知のように、データベース・セッションまたは接続は、トランザクションが完了した後にのみ解放できます。セッションレス・トランザクションを使用することで、トランザクションは開始後いつでも一時停止および再開できます。これにより、セッションまたは接続がプールに解放され、他のトランザクションで使用できるようになります。その結果、複数のトランザクションとセッション/接続を効果的に同時に管理できます。

どのように使用できますか? サーバー上のセッションレス・トランザクションは、PL/SQLパッケージDBMS_TRANSACTIONの追加関数と、Oracle Call Interface (OCI)やOracle JDBCなどのAPIを持つクライアントで使用できます(後述の「参考文献」の項も参照)。

非常に興味深いユース・ケースとして、Oracle REST Data Services (ORDS)があります。ORDSでは、Oracle Database上にREST APIが公開されています。ステートレス・リクエストを使用します。つまり、リクエストの処理に必要なすべてのデータを含む完全な独立したリクエストが送信されます。この場合、リクエストごとに完全なトランザクションを指定する必要があります。セッションレス・トランザクション・データベース機能を使用すると、複数のHTTPsコールにまたがるトランザクションを作成できます。「Database 23aiでのセッションレス・トランザクションとREST API」に関するJeff Smithの優れたブログ記事を読んで、ORDSでの使用方法を知ることができます。

セッションレス・トランザクションはどのように機能しますか?
ユーザーは次のことを実行できます:

  • 一意のトランザクション識別子を指定してデータベース・セッションでトランザクションを開始し、作業単位を発行し、トランザクションを一時停止します。
  • 同じトランザクション識別子を使用して、別のセッションで同じトランザクションを続行します。

最後に、同じトランザクションをCOMMITまたはROLLBACKで別のセッションから終了できます。
 
この投稿では、PL/SQL API DBMS_TRANSACTIONの基本を説明し、セッションレス・トランザクションを開始、一時停止および再開する方法を説明します。単純な例では、2つのセッションとSCOTTなどのデータベース・ユーザーを使用して、セッションレス・トランザクションで行を挿入および更新およびコミットします。

セッション1: 
ユーザーSCOTTに接続し、表TESTTRを作成します。

SQL> connect scott/tiger@<servicename>
Connected.

-- check the environment
SQL> select sys_context('USERENV','SESSION_USER') who, 
            sys_context('USERENV','CON_NAME') PDB,
            sys_context('USERENV','SID') id;
WHO        PDB        ID
---------- ---------- ----------
SCOTT      WKAB       7637

SQL> create table TESTTR (t number);
Table created.

セッションレス・トランザクションを開始する前に、DBMS_TRANSACTION.GET_TYPEを使用してトランザクション・タイプを確認します。結果は、TRANSACTION_TYPE_LOCAL、TRANSACTION_TYPE_SESSIONLESS、TRANSACTION_TYPE_XAまたはNULL (アクティブなトランザクションがない場合)です。gettrans.sqlというスクリプトを使用して、その間のトランザクションのタイプを確認します。

SQL> start gettrans.sql
SQL> set serveroutput on
SQL> begin
     if DBMS_TRANSACTION.GET_TRANSACTION_TYPE() = DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
      then
        dbms_output.put_line('This is a sessionless transaction with GTRID: '||                
                                UTL_RAW.CAST_TO_VARCHAR2(DBMS_TRANSACTION.GET_TRANSACTION_ID()));
      else
        dbms_output.put_line('This is NOT a sessionless transaction');
      end if;
     end;
    /
This is NOT a sessionless transaction
PL/SQL procedure successfully completed.

すべてのセッションレス・トランザクションは、グローバル・トランザクションID (GTRID)と呼ばれる一意のトランザクション識別子によって識別されるため、クライアント・ドライバまたはデータベース・サーバーが適切なアクションを実行できます。必要なGTRIDを指定するか、OracleでGTRIDを生成して開始するセッションレス・トランザクションを識別できます。

トランザクションの一意の識別子を指定します。このため、関数CAST_TO_RAWを使用して、ユーザー定義の識別子文字列’my_test1’をRAW値に変換します。また、ここでタイムアウト値2000を指定します。この値は、トランザクションが一時停止した後にこのトランザクションを再開できる期間(秒)を定義します。

ここで、DBMS_TRANSACTION.START_TRANSACTIONを使用して新しいセッションレス・トランザクションを開始します。

SQL> set serveroutput on
SQL> declare
  2    gtrid VARCHAR2(128);
  3  begin
  4    gtrid := DBMS_TRANSACTION.START_TRANSACTION
  5    ( XID              => UTL_RAW.CAST_TO_RAW('my_test1')
  6    , transaction_type => DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
  7    , timeout          => 2000 
  8    , flag             => DBMS_TRANSACTION.TRANSACTION_NEW
  9    );
 10   dbms_output.put_line('GTRID is: ' || gtrid);
 11  end;
 12  /

GTRID is: 6D795F7465737431
PL/SQL procedure successfully completed.

トランザクションを再度確認します。トランザクションはセッションレスです。

SQL> start gettrans.sql
This is a sessionless transaction with GTRID: my_test1
PL/SQL procedure successfully completed.

1行を挿入し、表TESTTRを問い合せます。

SQL> insert into testtr values (1);
1 row created.
SQL> select * from testtr;
         T
----------
         1 

次のコマンドを使用してセッションを一時停止します。

SQL> execute DBMS_TRANSACTION.SUSPEND_TRANSACTION;

またテーブルに問い合わせましょう。

SQL> select * from testtr;
no rows selected

セッション2:

別のセッションでユーザーSCOTTとしてトランザクションを開始し、トランザクションがCOMMITでファイナライズされるまで、同じセッションおよびインスタンスにトランザクションを関連付け続けます。まず、セッションと表の内容を確認します。

SQL> select sys_context('USERENV','SESSION_USER') who, 
            sys_context('USERENV','CON_NAME') PDB,  
            sys_context('USERENV','SID') id;

WHO        PDB        ID
---------- ---------- ----------
SCOTT      WKAB       11259

SQL> select * from testtr;
no rows selected

SQL> start gettrans.sql;
This is NOT a sessionless transaction
PL/SQL procedure successfully completed.

タイムアウト値2000のため、トランザクションを再開する時間は残っています。トランザクションの停止後にトランザクションを再開するタイムアウト期間を満たせない場合はどうなりますか? この場合、次のエラーが表示されます:

DECLARE
*
ERROR at line 1:
ORA-26218: sessionless transaction with GTRID 6D795F7465737431 does not exist.
ORA-06512: at "SYS.DBMS_TRANSACTION", line 299

次のコマンドを使用してセッションを再開します。同じ一意のトランザクション識別子を持つ同じ関数START_TRANSACTIONを、フラグ値DBMS_TRANSACTION.TRANSACTION_RESUMEとともに使用します。

SQL> set serveroutput on
SQL> declare
  2      gtrid VARCHAR2(128);
  3  begin
  4      gtrid := DBMS_TRANSACTION.START_TRANSACTION
  5      ( xid              => UTL_RAW.CAST_TO_RAW('my_test1')
  6      , transaction_type => DBMS_TRANSACTION.TRANSACTION_TYPE_SESSIONLESS
  7      , flag             => DBMS_TRANSACTION.TRANSACTION_RESUME
  8      );
  9  dbms_output.put_line('Resumed GTRID: '||gtrid);
 10  end;
 11  /
Resumed GTRID: 6D795F7465737431
PL/SQL procedure successfully completed.

-- check the transaction
SQL> start gettrans.sql
This is a sessionless transaction with GTRID: my_test1
PL/SQL procedure successfully completed.

表を問い合せて更新します…

SQL> select * from testtr;
T
----------          
1 

SQL> update testtr set t=2; 
1 row updated. 

SQL> select * from testtr;          
T 
----------          
2

今すぐトランザクションを完了できます。COMMITを実行することにしました。

SQL> commit;
Commit complete

-- check the transaction
SQL> start gettrans
This is NOT a sessionless transaction
PL/SQL procedure successfully completed.

サマリーおよび追加の考慮事項

セッションレス・トランザクションは、データベース・アプリケーションでトランザクションを効率的に管理する機能です。ライフサイクル中にトランザクションを一時停止および再開する柔軟性が提供されます。セッションレス・トランザクション機能は、トランザクションをコミットまたはロールバックし、トランザクションとセッション間の結合を切断するネイティブ・メカニズムをアプリケーションに提供します。トランザクションを開始した後は、そのトランザクションをセッションまたは接続に関連付ける必要はありません。セッションまたは接続を解放して、別のクライアントで使用できるようにします。したがって、インダウト・トランザクションのリスクはなく、リカバリ・メカニズムも必要ありません。

セッションレス・トランザクションを使用する場合、Oracle Databaseとの通信時にトランザクション・マネージャを使用する必要はありません(単一インスタンスまたは複数インスタンス)。RACまたはRAC以外のデプロイメントでセッションレス・トランザクションを使用できます。データベースは、トランザクションを調整するすべての作業を行います。さらに、データベースは、アプリケーション側のロジックを必要とせずに、2フェーズ・コミット(2PC)プロトコルを内部的に調整します。

どのように使用できますか? 非常に興味深いシナリオは、すでに紹介したように、Oracle REST Data Services (ORDS)と連動しています。セッションレス・トランザクション・データベース機能を使用すると、複数のHTTPsコール間でトランザクションを持つことができます。たとえば、DBMS_TRANSACTIONを使用して、この機能を利用するPOSTハンドラを開発できます。Jeff Smithは、ここでの彼の投稿で非常にわかりやすい例でこれを実証しています。

しかし、それだけではありません。Oracle Call Interface (OCI)やOracle JDBCなどのAPIを使用して、サーバー側だけでなくクライアント側でもセッションレス・トランザクションを操作できます。そのため、サーバー上でコーディングする場合でも、クライアント側でアプリケーションを構築する場合でも、トランザクションをスムーズかつシームレスに維持できます。

参考文献