現象
Laravelを利用した環境で通常のケースではTransactionも機能しており問題なかったが、RefreshDatabaseを用いたPHPUnitでの実行ではエラーが発生していた。
Doctrine\DBAL\Driver\PDOException : SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist
/src/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:43
/src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:260
/src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:238
/src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/RefreshDatabase.php:90
/src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:233
/src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:149
Caused by
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist
/src/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:41
/src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:260
/src/vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php:238
/src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/RefreshDatabase.php:90
/src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:233
/src/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestCase.php:149
環境
全てdockerコンテナ内
- mysql: 5.7
- Laravel: 6.18.34
- PHPUnit: 9.3.0
先に結論
- 今回の場合はテストの中(正確にはseeder)でテーブルに対してtruncateを発行していたのが問題になっていた。
- これはMySQLの仕様によるもので暗黙的なコミットが発生してしまうため。
調査方法
- MySQLのクエリログを有効にする
[mysqld]
general_log=ON
general_log_file=general_query.log
- テストを実行する
-
/var/lib/mysql/general_query.log
を確認 - QueryとExecuteだけ抜き出し
今回はこの時点でSTART TRANSACTION後にテーブルのtruncateを行っていた事に気づけたが一応MySQLコンソールにクエリ流し込んでエラーになることを確認したりもした。
あとがき
LaravelのRefreshDatabaseでRollback出来ない、Transactionがおかしいみたいな内容はググると結構出てくるが、結局きちんと解決しているものが少ないように思えたので残してみることにした。
はじめはLaravel側でクエリログを出力していたが、SAVEPOINT周りなんかはTransactionRolledBack
、 TransactionCommitted
、TransactionBeginning
で追いきれなかったのでMySQL側でログ出力したほうが確実だったなと後から反省。