15
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

グレンジAdvent Calendar 2018

Day 5

Cloud SpannerのトランザクションをPHPで試してみた

Last updated at Posted at 2018-12-04

グレンジ Advent Calendar 2018 5日目の記事を担当の jinjin1 です。
グレンジでサーバサイドエンジニアをしています。

今回は、「Cloud Spanner」のトランザクションをPHPを使って簡単に確認していきたいと思います。

公式ドキュメントの「トランザクション」、「PHP で Cloud Spanner を使ってみる」を参考にしています。

インストール

composerでSpannerのPHPクライアントライブラリを入れます。

composer require google/cloud-spanner

gRPC拡張も必要です。

pecl install grpc
pecl install protobuf

トランザクション

Database::runTransactionを使って、読み書きトランザクションを実行します。
トランザクションがアボートされた場合、成功するまで関数の実行を繰り返すようになっています。

$spanner = new SpannerClient();
$database = $spanner->connect($instanceId, $databaseId);

$database->runTransaction(function (Transaction $transaction) {

    // トランザクション処理

    $transaction->commit();
});

同時に、同一レコードへの更新を行う

以下のコードを同時に実行してみます。

a-1.php
$database->runTransaction(function (Transaction $transaction) {
    echo "start\n";
    
    sleep(1);
    
    echo "select\n";
    $test = $transaction->execute('SELECT count FROM test WHERE id = 1')->rows()->current();
    
    sleep(2);
    
    echo "update\n";
    $transaction->executeUpdate('UPDATE test SET count = 1 WHERE id = 1');
    
    echo "commit\n";
    $transaction->commit();
});
a-2.php
$database->runTransaction(function (Transaction $transaction) {
    echo "start\n";
    
    sleep(2);
    
    echo "select\n";
    $test = $transaction->execute('SELECT count FROM test WHERE id = 1')->rows()->current();
    
    echo "update\n";
    $transaction->executeUpdate('UPDATE test SET count = 1 WHERE id = 1');
    
    echo "commit\n";
    $transaction->commit();
});

sleepを入れて、1つ目のトランザクションが最初にSELECTをした後、
2つ目のトランザクションが先にSELECT、UPDATEを行うようにしました。

$ php a-1.php
start
select
update
commit
$ php a-2.php
start
select
update
commit
start
select
update
commit

2つ目のトランザクションがcommit後アボートされ、処理が最初から再実行されています。

同時に、同一レコードの挿入を行う

b-1.php
$database->runTransaction(function (Transaction $transaction) {
    echo "start\n";
    
    sleep(1);
    
    echo "select\n";
    $test = $transaction->execute('SELECT count FROM test WHERE id = 2')->rows()->current();
    
    sleep(2);
    
    if (is_null($test)) {
        echo "insert\n";
        $transaction->executeUpdate('INSERT INTO test (id, count) VALUES (2, 1)');
    }
    
    echo "commit\n";
    $transaction->commit();
});
b-2.php
$database->runTransaction(function (Transaction $transaction) {
    echo "start\n";
    
    sleep(2);
    
    echo "select\n";
    $test = $transaction->execute('SELECT count FROM test WHERE id = 2')->rows()->current();
    
    if (is_null($test)) {
        echo "insert\n";
        $transaction->executeUpdate('INSERT INTO test (id, count) VALUES (2, 1)');
    }
    
    echo "commit\n";
    $transaction->commit();
});

sleepを入れて、1つ目のトランザクションが最初にSELECTをした後、
2つ目のトランザクションが先にSELECT、INSERTを行うようにしました。

$ php b-1.php
start
select
insert
commit
$ php b-2.php
start
select
insert
commit
start
select
commit

こちらも、2つ目のトランザクションがcommit後アボートされ、処理が最初から再実行されています。

同時に、同一レコードの更新(加算)を行う

c-1.php
$database->runTransaction(function (Transaction $transaction) {
    echo "start\n";
    
    sleep(1);
    
    echo "update\n";
    $transaction->executeUpdate('UPDATE test SET count = count + 1 WHERE id = 1');
    
    sleep(2);
    
    echo "commit\n";
    $transaction->commit();
});
c-2.php
$database->runTransaction(function (Transaction $transaction) {
    echo "start\n";
    
    sleep(2);
    
    echo "update\n";
    $transaction->executeUpdate('UPDATE test SET count = count + 1 WHERE id = 1');
    
    echo "commit\n";
    $transaction->commit();
});

こちらはSELECTを行わずに、1つ目のトランザクションが最初にUPDATE(加算の式)を行った後に、
2つ目のトランザクションがUPDATEを行うようにしました。

$ php c-1.php
start
update
commit
$ php c-2.php
start
update
commit
start
update
commit

こちらも、2つ目のトランザクションがcommit後アボートされ、処理が最初から再実行されています。

まとめ

Cloud Spannerでは、MySQLのような「FOR UPDATE」はなく、
Database::runTransactionに渡す関数内で読み取り、書き込みのトランザクション処理を書きます。
トランザクションの同時実行で、一方のトランザクションがアボートされ、トランザクション処理が再実行される挙動を確認することができました。

15
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?