8.15. トランザクションリカバリー

クラスターがスケールダウンしたとき、トランザクションブランチがインダウトの状態になる可能性があります。そのような場合、手動によるトランザクションリカバリー が必要になることがあります。

重要

自動化トランザクションリカバリー機能は OpenShift 3 でのみサポートされ、この機能はテクノロジープレビューとしてのみ提供されます。

OpenShift 4 では、EAP Operator を使用してトランザクションを安全にリカバリーできます。安全なトランザクションリカバリーの EAP Operator を参照してください。

8.15.1. サポートされないトランザクションリカバリーのシナリオ

  • JTS トランザクション

    親のネットワークエンドポイントはリカバリーコーディネーター IOR でエンコードされるため、子または親ノードのいずれかが新しい IP アドレスでリカバリーしたり、仮想化された IP アドレスを使用してアクセスされる目的である場合は、リカバリーが確実に動作しません。

  • XTS トランザクション

    XTS はリカバリー目的ではクラスター化された状況で動作しません。詳細は JBTM-2742 を参照してください。

  • OpenShift 3 では、JBoss Remoting 上で伝搬されるトランザクションはサポートされません。
注記

JBoss Remoting 上で伝搬されるトランザクションは OpenShift 4 と EAP のオペレーターでサポートされます。

  • XATerminator 上で伝搬されるトランザクション

    EIS は、Java EE アプリケーションサーバーの単一インスタンスに接続することを目的とするため、これらのプロセスに対応する明確に定義された方法はありません。

8.15.2. 手動のトランザクションリカバリープロセス

8.15.2.1. 注意事項

この手順は、単一の JVM 内で完全に自己完結したトランザクションを手動でリカバリーする方法のみを説明しています。この手順の説明は、別の JVM に伝搬された JTA トランザクションをリカバリーする方法ではありません。

重要

同じ IP アドレスと同じノード名を持つ同じ Pod の複数のインスタンスを OpenShift が起動する可能性があり、パーティションにより古い Pod が稼働するさまざまなネットワークパーティションのシナリオがあります。そのため、手動リカバリーの間に、オブジェクトストアの古いビューを持つ Pod に接続される可能性があります。これに該当すると思われる場合は、すべての JBoss EAP Pod をシャットダウンし、使用中のリソースマネージャーやオブジェクトストアが存在しないようにします。

XA トランザクションでリソースを登録する場合、各リソースタイプがリカバリーでサポートされることを確認するのはユーザーの責任となります。たとえば、PostgreSQL と MySQL はリカバリーで適切に動作することが知られています。 しかし、A-MQ や JDV リソースマネージャーはの場合は、特定の OpenShift リリースのドキュメントをチェックする必要があります。

デプロイメントは JDBC オブジェクトストア を使用する必要があります。

重要

トラザクションマネージャーは、ノード識別子が一意であることに依存します。XID の最大バイト長は XA 仕様によって設定され、変更できません。JBoss EAP for OpenShift イメージが XID に含めなければならないデータにより、ノード識別子には 23 バイトの空き領域があります。

OpenShift では以下をこの 23 バイトの制限に合わせるよう強制されます。

  • すべてのノード名。 23 バイト未満の名前でも - (ダッシュ) は削除されます。
  • 名前が 23 バイトを超える場合、名前の最初から 23 バイトの長さまでに省略されます。

しかし、これにより識別子の一意性に影響を与える可能性があります。たとえば、aaa123456789012345678m0jwhbbb123456789012345678m0jwh は両方 123456789012345678m0jwh に省略され、名前の一意性が保たれなくなります。this-pod-is-m0jwhthispod-is-m0jwh の場合でも、両方が thispodism0jwh に省略され、一意性が保たれなくなります。

上記の省略処理を念頭に置いて、設定するノード名が一意になるようにする必要があります。

8.15.2.2. 前提条件

OpenShift インスタンスは JDBC ストアと設定され、ストアテーブルは Pod 名に対応するテーブル接頭辞を使用してパーティションされることを前提とします。これは、JBoss EAP デプロイメントを使用する場合は常に自動である必要があります。稼働中の Pod で transaction サブシステムの設定を確認すると、JBoss EAP インスタンスが JDBC オブジェクトストアを使用していることが確認できます。

  1. /opt/eap/standalone/configuration/openshift-standalone.xml 設定ファイルに transaction サブシステムの要素が含まれることを確認します。

    <subsystem xmlns="urn:jboss:domain:transactions:3.0">
  2. JDBC オブジェクトストアが使用されている場合、以下と似たエントリーが存在します。

    <jdbc-store datasource-jndi-name="java:jboss/datasources/jdbcstore_postgresql"/>
    注記

    JNDI 名は、トランザクションログの格納に使用されるデータソースを識別します。

8.15.2.3. 手順

重要

以下の手順は、データソースのみに対する手動トランザクションリカバリーのプロセスについて説明します。

  1. データベースベンダーのツールを使用して、インダウト状態のブランチの XID (トランザクションブランチ識別子) をリストします。失敗またはスケールダウンした Pod で実行中であったすべてのデプロイメントが使用していたすべてのデータソース の XID をリストする必要があります。使用しているデータベース製品のドキュメントを参照してください。
  2. このような各 XID に対して、トランザクションを作成した Pod を特定し、その Pod が今も実行中であるかを確認します。

    1. 実行中である場合、ブランチはそのままにしておきます。
    2. Pod が実行していない場合、クラスターから削除されたと仮定し、ここで説明する手動の解決手順を適用する必要があります。失敗した Pod によって使用されたトランザクションログストレージに、対応するトランザクションログがあるかどうかを確認します。

      1. ログがある場合、ベンダーのツールを使用して XID を手動でコミットします。
      2. ログがない場合、orphan ブランチであることを仮定し、ベンダーのツールを使用して XID をロールバックします。

これ以降の手順では、各ステップの実行方法を詳細に説明します。

8.15.2.3.1. インダウト状態のブランチの解決

最初に、デプロイメントが使用しているリソースをすべて探します。

この作業は、JBoss EAP 管理 CLI を使用して行うことが推奨されます。リソースは JBoss EAP の standalone-openshift.xml 設定ファイルに定義する必要がありますが、アプリケーションサーバー内で transaction サブシステムが利用できるようにする方法が他にあります。たとえば、デプロイメントのファイルを使用したり、ランタイムで管理 CLI を動的に使用したりして行うことができます。

  1. 失敗した Pod のクラスターで JBoss EAP インスタンスを実行する Pod にてターミナルを開きます。対象の Pod がない場合は、その Pod に対してスケールアップします。
  2. /opt/eap/bin/add-user.sh スクリプトを使用して管理ユーザーを作成します。
  3. /opt/eap/bin/jboss-cli.sh スクリプトを使用して、管理 CLI にログインします。
  4. サーバーに設定されたデータソースをリストします。これらにインダウト状態のトランザクションブランチが含まれる可能性があります。

    /subsystem=datasources:read-resource
    {
        "outcome" => "success",
        "result" => {
        	"data-source" => {
            	"ExampleDS" => undefined,
            	...
        	},
      ...
    }
  5. リストを表示したら、各データソースの接続 URL を見つけます。例を以下に示します。

    /subsystem=datasources/data-source=ExampleDS:read-attribute(name=connection-url)
    {
        "outcome" => "success",
        "result" => "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE",
        "response-headers" => {"process-state" => "restart-required"}
    }
  6. 各データソースに接続し、インダウト状態のトランザクションブランチをすべて表示します。

    注記

    インダウト状態のブランチを格納するテーブル名は各データソースベンダーごとに異なります。

    JBoss EAP には、各データベースをチェックするのに使用できるデフォルトの SQL クエリーツール (H2) があります。例を以下に示します。

    java -cp /opt/eap/modules/system/layers/base/com/h2database/h2/main/h2-1.3.173.jar \
    -url "jdbc:postgresql://localhost:5432/postgres" \
    -user sa \
    -password sa \
    -sql "select gid from pg_prepared_xacts;"

    この代わりに、リソースのネイティブツールを使用することもできます。たとえば、sampledb と呼ばれる PostGreSQL データソースでは、OpenShift クライアントツールを使用して Pod にリモートでログインし、インダウト状態のトランザクションテーブルにクエリーを実行することができます。

    $ oc rsh postgresql-2-vwf9n # rsh to the named pod
    sh-4.2$ psql sampledb
    psql (9.5.7)
    Type "help" for help.
    
    sampledb=# select gid from pg_prepared_xacts;
    131077_AAAAAAAAAAAAAP//rBEAB440GK1aJ72oAAAAGHAtanRhLWNyYXNoLXJlYy0zLXAyY2N3_AAAAAAAAAAAAAP//rBEAB440GK1aJ72oAAAAGgAAAAEAAAAA
8.15.2.3.2. グローバルトランザクション ID およびノード識別子を各 XID から取得

インダウト状態のブランチの XID がすべて特定できたら、XID をトランザクションマネージャーのトランザクションテーブルに保存されたログと比較できる形式に変換します。

たとえば、この変換に以下の Bash スクリプトを使用することができます。$PG_XID が上記の select ステートメント からの XID を保持する場合、以下のように JBoss EAP トランザクション ID を取得することができます。

PG_XID="$1"
IFS='_' read -ra lines <<< "$PG_XID"
[[ "${lines[0]}" = 131077 ]] || exit 0; # this script only works for our own FORMAT ID
PG_TID=${lines[1]}

a=($(echo "$PG_TID"| base64 -d  | xxd -ps |tr -d '\n' | while read -N16 i ; do echo 0x$i ; done))
b=($(echo "$PG_TID"| base64 -d  | xxd -ps |tr -d '\n' | while read -N8 i ; do echo 0x$i ; done))
c=("${b[@]:4}") # put the last 3 32-bit hexadecimal numbers into array c
# the negative elements of c need special handling since printf below only works with positive
# hexadecimal numbers
for i in "${!c[@]}"; do
  arg=${c[$i]}
  # inspect the MSB to see if arg is negative - if so convert it from a 2’s complement number
  [[ $(($arg>>31)) = 1 ]] && x=$(echo "obase=16; $(($arg - 0x100000000 ))" | bc) || x=$arg
  if [[ ${x:0:1} = \- ]] ; then # see if the first character is a minus sign
     neg[$i]="-";
     c[$i]=0x${x:1} # strip the minus sign and make it hex for use with printf below
  else
     neg[$i]=""
     c[$i]=$x
  fi
done
EAP_TID=$(printf %x:%x:${neg[0]}%x:${neg[1]}%x:${neg[2]}%x ${a[0]} ${a[1]} ${c[0]} ${c[1]} ${c[2]})

完了後、$EAP_TID 変数はこの XID を作成したトランザクションのグローバルトランザクション ID を保持します。トランザクションを開始した Pod のノード識別子は、以下の bash コマンドの出力によって提供されます。

echo "$PG_TID"| base64 -d | tail -c +29
注記

ノード識別子は、PostgreSQL グローバルトラザクション ID フィールドの 29 番目の文字から始まります。

  • Pod が 実行している 場合は、トランザクションが実行中であるため、インダウト状態のブランチをそのままにしておきます。
  • この Pod が実行していない場合は、トランザクションログの関連するトランザクションログストレージを検索する必要があります。ログストレージは、os<node-identifier>jbosststxtable パターンにしたがって命名される JDBC テーブルにあります。

    • このようなテーブルがない場合は、他のトランザクションマネージャーによって所有されているため、ブランチをそのままにしておきます。このテーブルが含まれるデータソースの URL は、以下の transaction サブシステムで定義されます。
    • このようなテーブルがある場合、グローバルトランザクション ID と一致するエントリーを探します。

      • グローバルトランザクション ID と一致するエントリーがテーブルにある場合、以下の説明どおりにデータソースベンダーのツールを使用してインダウト状態のブランチをコミットする必要があります。
      • そのようなエントリーがない場合、ブランチは orphan であり、安全にロールバックすることができます。

以下は、インダウト状態の PostgreSQL ブランチをコミットする方法の例になります。

$ oc rsh postgresql-2-vwf9n
sh-4.2$ psql sampledb
psql (9.5.7)
Type "help" for help.
psql sampledb
commit prepared '131077_AAAAAAAAAAAAAP//rBEAB440GK1aJ72oAAAAGHAtanRh
 ----
LWNyYXNoLXJlYy0zLXAyY2N3_AAAAAAAAAAAAAP//rBEAB440GK1aJ72oAAAAGgAAAAEAAAAA';
重要

この手順をすべてのデータソースおよびインダウト状態のブランチに繰り返します。

8.15.2.3.3. リソースマネージャーに接触できるクラスターで稼働中すべての JBoss EAP インスタンスのノード識別子リストを取得

ノード識別子は、Pod 名と同じ名前になるように設定されます。oc コマンドを使用すると、使用中の Pod 名を取得できます。以下のコマンドを使用して、実行中の Pod をリストします。

$ oc get pods | grep Running
eap-manual-tx-recovery-app-4-26p4r   1/1       Running     0          23m
postgresql-2-vwf9n                   1/1       Running     0          41m

実行中の各 Pod に対し、Pod のログの出力を確認し、ノード名を取得します。たとえば、上記の出力にある最初の Pod の場合、以下のコマンドを使用します。

$ oc logs eap-manual-tx-recovery-app-4-26p4r | grep "jboss.node.name" | head -1
jboss.node.name = tx-recovery-app-4-26p4r
重要

前述の JBoss ノード名識別子 は、常に最大文字数である 23 文字になるよう省略されます。これは、先頭から文字を削除して最大長の 23 文字になるまで末尾の文字を確保します。

8.15.2.3.4. トランザクションログの検索
  1. トランザクションログは JDBC が基盤のオブジェクトストアにあります。このストアの JNDI 名は、JBoss EAP 設定ファイルの transaction サブシステム定義に定義されています。
  2. 設定ファイルを確認し、上記の JNDI 名に対応するデータソース定義を見つけます。
  3. JNDI 名を使用して、接続 URL を取得します。
  4. URL を使用してデータベースに接続し、関係するインダウト状態のトランザクションテーブルで select クエリーを実行します。

    データベースが実行している Pod が分かり、データベースの名前が分かる場合は、Pod に OpenShift リモートシェルを開き、直接データベースツールを使用した方が簡単であることがあります。

    たとえば、JDBC ストアが Pod postgresql-2-vwf9n で実行されている sampledb という PostgreSQL データベースによってホストされる場合、以下のコマンドを使用してトランザクションログを見つけることができます。

    注記

    以下のコマンドの ostxrecoveryapp426p4rjbosststxtable テーブル名は、ログストレージエントリーを保持する JDBC テーブル名のパターン にしたがっているため選択されました。ご使用の環境では、テーブル名は以下と似た形式になります。

    • os 接頭辞で始まります。
    • 中間部分は、上記の JBoss ノード名 から適用されます。 -(ダッシュ) が存在する場合は削除される可能性があります。
    • 最後に jbosststxtable 接頭辞が追加され、テーブルの最終名が作成されます。
    $ oc rsh postgresql-2-vwf9n
    sh-4.2$ psql sampledb
    psql (9.5.7)
    Type "help" for help.
    
    sampledb=# select uidstring from ostxrecoveryapp426p4rjbosststxtable where TYPENAME='StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction'
    ;
                  uidstring
     -------------------------------------
     0:ffff0a81009d:33789827:5a68b2bf:40
     (1 row)
8.15.2.3.5. 調整したインダウト状態のブランチのトランザクションログをクリーンアップ
警告

インダウト状態のブランチが残っていないことを確信できるまで、ログを削除しないでください。

指定のトラザクションのブランチがすべて完了し、A-MQ および JDV を含む潜在的なリソースマネージャーがすべてチェックされた後、トランザクションログを安全に削除できます。

以下のコマンドを実行します。適切な uidstring を使用して削除するトラザクションログを指定します。

DELETE FROM ostxrecoveryapp426p4rjbosststxtable where uidstring = UIDSTRING
重要

ログを削除しない場合、準備後に失敗し、現在は解決された完了済みのトランザクションはトランザクションログストレージから削除されません。この結果、不必要なストレージが使用され、今後の手作業の調整がより困難になります。