Laravel確保資料庫正確:transaction與lock

最近在開發上碰到一個request要執行的任務比較多、後面的任務牽扯到第3方API因此有可能fail,此時前面執行的任務需要將資料回溯的問題。

另外還碰到race condition的資料庫資料同步問題:當兩個使用者同時按下某個按鈕時,資料庫的資料會出現問題。

前者需要利用資料庫的transaction功能確保任務全執行或是全不執行、後者需要用到lock/unlock將某些資料表鎖住不讓其他程序修改。

在Laravel中,該怎麼做呢?查了一下,大概整理出來。

Transaction

有兩種作法,第1種使用closure:

DB::transaction(function()
 {
      // do something
 });

第2種也許比較親切,

try {
    DB::connection()->getPdo()->beginTransaction();
    // database queries here
    DB::connection()->getPdo()->commit();
} catch (\PDOException $e) {
    // Woopsy
    DB::connection()->getPdo()->rollBack();
}

在Laravel facades的協助下,這兩種作法都滿簡潔有力的。
要注意的是,在第1種情況下,若是closure要使用到執行closure之前定義的變數,記得使用關鍵字「use」:

DB::transaction(function() use ($user_id)
{
    // do something with $user_id
});

Lock

要保證一次只有一個程序在修改某幾張資料表,以Mysql來說有LOCK跟UNLOCK可以用。Laravel並沒有直接支援此功能,不過這樣變通就可以了:

DB::connection()->getPdo()->exec( 'LOCK TABLES items WRITE, accounts WRITE, orders WRITE' );
// blah blah
DB::connection()->getPdo()->exec( 'UNLOCK TABLES' );

這邊一次用WRTIE LOCK鎖住三張資料表。當然也有READ LOCK可以使用。