🙆

【PHP】トランザクションのネストを回避する

2022/07/22に公開

トランザクションのネストについて

調べたところ、RDBの違いによって挙動が異なるため、トランザクションのネストは「好ましく無い」ようです。
https://qiita.com/yuba/items/9b5b86bc3e128a84db5e

自分も同じような場面に差し掛かったので、回避した方法を載せます。

【背景】
funcA()内で既存の関数funcB()をどうしても使いたい。
しかし、funcB()には既にトランザクションが貼ってあるのでfuncA()でトランザクションを貼ってしまうとトランザクションがネストしてしまう。

修正前

public function funcA(){
    $transction = Transaction::begin();
    try{
        User::create();
        funcB();
        $transaction->commit();
    }catch{
        $transaction->rollback();
    }
}

public function funcB(){
    $transction = Transaction::begin();
    try{
        UserHistory::create();
        UserStatus::create();
        $transaction->commit();
    }catch{
        $transaction->rollback();
    }
}

上記の場合、funcB()でもトランザクションを貼っていて、呼び出し元のfuncA()でもトランザクションを貼っています。

修正後

public function funcA(){
    $transction = Transaction::begin();
    $is_user_transaction = false;
    try{
        User::create();
        funcB($is_user_transaction);
        $transaction->commit();
    }catch{
        $transaction->rollback();
    }
}

public function funcB(bool $is_user_transaction = true){
    if($is_user_transaction){
        $transction = Transaction::begin();
    }  
    try{
        UserHistory::create();
        UserStatus::create();
        if($is_user_transaction){
            $transaction->commit();
        }
    }catch{
        if($is_user_transaction){
            $transaction->rollback();
        }
    }
}

このようにしてみました。
端的には、funcA()を実行するときのみfuncB()のトランザクションを外して、funcA()にトランザクションを貼っています。これによりトランザクションのネストを防ぐことができます。

funcB()は、他の箇所でも使われている関数になります。
よって、基本的には$is_user_transaction = trueです。

宣伝

パーソンリンクではエンジニアを募集しています!
https://www.wantedly.com/companies/person-link-co/projects

Discussion