XA Transaction

XA Transaction

XA は分散トランザクションで複数の個別のトランザクションリソースがグローバルトランザクションに参加することを許可する機能です。トランザクションリソースの多くはRDBMS ですが、他の種類のリソースであってもかまいません。

XA STARTXA トランザクションを開始し、それを ACTIVE 状態にします。
XA ENDACTIVE XA トランザクションに対しては、トランザクションを構成する SQL ステートメントを発行したあと、XA END ステートメントを発行します。 XA END は、トランザクションを IDLE 状態にします
XA PREPAREIDLE XA トランザクションに対しては、XA PREPARE ステートメントまたは XA COMMIT … ONE PHASE ステートメントのどちらかを発行可能。

XA PREPARE は、トランザクションを PREPARED 状態にします。 XA RECOVER では PREPARED 状態の XA トランザクションがすべてリストされるため、この時点での XA RECOVER ステートメントの出力にはトランザクションの xid 値が含まれます。
XA COMMIT … ONE PHASE は、トランザクションの準備とコミットを行います。 トランザクションが終了するため、xid 値は XA RECOVER によってリストされません。
XA COMMITPREPARED XA トランザクションに対しては、XA COMMIT ステートメントを発行してトランザクションをコミットおよび終了するか、または XA ROLLBACK を発行してトランザクションをロールバックおよび終了することができます。
3.3.8.2 XA トランザクションの状態

分散トランザクションのいくつかの例:

あるアプリケーションが、メッセージングサービスを RDBMS と組み合わせる統合ツールとして機能する場合があります。 このアプリケーションは、同じくトランザクションデータベースを含む、メッセージの送信、取得、および処理を行うトランザクションがすべて、確実にグローバルトランザクション内で実行されるようにします。 これは、「トランザクション電子メール」と考えることができます。

アプリケーションが、MySQL サーバーや Oracle サーバー (または複数の MySQL サーバー) などの異なるデータベースサーバーに関連するアクションを実行します。ここで、複数のサーバーに関連するアクションは、各サーバーに対してローカルな個別のトランザクションとしてではなく、グローバルトランザクションの一部として実行する必要があります。

銀行が口座情報を RDBMS 内に保持し、現金自動預け払い機 (ATM) を通して現金を出し入れしています。 ATM のアクションが口座に正しく反映されるように保証することが必要ですが、これは RDBMS だけでは実行できません。 グローバルなトランザクションマネージャーが ATM とデータベースリソースを統合して、財務トランザクションの全体的な一貫性を確保します。

13.3.8 XA トランザクション

In Case of MySQL

MySQL8.0.13のリリースノートに以下の様な

XA Transaction Notes:Previously, metadata locks for XA transactions in PREPARED state could be dropped when the client performing the transaction disconnected or the server restarted. This could lead to behavior such as one session being able to drop tables used by an XA transaction in progress for another session. Metadata locks for XA transactions in PREPARED state now are maintained across client disconnects and server restarts, until an XA COMMIT or XA ROLLBACK is executed. (Bug #22710164, Bug #79940)

Changes in MySQL 8.0.13 (2018-10-22, General Availability)

Connection ID 11: XA START → XA END -> QUIT Connection ID 15: drop table t1; (適切にLOCKされる)

mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 8.0.30    |
+-----------+
1 row in set (0.08 sec)

mysql> SELECT CONNECTION_ID();
+-----------------+
| CONNECTION_ID() |
+-----------------+
|              11 |
+-----------------+
1 row in set (0.01 sec)

mysql> create table t1 (a int) engine=innodb;
Query OK, 0 rows affected (0.08 sec)

mysql> insert t1 values (10);
Query OK, 1 row affected (0.02 sec)

mysql> insert t1 values (20);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t1;
+------+
| a    |
+------+
|   10 |
|   20 |
+------+
2 rows in set (0.00 sec)

mysql> xa start 'test';
Query OK, 0 rows affected (0.00 sec)

mysql> insert t1 values (40);
Query OK, 1 row affected (0.00 sec)

mysql> xa end 'test';
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t1;
ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the  IDLE state
mysql> xa prepare 'test';
Query OK, 0 rows affected (0.01 sec)

mysql> quit;
Bye

~/git/rdbms-docker/mysql [18:09:47]>  mysql -h 127.0.0.1 -P 13306 -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 15
Server version: 8.0.30 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.


mysql> use DBXA
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from t1;
+------+
| a    |
+------+
|   10 |
|   20 |
+------+
2 rows in set (0.00 sec)

mysql> SELECT CONNECTION_ID();
+-----------------+
| CONNECTION_ID() |
+-----------------+
|              15 |
+-----------------+
1 row in set (0.00 sec)

mysql>  drop table t1;

Connection ID 14: Check Lock Status (Drop Tableが再接続後も保持されているXAで待たされている。)

XA Transaction and Lock Status
mysql> SELECT CONNECTION_ID();
+-----------------+
| CONNECTION_ID() |
+-----------------+
|              14 |
+-----------------+
1 row in set (0.00 sec)

mysql> select * from t1;
+------+
| a    |
+------+
|   10 |
|   20 |
+------+
2 rows in set (0.00 sec)

mysql> select * from t1;
+------+
| a    |
+------+
|   10 |
|   20 |
+------+
2 rows in set (0.00 sec)

mysql> SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, THREAD_ID, PROCESSLIST_ID, PROCESSLIST_INFO FROM performance_schema.metadata_locks INNER JOIN performance_schema.threads ON THREAD_ID = OWNER_THREAD_ID WHERE PROCESSLIST_ID <> CONNECTION_ID()\G
*************************** 1. row ***************************
     OBJECT_TYPE: SCHEMA
   OBJECT_SCHEMA: DBXA
     OBJECT_NAME: NULL
       LOCK_TYPE: INTENTION_EXCLUSIVE
     LOCK_STATUS: GRANTED
       THREAD_ID: 55
  PROCESSLIST_ID: 15
PROCESSLIST_INFO: drop table t1
*************************** 2. row ***************************
     OBJECT_TYPE: TABLE
   OBJECT_SCHEMA: DBXA
     OBJECT_NAME: t1
       LOCK_TYPE: EXCLUSIVE
     LOCK_STATUS: PENDING
       THREAD_ID: 55
  PROCESSLIST_ID: 15
PROCESSLIST_INFO: drop table t1
*************************** 3. row ***************************
     OBJECT_TYPE: GLOBAL
   OBJECT_SCHEMA: NULL
     OBJECT_NAME: NULL
       LOCK_TYPE: INTENTION_EXCLUSIVE
     LOCK_STATUS: GRANTED
       THREAD_ID: 55
  PROCESSLIST_ID: 15
PROCESSLIST_INFO: drop table t1
*************************** 4. row ***************************
     OBJECT_TYPE: BACKUP LOCK
   OBJECT_SCHEMA: NULL
     OBJECT_NAME: NULL
       LOCK_TYPE: INTENTION_EXCLUSIVE
     LOCK_STATUS: GRANTED
       THREAD_ID: 55
  PROCESSLIST_ID: 15
PROCESSLIST_INFO: drop table t1
4 rows in set (0.00 sec)


mysql> show tables;
+----------------+
| Tables_in_DBXA |
+----------------+
| t1             |
+----------------+
1 row in set (0.00 sec)

mysql>

実行されている、XA TransactionをCommitしたらDrop Tableが正常に処理された。

mysql> xa commit 'test';
Query OK, 0 rows affected (0.01 sec)

mysql>  drop table t1;
Query OK, 0 rows affected (0.03 sec)

mysql> show tables;
Empty set (0.00 sec)

mysql> xa recover;
Empty set (0.00 sec)

mysql> show tables;
Empty set (0.00 sec)

mysql>

MySQL5.7.18:XA TransactionがCommitされていない状態で、他の接続からテーブルがDROP出来てしまっていた。Bug #79940

mysql> CREATE DATABASE `DBXA` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;
Query OK, 1 row affected (0.02 sec)

mysql> select @@version;
+-----------+
| @@version |
+-----------+
| 5.7.18    |
+-----------+
1 row in set (0.00 sec)

mysql> use DBXA
Database changed
mysql> create table t1 (a int) engine=innodb;
Query OK, 0 rows affected (0.06 sec)

mysql> insert t1 values (10);
Query OK, 1 row affected (0.03 sec)

mysql> insert t1 values (20);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t1;
+------+
| a    |
+------+
|   10 |
|   20 |
+------+
2 rows in set (0.00 sec)

mysql> xa start 'test';
Query OK, 0 rows affected (0.00 sec)

mysql> insert t1 values (40);
Query OK, 1 row affected (0.00 sec)

mysql> xa end 'test';
Query OK, 0 rows affected (0.01 sec)

mysql> select * from t1;
ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the  IDLE state
mysql> xa prepare 'test';
Query OK, 0 rows affected (0.00 sec)

mysql> quit;
Bye


~/win/tmp/rdbms-docker/mysql [18:27:19]>  mysql -h 127.0.0.1 -P 23306 -u root -p --ssl-mode=DISABLED
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.18 MySQL Community Server (GPL)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use DBXA
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from t1;
+------+
| a    |
+------+
|   10 |
|   20 |
+------+
2 rows in set (0.00 sec)

mysql> drop table t1;
Query OK, 0 rows affected (0.04 sec)

mysql> show tables;
Empty set (0.01 sec)

mysql> xa recover;
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
|        1 |            4 |            0 | test |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)

mysql> show tables;
Empty set (0.01 sec)

mysql> xa commit 'test';
Query OK, 0 rows affected (0.01 sec)

mysql> show tables;
Empty set (0.01 sec)

mysql> xa recover;
Empty set (0.00 sec)

mysql> show tables;
Empty set (0.00 sec)

mysql>

In Case of PostgreSQL

max_prepared_transactions

PostgreSQLでは、MySQLと若干コマンドが異なるが挙動は同じ。但し、Defaultではmax_prepared_transactionsがOFFになっているので利用出来ないので設定変更が必要。

POC=# select version();
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------
version | PostgreSQL 13.5 (Debian 13.5-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit

POC=# select name,setting,unit,context,category,short_desc from pg_settings where name like '%max_prepared_transactions%';
-[ RECORD 1 ]----------------------------------------------------------------
name       | max_prepared_transactions
setting    | 0
unit       |
context    | postmaster
category   | Resource Usage / Memory
short_desc | Sets the maximum number of simultaneously prepared transactions.

POC=#

Enable Prepare Transaction in PostgreSQL

Default設定では、max_prepared_transactionsが0に設定されていて、PREPAREは利用出来ない様になっている。postgresql.confにも上記の様に注意書きがコメントされている。ALTER SYSTEMコマンドにて起動パラメーターを変更して、再度PostgreSQLを起動して設定を反映させてみる。

postgres=# select name,setting,unit,context,category,short_desc from pg_settings where name like '%max_prepared_transactions%';
-[ RECORD 1 ]----------------------------------------------------------------
name       | max_prepared_transactions
setting    | 0
unit       |
context    | postmaster
category   | Resource Usage / Memory
short_desc | Sets the maximum number of simultaneously prepared transactions.

postgres=# alter system set max_prepared_transactions = 1;
ALTER SYSTEM
postgres=# \q

~/git/rdbms-docker/postgresql/docker/postgresql [ 8:11:22]> docker-compose stop
Stopping postgresql_postgres_1 ... done
~/git/rdbms-docker/postgresql/docker/postgresql [ 8:11:52]> docker-compose start
Starting postgres ... done
~/git/rdbms-docker/postgresql/docker/postgresql [ 8:11:57]> psql -h 127.0.0.1 -p 5432  -U postgres
Password for user postgres:
psql (14.1 (Ubuntu 14.1-1.pgdg18.04+1), server 13.5 (Debian 13.5-1.pgdg110+1))
Type "help" for help.

postgres=# \x
Expanded display is on.
postgres=# select name,setting,unit,context,category,short_desc from pg_settings where name like '%max_prepared_transactions%';
-[ RECORD 1 ]----------------------------------------------------------------
name       | max_prepared_transactions
setting    | 1
unit       |
context    | postmaster
category   | Resource Usage / Memory
short_desc | Sets the maximum number of simultaneously prepared transactions.

postgres=#

テーブルを作成して、データを投入後にTransactionをPREPAREに設定してDBから切断してみる。その後、再度接続しなおして、PREPARE状態にあるテーブルをDROPしてみるとコミットされていないのでロックがかかる。これはMySQL8.0.13以降の挙動と同じ。

psql (14.1 (Ubuntu 14.1-1.pgdg18.04+1), server 13.5 (Debian 13.5-1.pgdg110+1))
You are now connected to database "POC" as user "postgres".
POC=# \x
Expanded display is off.
POC=#  create table t_prepare (a int);
CREATE TABLE
POC=#  insert t_prepare values (10);
ERROR:  syntax error at or near "t_prepare"
LINE 1: insert t_prepare values (10);
               ^
POC=#  insert into t_prepare values (10);
INSERT 0 1
POC=#  insert into t_prepare values (20);
INSERT 0 1
POC=# select * from t_prepare;
 a
----
 10
 20
(2 rows)



POC=# BEGIN;
BEGIN
POC=*# insert into t_prepare values (30);
INSERT 0 1
POC=*# insert into t_prepare values (40);
INSERT 0 1
POC=*# PREPARE TRANSACTION 'test';
PREPARE TRANSACTION
POC=# select * from t_prepare;
 a
----
 10
 20
(2 rows)



POC=# \q
~/git/rdbms-docker/postgresql/docker/postgresql [ 8:21:49]> psql -h 127.0.0.1 -p 5432  -U postgres
Password for user postgres:
psql (14.1 (Ubuntu 14.1-1.pgdg18.04+1), server 13.5 (Debian 13.5-1.pgdg110+1))
Type "help" for help.

postgres=# \c POC
psql (14.1 (Ubuntu 14.1-1.pgdg18.04+1), server 13.5 (Debian 13.5-1.pgdg110+1))
You are now connected to database "POC" as user "postgres".
POC=# select * from t_prepare;
 a
----
 10
 20
(2 rows)

POC=# drop table t_prepare;



ロックを別のセッションから確認してみると、X Lockがかかっている事が確認出来る。

~/git/rdbms-docker/postgresql [ 8:22:43]> psql -h 127.0.0.1 -p 5432  -U postgres
Password for user postgres:
psql (14.1 (Ubuntu 14.1-1.pgdg18.04+1), server 13.5 (Debian 13.5-1.pgdg110+1))
Type "help" for help.


POC=# select lock.locktype, class.relname, lock.pid, lock.mode, act.query
from pg_locks lock
  left outer join pg_stat_activity act on lock.pid = act.pid
  left outer join pg_class class on lock.relation = class.oid where not lock.granted
order by lock.pid;
 locktype |  relname  | pid |        mode         |         query
----------+-----------+-----+---------------------+-----------------------
 relation | t_prepare |  69 | AccessExclusiveLock | drop table t_prepare;
(1 row)

POC=#
AccessExclusiveLock

上記のロックを確認したセッションにてPREPAREをCOMMITすると、COMMITされLOCKは解除される。

POC=# COMMIT PREPARED 'test';
COMMIT PREPARED
POC=# select lock.locktype, class.relname, lock.pid, lock.mode, act.query
from pg_locks lock
  left outer join pg_stat_activity act on lock.pid = act.pid
  left outer join pg_class class on lock.relation = class.oid where not lock.granted
order by lock.pid;
 locktype | relname | pid | mode | query
----------+---------+-----+------+-------
(0 rows)

POC=#

LOCKされていたDROPテーブルは実行されてオブジェクトが無くなっている。

POC=# drop table t_prepare;
DROP TABLE
POC=# select * from t_prepare;
ERROR:  relation "t_prepare" does not exist
LINE 1: select * from t_prepare;
                      ^
POC=#

Prepareの状態でトランザクションを残しておくと、VACUUM等に影響が出るので注意との事。

It is unwise to leave transactions in the prepared state for a long time. This will interfere with the ability of VACUUM to reclaim storage, and in extreme cases could cause the database to shut down to prevent transaction ID wraparound (see Section 24.1.5). Keep in mind also that the transaction continues to hold whatever locks it held. The intended usage of the feature is that a prepared transaction will normally be committed or rolled back as soon as an external transaction manager has verified that other databases are also prepared to commit. If you have not set up an external transaction manager to track prepared transactions and ensure they get closed out promptly, it is best to keep the prepared-transaction feature disabled by setting max_prepared_transactions to zero. This will prevent accidental creation of prepared transactions that might then be forgotten and eventually cause problems.

PREPARE TRANSACTION

設定 (max_prepared_transactions)を元に戻して確認を終了。

POC=# select name,setting,unit,context,category,short_desc from pg_settings where name like '%max_prepared_transactions%';
-[ RECORD 1 ]----------------------------------------------------------------
name       | max_prepared_transactions
setting    | 1
unit       |
context    | postmaster
category   | Resource Usage / Memory
short_desc | Sets the maximum number of simultaneously prepared transactions.

POC=# ALTER SYSTEM RESET max_prepared_transactions;
ALTER SYSTEM
POC=# \q
~/git/rdbms-docker/postgresql/docker/postgresql [ 8:29:39]> docker-compose stop
Stopping postgresql_postgres_1 ... done
~/git/rdbms-docker/postgresql/docker/postgresql [ 8:29:48]> docker-compose start
Starting postgres ... done
~/git/rdbms-docker/postgresql/docker/postgresql [ 8:29:52]> psql -h 127.0.0.1 -p 5432  -U postgres
Password for user postgres:
psql (14.1 (Ubuntu 14.1-1.pgdg18.04+1), server 13.5 (Debian 13.5-1.pgdg110+1))
Type "help" for help.

postgres=# \x
Expanded display is on.
postgres=# select name,setting,unit,context,category,short_desc from pg_settings where name like '%max_prepared_transactions%';
-[ RECORD 1 ]----------------------------------------------------------------
name       | max_prepared_transactions
setting    | 0
unit       |
context    | postmaster
category   | Resource Usage / Memory
short_desc | Sets the maximum number of simultaneously prepared transactions.

postgres=#

カテゴリー:

最近のコメント

表示できるコメントはありません。