別サーバーにまたがる複数のDBへのTransactrionを管理するには、2 Phase Commitが用いられることがあります。
今回のコードは、できるだけ流れがわかるように可能な限り単純にしてあります。エラー処理などは厳密に実施していませんので注意してください。
その代わり1コマンドごとにログを書くようにして、エラーが起きたときにどこで落ちたか、可能な限り終えるようにしてあります。
このコードをProductionで使う場合(あまり推奨しません。。。)は、十分に検証してから使ってください。
XA Transactionのコマンドの流れ
server1とserver2の2つのDBサーバーに対してXA transactionコマンドを実行する流れは、下記のようになります。
// XA trsanction start server1: XA start 'test' server2: XA start 'test' //-------------------------------- // server1とserver2のDBのデータ更新 //-------------------------------- // XA trsanction commit server1: XA END 'test' server2: XA END 'test' server1: XA PREPARE 'test' server2: XA PREPARE 'test' server1: XA COMMIT 'test' server2: XA COMMIT 'test' // 更新中にエラーが起きた場合のrollback server1: XA END 'test' server2: XA END 'test' server1: XA PREPARE 'test' server2: XA PREPARE 'test' server1: XA ROLLBACK 'test' server2: XA ROLLBACK 'test'
PDOとMySQLのXA Trsansactionコマンドを使ったXA Transactionの実装例
XA TransactionをPDO使って実装してみました。$gtxidはプログラム側から渡すので、実運用上は$gtxidの管理もする必要あります。
<?php class XATrasnaction { private $connections; private $gtxid; public function __construct(array $connections, $gtxid) { $this->connections = $connections; $this->gtxid = $gtxid; } function begin() { $this->log("== Start 'Begin' for ".$this->gtxid." =="); $this->executeOnAllConnections("XA START '$this->gtxid'"); $this->log("== Finish 'Begin' for ".$this->gtxid." =="); } function commit() { $this->log("== Start 'Commit' for ".$this->gtxid." =="); $this->executeOnAllConnections("XA END '$this->gtxid'"); $this->executeOnAllConnections("XA PREPARE '$this->gtxid'"); $this->executeOnAllConnections("XA COMMIT '$this->gtxid'"); $this->log("== Finish 'Commit' for ".$this->gtxid." =="); } function rollBack() { try { $this->log("== Start 'RollBack' for ".$this->gtxid." =="); $this->executeOnAllConnections("XA END '$this->gtxid'"); $this->executeOnAllConnections("XA PREPARE '$this->gtxid'"); $this->executeOnAllConnections("XA ROLLBACK '$this->gtxid'"); $this->log("== Finish 'RollBack' for ".$this->gtxid." =="); } catch (Exception $e) { $this->log("XATrasaction: Exception was caught during rollback: ".$e->__toString()); } } function executeOnAllConnections($statement) { /* @var $conn \PDO */ foreach($this->connections as $key => $conn) { $this->log("start: $key: $statement"); $conn->exec($statement); $this->log("end : $key: $statement"); } } private function log($msg) { // add logging here } }
XATrasnactionクラスの利用例です。
<?php $conn1 = new \PDO('server1', ...); $conn2 = new \PDO('server2', ...); $conns = ['server1' => $conn1, 'server2' => $conn2]; $transaction = new \XATransaction($conns, uniqueid()); // gtxidの生成もPHP側で管理する必要がありますので、Productionコードでは注意が必要です。 try{ $transaction->begin(); $conn1->exec('UPDATE table_a SET name = "name" WHERE id = 8); $conn2->exec('UPDATE table_b SET name = "name" WHERE id = 8); $transaction->commit(); } catch(\Exception $e) { $transaction->rollBack(); }
コメント