6.9 XAでのNode.jsアプリケーションの開発

6.9.1 トランザクション・イニシエータとしてのNode.jsアプリケーションの構成

トランザクション・イニシエータ・サービスは、トランザクションを開始します。トランザクション・イニシエータ・サービスは、アプリケーションのビジネス・ロジックに基づいて、トランザクションの開始のみ、またはトランザクションの開始とトランザクションへの参加を行うことができます。

開始する前に、アプリケーションがトランザクションを開始するだけか、トランザクションを開始して参加するのかを確認します。2つのシナリオでは要件が若干異なるため、それに応じてアプリケーションを構成します。

2つのシナリオを検討して、アプリケーションがトランザクションを開始するだけか、トランザクションにも参加するかを理解します。
  • シナリオ1: 銀行振込アプリケーションによって、ある部門から別の部門に送金します。この場合、振込アプリケーションはトランザクションを開始するだけで、トランザクションには参加しません。振込アプリケーションは、トランザクションを完了するためにビジネス・ロジックに基づいて様々なサービスをコールします。データベース・インスタンスは、振込アプリケーションにアタッチされる場合とアタッチされない場合があります。
  • シナリオ2: 銀行振込アプリケーションによって、ある部門から別の部門に送金します。振込アプリケーションは、トランザクションごとに手数料として1%を請求します。この場合、振込アプリケーションはトランザクションを開始して参加します。データベース・インスタンスは、トランザクション情報を保存するために振込アプリケーションにアタッチされる必要があります。
Node.jsアプリケーションをトランザクション・イニシエータとして構成するには:
  1. Node.jsのMicroTxライブラリをpackage.jsonファイルに依存関係として追加します。
    "dependencies": {
        "tmmlib-node": "file:tmmlib-node-23.4.2.tgz"
      }
  2. MicroTxライブラリのプロパティ値を構成します。ライブラリ・プロパティの構成を参照してください。Node.jsアプリケーションのロギングを有効にするには、追加のプロパティを設定する必要があります。「MicroTx Node.jsライブラリのログの有効化」を参照してください。
  3. プロパティ値を定義したtmm.propertiesファイルを渡して、マイクロサービスのMicroTxライブラリ・プロパティを構成します。
    TrmConfig.init('./tmm.properties');
  4. アプリケーション・コードを編集します:
    1. TrmUserTransactionオブジェクトを作成します。
    2. トランザクションを開始するために、作成したTrmUserTransactionオブジェクトに対してbegin()をコールします。begin()をコールするときに渡すパラメータは、アプリケーションがトランザクションを開始するだけか参加もするかによって異なります。
    3. トランザクションをコミットまたはロールバックするために、作成したTrmUserTransactionオブジェクトに対してcommit()またはrollback()をコールします。

    次の例は、utという名前のTrmUserTransactionオブジェクトを作成し、トランザクションを開始してからコミットまたはロールバックする方法を示しています。ここで、reqはリクエストを表します。

    //Step 3(a): Create a TrmUserTransaction object
    let ut: TrmUserTransaction = newTrmUserTransaction();
    try {
      //Step 3(b): Transaction demarcation - (start)
      await ut.begin(req);  //If your application only initiates the transaction and does not participate in it.
      await ut.begin(req, true);  //If your application initiates the transaction and participates in it.
    
      ... // implement business logic
      
      await ut.commit(req); //Step 3(c): Transaction demarcation - commit (end)
    
      resp.status(200).send("Transaction complete.");
    } 
    catch (e) {
      console.log("Transaction Failed: ", e);
      let message = e.message;
      try {
        console.log("Rollback on transaction failure.");
        await ut.rollback(req); //Step 3.c: Transaction rollback (end)
        message = message + ". Transaction rolled back. ";
      } catch (ex) {
        console.log("Error in rollback for transfer failure: ", ex);
      }
      resp.status(500).send(message);
    }

    例のコードは、try-catch文に実装されているため、エラーがあっても正常に処理されます。try-catch文を使用せずにサンプル・コードを実装することもできます。

  5. 変更内容を保存してからアプリケーションをデプロイします。アプリケーションのデプロイを参照してください。
イニシエータ・サービスが、トランザクションを開始するだけでなくトランザクションに参加する場合、トランザクションに参加してリソース・マネージャと通信するアプリケーションのために追加の構成を行う必要があります。トランザクション参加側としてのNode.jsアプリケーションの構成を参照してください。

6.9.2 トランザクション参加側としてのNode.jsアプリケーションの構成

リソース・マネージャがXAに準拠しているかどうかに応じて、環境変数を設定し、MicroTxクライアント・ライブラリの様々なクラスを実装します。

6.9.2.1 XA準拠リソース・マネージャを使用するNode.jsアプリケーションの構成

XA準拠のリソース・マネージャを使用する場合は、この項に記載されている情報を使用してNode.jsトランザクション参加側アプリケーションを構成します。

  1. Node.jsのMicroTxライブラリをpackage.jsonファイルに依存関係として追加します。
    "dependencies": {
        "tmmlib-node": "file:tmmlib-node-23.4.2.tgz"
      }
  2. MicroTxライブラリのプロパティ値を構成します。ライブラリ・プロパティの構成を参照してください。Node.jsアプリケーションのロギングを有効にするには、追加のプロパティを設定する必要があります。「MicroTx Node.jsライブラリのログの有効化」を参照してください。
    oracle.tmm.xa.XaSupportの値がtrueoracle.tmm.xa.LLRSupportの値がfalseに設定されていることを確認します。
    oracle.tmm.xa.XaSupport = true
    oracle.tmm.xa.LLRSupport = false
  3. プロパティ値を定義したtmm.propertiesファイルを渡して、マイクロサービスのMicroTxライブラリ・プロパティを構成します。
    TrmConfig.init('./tmm.properties');
  4. MicroTxライブラリをインポートします。
    import {Request, Response, Router} from 'express';
    import {XATransactionMethod, XAConfig, XADataSource, TrmXAResource} from "tmmlib-node/xa/xa";
    import {TrmConfig} from "tmmlib-node/util/trmutils";
    import {asyncHandler} from "tmmlib-node/util/asynchandler";
  5. リソース・マネージャとしてOracle Databaseを使用している場合は、さらに次のライブラリをインポートします。
    import {OracleXADataSource} from "tmmlib-node/xa/oraxa";
  6. ルーター・オブジェクトを作成します。
    たとえば、次のコードでは、bankSvcRouterという名前のルーター・オブジェクトが作成されます。一意のルーター名を指定します。
    const bankSvcRouter = Router();
  7. 次の形式を使用して、データベース接続の詳細をパラメータに指定します。
    dbConfig = export default {
    user : "database_user",
    password : "database_password",
    connectString : "database_connection_string"
    };

    説明

    • dbConfigは、作成するパラメータの名前です。
    • database_userおよびdatabase_passwordは、XA準拠のリソース・マネージャにアクセスするためのユーザー名とパスワードです。
    • connectionString: Oracle Databaseのデータ・ストアの接続文字列を入力します。
      • 非自律型Oracle Database (資格証明ウォレットを使用しないデータベース)を使用している場合は、次の形式を使用して接続文字列を入力します:
        jdbc:oracle:thin:@<publicIP>:<portNumber>/<database unique name>.<host domain name>
        たとえば:
        jdbc:oracle:thin:@123.213.85.123:1521/CustDB_iad1vm.sub05031027070.customervcnwith.oraclevcn.com
      • Oracle Database Cloud ServiceとOracle Cloud Infrastructureを一緒に使用している場合は、『Oracle Blockchain Platformの使用』Oracle Database Classic Cloud Service接続文字列の作成を参照してください。
      • Oracle Autonomous Transaction Processingを使用している場合は、次の形式を使用して接続文字列を入力します:
        jdbc:oracle:thin:@tcps://<host>:<port>/<service_name>?wallet_location=<wallet_dir>

        必要な詳細(ホスト、ポート、サービス名など)は、ウォレットを抽出したフォルダにあるtnsnames.oraファイルで確認できます。

        たとえば:

        jdbc:oracle:thin:@tcps://adb.us-phoenix-1.oraclecloud.com:7777/unique_connection_string_low.adb.oraclecloud.com?wallet_location=Database_Wallet
  8. データベース接続の詳細を含むパラメータを渡し、OracleXADataSourceオブジェクトを作成します。
    const xaPds: XADataSource = new OracleXADataSource(dbConfig);
  9. 作成したOracleXADataSourceオブジェクトをTrmXAResource.initメソッドに渡します。
    TrmXAResource.init(xaPds);
  10. getXaConnectionメソッドをコールして、データベース接続を初期化します。
    xaPds.getXAConnection();
  11. XAトランザクションに参加できる参加側サービス内のすべてのREST APIエンドポイントでXAConfigを初期化します。XAトランザクションに参加できるエンドポイント・メソッドは複数存在できます。エンドポイントごとにXATransactionMethodのインスタンスを作成し、XATransactionMethodの配列をXAConfigオブジェクトに渡します。

    次のコード・サンプルでは、/depositエンド・ポイントのオブジェクトを初期化する方法を示しています。

    
    const xaTransactionDeposit : XATransactionMethod = new XATransactionMethod("/deposit");
    const xaTransactionMethods : XATransactionMethod[] = [xaTransactionDeposit];
    const xaConfig: XAConfig = new XAConfig(bankSvcRouter, '/', xaTransactionMethods);
  12. ここではインターセプタを設定して、現在のグローバル・トランザクションで、これらのエンドポイントにコールを関与させるようにします。次のコード・サンプルでは、Express.jsルーターbankSvcRouterが、指定したエンドポイント/depositの受信リクエストを、指定した関数にルーティングする方法を示しています。
    //This is an endpoint that can participate in an XA transaction.
    bankSvcRouter.post('/deposit', (req, resp) => {
        doDeposit(req, resp); //business logic
    });
    
    async function doDeposit(req: Request, resp: Response) {
        console.log(`Nodejs department Service deposit() called`);
    //The following sample code demonstrates how you can use the connection object within your business logic. 
        let amount = 10;
        if (req.query.amount != null && typeof req.query.amount === 'string') {
            amount = parseInt(req.query.amount, 10);
        }
        // XA connection pool is created and managed by the MicroTx library
        // and is present in the context property of req object. 
        // This is available on endpoints that are part of a XA transaction.
        try {
            await req.context.xaConnection.connection.execute('UPDATE accounts SET amount = amount + :1 where account_id = :2', [amount, req.params.id]);
            resp.status(200).send();
        } catch (e: any) {
            resp.status(500).send();
        }
    }

6.9.2.2 非XAリソースを使用するNode.jsアプリケーションの構成

非XAリソース(MongoDBなど)を使用する場合は、この項に記載されている情報を使用してNode.jsトランザクション参加側アプリケーションを構成します。

  1. Node.jsのMicroTxライブラリをpackage.jsonファイルに依存関係として追加します。
    "dependencies": {
        "tmmlib-node": "file:tmmlib-node-23.4.2.tgz"
      }
  2. MicroTxライブラリのプロパティ値を構成します。ライブラリ・プロパティの構成を参照してください。Node.jsアプリケーションのロギングを有効にするには、追加のプロパティを設定する必要があります。「MicroTx Node.jsライブラリのログの有効化」を参照してください。
    oracle.tmm.xa.XaSupportの値がfalseoracle.tmm.xa.LLRSupportの値がtrueに設定されていることを確認します。
    oracle.tmm.xa.XaSupport = false
    oracle.tmm.xa.LLRSupport = true
  3. プロパティ値を定義したtmm.propertiesファイルを渡して、マイクロサービスのMicroTxライブラリ・プロパティを構成します。
    TrmConfig.init('./tmm.properties');
  4. MicroTxライブラリをインポートします。
    import {Request, Response, Router} from 'express';
    import {XATransactionMethod, XAConfig, TrmConfig, NonXAResource, TrmNonXAResource} from "../trmlib/xa";
  5. ルーター・オブジェクトを作成します。
    たとえば、次のコードでは、bankSvcRouterという名前のルーター・オブジェクトが作成されます。一意のルーター名を指定します。
    const bankSvcRouter = Router();
  6. NonXAResourceインタフェースを実装します。

    たとえば、次のコード・サンプルでは、MongoDbNonXAResourceクラスによってNonXAResourceインタフェースが実装されます。

    public class MongoDbNonXAResource implements NonXAResource {
    // Provide application-specific code for all the methods in the NonXAResource interface.
    }
  7. NonXAResourceインタフェースを実装するクラスを、XA操作を処理するためのMicroTxライブラリに登録します。

    次の例では、NonXAResourceインタフェースを実装するMongoDbNonXAResourceクラスをMicroTxライブラリに登録する方法について説明します。

    const nonxaResource: NonXAResource = new MongoNonXAResource();
  8. TrmNonXAResource.init()関数を使用して、MicroTxライブラリが使用するNonXAResourceオブジェクトを指定します。
    TrmNonXAResource.init(nonxaResource)
  9. 変更内容を保存します。