別サーバーにまたがる複数の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();
}
コメント