WebSphere Liberty 用のインダウト生成アプリ

ダミー・リソース・マネージャー(ダミーRM)を使って２フェーズ・コミット(2 phase commit, 以下、2pc)を中断させ、WebSphere Liberty （が稼働するノード）をダウンさせることで、トランザクションのリカバリー動作を検証できます。
前の記事でダミーRMを紹介しましたが、今回は、このダミーRMを使った検証用の Web アプリケーションである、インダウト生成アプリに関して記載します。

* [WebSphere Liberty 用のダミー・リソース・マネージャー](https://qiita.com/splendormy/items/1645a90bc16b56baf492)

インダウト生成アプリのソースは、github で公開しています。

* [github: GenIndoubtTran ](https://github.com/splendormy/GenIndoubtTran)

# ソースの説明

インダウト生成アプリは、JSP のみで実装されている Web アプリケーションで、下記のパターンの 2PC トランザクションを実行できます。

* DB と MOM にアクセスするパターン
    * 最初に DB、次に MOM にアクセスするパターン
    * 最初に MOM、次に DB にアクセスするパターン
* ２つの DB にアクセスするパターン
* １つの DB だけにアクセスするパターン
* １つの MOM だけにアクセスするパターン

色々なパターンに対応していますが、WebSphere Liberty のリカバリー動作を確認するだけであれば、どれか１つだけで良かったわけで、少しオーバー・スペックなものになっています。。。

処理フローはどのパターンでも同じなので、「最初に DB、次に MOM にアクセスするパターン」のコードをサンプルとして説明します。コードの主要部分は、以下の通りです。

```java
    // Start Transaction
    UserTransaction ut = lookupUserTransaction();
    ut.begin();

    // Dummy RM 1 - enlist
    DummyXAUtil.enlist( "dummy1", sleepPosition1, sleepDuration1 * 1000 );

    //---------------------------------------------------------------------
    // DB 
    //---------------------------------------------------------------------
    String sql1 = "INSERT INTO TESTTBL VALUES( CURRENT TIMESTAMP, \'2PC TEST: DB-MQ\' )";
    DataSource ds1 = lookupDataSource1();
    java.sql.Connection dbcon1 = ds1.getConnection();
    Statement st1 = dbcon1.createStatement();
    st1.executeUpdate( sql1 );
    st1.close();
    dbcon1.close();

    // Dummy RM 2 - enlist
    DummyXAUtil.enlist( "dummy2", sleepPosition2, sleepDuration2 * 1000 );

    //---------------------------------------------------------------------
    // MQ
    //---------------------------------------------------------------------
    ConnectionFactory cf = lookupConnectionFactory();
    Queue q = lookupQueue();
    javax.jms.Connection mqcon = cf.createConnection();
    mqcon.start();	
    Session ses = mqcon.createSession( false, Session.AUTO_ACKNOWLEDGE );
    MessageProducer sender = ses.createProducer( q );
    String msgStr = "2PC TEST: DB-MQ";
    TextMessage txtMsg = ses.createTextMessage( msgStr );
    sender.send( txtMsg );
    sender.close();
    ses.close();
    mqcon.close();

    // Dummy RM 3 - enlist
    DummyXAUtil.enlist( "dummy3", sleepPosition3, sleepDuration3 * 1000 );

    // Commit Transaction
    ut.commit();
```

コード内のコメントに書かれているように、主な処理内容は以下のようになります。ダミーRMに対しては、画面から入力された情報に従って、スリープするメソッドと長さを設定するようになっています。

1. 2PC トランザクションを開始する
1. １つめのダミーRMを enlist する（トランザクションに参加させる）
1. DB に１行挿入する
1. ２つめのダミーRMを enlist する（トランザクションに参加させる）
1. MOM に１つのメッセージを put する
1. ３つめのダミーRMを enlist する（トランザクションに参加させる）
1. 2PC トランザクションをコミットする

# デプロイ方法

インダウト生成アプリを稼働させるには、ダミーRMのフィーチャーの追加や、DB や MOM のリソース定義が必要になります。さらに、DB 内には、以下のようなテーブルを定義しておく必要があります。尚、DB のデータソース定義では、`type="javax.sql.XADataSource"` を指定し、XA 対応の JDBC ドライバーが使用されるように指定する必要があります。

<details><summary>テーブル定義（クリックで展開）</summary>
<div>

```sql
create table testtbl ( TS TIMESTAMP not null, TX CHARACTER(50) not null )
```
</div>
</details>

アプリ自体のデプロイは、通常のアプリケーションと同じですが、リソース参照を適切にマップする必要があります。（下記のサンプルでは、war ファイル内の [WEB-INF/ibm-web-bnd.xml](https://github.com/splendormy/GenIndoubtTran/blob/main/WebContent/WEB-INF/ibm-web-bnd.xml) で指定されているマッピングを使用しているため、server.xml 内ではマッピングが指定されていません。）

server.xml のサンプルは以下の通りです。DB として IBM Db2、MOM として IBM MQ を使う場合のものです。

<details><summary>server.xml ファイルの詳細（クリックで展開）</summary>
<div>

```xml:server.xml
<server>
    <featureManager>
        <feature>webProfile-7.0</feature>
        <feature>jms-2.0</feature>
        <feature>wmqJmsClient-2.0</feature>
        <feature>usr:dummy-resource-manager</feature>
    </featureManager>

    <jdbcDriver id="DB2Driver">
        <library>
            <fileset dir="/IBM/DB2Driver" includes="db2jcc4.jar"/>
        </library>
    </jdbcDriver>
    <dataSource id="SampleDS" jndiName="jdbc/sample" type="javax.sql.XADataSource" jdbcDriverRef="DB2Driver">
        <containerAuthData user="db2inst1" password="?????"/>
        <recoveryAuthData  user="db2inst1" password="?????"/>
        <properties.db2.jcc databaseName="sample" serverName="db2-db2u.db2.svc.cluster.local" portNumber="50000"/>
    </dataSource>

    <variable name="wmqJmsClient.rar.location" value="/IBM/MQRA/wmq/wmq.jmsra.rar"/>
    <jmsConnectionFactory id="jms_cf_qmtest" jndiName="jms/cf_qmtest">
        <containerAuthData user="admin" password="?????"/>
        <recoveryAuthData  user="admin" password="?????"/>
        <properties.wmqJms channel="DEV.ADMIN.SVRCONN" hostName="mq-ibm-mq.mq.svc.cluster.local" queueManager="QM_TEST"/>
    </jmsConnectionFactory>
    <jmsQueue id="jms_q_q1" jndiName="jms/q_q1">
        <properties.wmqJms baseQueueName="Q1"/>
    </jmsQueue>

    <transaction recoverOnStartup="true"
                 transactionLogDBTableSuffix="_WLP1"
                 recoveryGroup="wlp1"
                 recoveryIdentity="${env.HOSTNAME}">
        <dataSource transactional="false" jdbcDriverRef="DB2Driver">
            <properties.db2.jcc databaseName="tranlog"
                                serverName="db2-db2u.db2.svc.cluster.local" portNumber="50000"
                                user="db2inst1" password="?????"/>
        </dataSource>
    </transaction>

    <applicationManager autoExpand="true"/>
    <webApplication id="GenIndoubtTran" location="GenIndoubtTran.war" name="GenIndoubtTran"/>

</server>
```
</div>
</details>

# インダウト状態の生成

「最初に DB、次に MOM にアクセスするパターン」の 2PC トランザクションのフローを分かりやすくシーケンス図的に示すと次のようになります。

:::note warn
図や説明では、リソース・マネージャーにアクセスした順序と逆の順序で prepare が行われ、prepare の順序と逆の順序で commit が実行されるようになっています。仕様やマニュアルで確認したことはありませんが、WebSphere のトランザクション・マネージャーはこのような順序で 2PC を処理するようです。
:::

![2pcフロー_DummyRM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555650/185071b1-0e0e-9d28-4f83-7912f0a83f41.png)

2PC トランザクションのリカバリー動作を検証する場合は、図に示した障害シミュレーション・ポイントで処理を中断させ（ダミーRMの該当メソッド内でスリープさせ）、WebSphere Liberty （が稼働するノード）をダウンさせることになります。
インダウト生成アプリは、３つのダミーRMの指定されたメソッド内で処理を中断できるようになっていますが、ダミーRM #1 の prepare または commit メソッド内でスリープできれば良いわけで、この点に関し点も少しオーバー・スペックなアプリになっています。。。

２つの障害シミュレーション・ポイントで WebSphere Liberty をダウンさせた場合の状態と、想定されるリカバリー動作は以下のようになります。

* 障害シミュレーション・ポイント #1
    * DB と MOM は prepare が完了した状態
    * トランザクション・ログには **prepare 完了が記録されていない状態**
    * リカバリー動作でトランザクションは **rollback される**
* 障害シミュレーション・ポイント #2 
    * DB と MOM は prepare が完了した状態
    * トランザクション・ログには **prepare 完了が記録された状態**
    * リカバリー動作でトランザクションは **commit される**

ピア・リカバリーなどでトランザクション・ログが適切に引き継がれいていることを確認したいだけであれば、障害シミュレーション・ポイント #2 でダウンさせれば十分と思います。

尚、ダミーRMの指定メソッド内で処理が中断すると、WebSphere Liberty の messages.log に「Sleeping in commit. (30s)」ようなメッセージが出力されますので、この出力を確認してダウンさせることになります。

# 終わりに
WebSphere Liberty がトランザクションのピア・リカバリーに対応したことから始まった一連の投稿でしたが、今回で終了となります。
多くの方に役に立つような内容ではありませんが、必要となった方のお役に立てればと思います。