PHP・MySQLでトランザクション中にupdateしたレコードを再度、updateで未反映
処理の流れ上、updateしたあと、もう一回updateしたかった。
作成日:2018-04-23, 更新日:2018-04-26
結論(2018/04/24)
FuelPHPでしか確認していないので分からないが・・・トランザクション開始したらCommitするまで同一レコードの修正は・・・可能
経緯
▼下記のようなコトをしていた。
A1.レコード追加(id=連番、filePath=''、他のカラムたち)
A2.「1」の「lastInsertId()」でファイル名や保存PATHを決めたい
A3.「2」でファイル名や保存PATHを「1」の「カラム:filePath」に保存
「A1」はinsert or updateのどちらかが実行される
「A2」はファイルのアップロード
「A3」は「A1」のレコードをupdateする。
※「A1」の前でトランザクション開始、「A3」のあとにCommit。
▼問題の内容と経緯
B1.FuelPHPで「updateしたときの戻り値」は「更新したレコード数」なのに・・・「count()」していた。
B2.FuelPHPのバージョンアップに合わせて、PHPもバージョンアップ(PHP7.2にした)
B3.PHP7.2にバージョンアップしたら「配列じゃない値はcount()できない」ってトコでつまづいた。
B4.そのため「B1」の「updateしたときの戻り値」を「count()」しないように修正
B5.その結果「A3」の「updateしたときの戻り値」が「0(false)」になった
B6.「B5」の結果により例外処理の分岐に入って・・・ロールバックされた
原因は、「A3」で変更内容が無いのにupdateしたこと。
FuelPHPで「updateしたときの戻り値」は「更新したレコード数」ってのは「変更したレコード数」になるらしく「変更していないレコード数」は含まれない。
そのため「A3」で変更内容が無いときの「updateしたときの戻り値」は「0(false)」となる。
更新日時の指定の問題
問題をややこしくしていたのは更新日時の指定。
「A1」と「A3」のそれぞれで更新日時をいれていて、その指定が原因にたどり着く邪魔をしていた。
そういう指定をした私がダメなんだけどね・・・。
C1.「$now=time()」で現在の日時を取得
C2.「A1」で更新日を「C1」の「$now」を設定
C3.「A3」も更新日を「C1」の「$now」を設定
※2回updateを実行しようが、「(1回の処理だから更新日は)一緒でいいや」って感じで、このようにしていた。
「A1」で「update」が実行されたときは、更新日時だけは確実に変わるから「更新したレコード数」がある。
「A3」で「update」が実行されると更新日時も使い回しているから「更新したレコード数」が「0」のときがある。
※仮に都度「time()」を指定しても、ミリ秒じゃないと同一日時になってしまうことがある。
更新箇所を確実に作るためにもレコードのバージョンを設定して更新時に「+1」しておけばいいんだろうね。
※参照:MySQLの「+1」した値をupdateしたい
結論(2018/04/23)
以下は、以前に勘違いしていた内容
※FuelPHPが絡まないと勘違いじゃないかもしれない・・・。とりあえずメモとして残しておく。
トランザクション開始したらCommitするまで同一レコードの修正は・・・ムリ
InsertやUpdateされるとCommitされるまでロックがかかるっぽい。
正確には出来るみたいだけど・・・出来てたんだけど出来ないことが分かった。
経緯
1.レコードの追加(or 修正)したい
2.「1」で保存した値を元にファイル保存したい
3.「2」で保存したファイルPATHを「1」のレコードで修正したい
ってなコトをしたかった。具体的にいうと・・・
1.レコード追加(id=連番、filePath=''、他のカラムたち)
2.「1」の「lastInsertId()」でファイル名や保存PATHを決めたい
3.「2」でファイル名や保存PATHを「1」の「カラム:filePath」に保存
・・・こんな感じ。
流れ的に
1.ファイルをup
2.レコードの追加(or 修正)
が望ましい。
でも、「2.レコードの追加(or 修正)」で失敗したときに「1.ファイルをup」のファイルを削除しなくちゃいけない。
DBだったらトランザクションで戻ってくれるし・・・ってことで下記のような処理にしていた。
1.レコードの追加(or 修正)したい
2.ファイルをupしたい
3.upしたPATHを追加(or 修正)したレコードに対して更新したい
※「1」が成功して「3」が「失敗」ってのはレアケースだから、都度対応にするって感じ。
今まで「1」「3」の処理は下記のようにしていた
$result = DBに保存(※FuelPHPが用意してくれている関数を実行); if ( !count($result) ) { ~ エラー処理 ~ }
※かなり昔に書いた内容を使い回していたので・・・なぜ「count()」にしたのかは記憶にない。
※さらにFuelPHPが用意してくれていたupdate用の関数は成功したレコード数を返してくれる。
このソースで今までエラー(警告)が出なかったけど・・・PHPのバージョンを7.2にしたら警告が出てきた。
→「$result」が配列じゃないから「count()」は使えないっていう内容。
ということで「1」「3」を下記のように修正
$result = DBに保存(※FuelPHPが用意してくれている関数を実行); if ( !$result ) { ~ エラー処理 ~ }
するとエラーに・・・。調べると「1:$result=1」「3:$result=0」となっている。
で、やっと「トランザクション中に同一レコードの修正って出来ない感じ?」と思い、調べてみて気付いた。
でも・・・ロックかかっているなら「count($result)」のときにcommitで更新されているのは何故なんだろうと思ったら「count(0)」は「1」になるっぽい。
対応案
下記の流れで「1」or「2」で一回コミットすればいいんだけど・・・気持ち悪い。
1.レコードの追加(or 修正)
2.「1」で保存した値を元にファイル保存
3.「2」で保存したファイルPATHを「1」のレコードに追加