PHP / MySQL に、次のようなコードのセグメントがあります。
foreach item
try
BEGIN
update table 1, throw if failed
update table 2, throw if failed
update table 3, throw if failed
etc. etc. etc.
COMMIT
print "COMMITTING"
catch
ROLLBACK
print "ROLLING BACK"
end try
end foreach
すべての反復から一貫して「COMMITTING」メッセージを受け取るという問題が発生していますが、時々 (ランダムに見える)、常にコミットする最後のものを除いて、更新はコミットされません。
すべてのテーブルは InnoDB です。問題が何であるか知っている人はいますか?
編集: これはコードの簡略化されたバージョンです。うまくいけば、問題を根絶するのに十分でしょうか?
foreach($auctions as $a) {
try {
mysql_query("BEGIN");
$items = fetchAll("SELECT * FROM users_auctions_items WHERE amount > 0 AND auction = {$a['id']}");
$highBid = fetchOne("SELECT * FROM users_auctions_bids WHERE auction = {$a['id']} AND status = 0 LIMIT 1");
if($highBid === false) {
echo "No high bid, returning items to {$a['user']}\n";
$recipientId = $a['user'];
} else {
$recipientId = $highBid['user'];
echo "High bid by {$recipientId}\n";
echo "Giving {$highBid['amount']} gold to {$a['user']}...\n";
mysql_query("UPDATE users SET gold = gold + {$highBid['amount']} WHERE id = {$a['user']}");
if(mysql_affected_rows() != 1) throw new Exception();
}
if($items) {
foreach($items as $i) {
echo "Giving {$recipientId} item #{$i['item']} (x{$i['amount']})...\n";
mysql_query("INSERT INTO users_inventory (user, item, amount)
VALUES ({$recipientId}, {$i['item']}, {$i['amount']})
ON DUPLICATE KEY UPDATE amount = amount + {$i['amount']}");
if(mysql_affected_rows() < 1) throw new Exception();
}
}
mysql_query("UPDATE users_auctions SET status = 2 WHERE id = {$a['id']}");
if(mysql_affected_rows() != 1) throw new Exception();
mysql_query("COMMIT");
echo "Committing\n";
} catch(Exception $e) {
echo "Rolling back\n";
mysql_query("ROLLBACK");
}
}
より具体的には、これは cron によって数分ごとに実行され、すべての未処理のオークションを実行して商品を配布することになっています。その後、オークションを完了するように設定します。ログは、それが完全に実行されており、正しい商品とユーザーを見つけていることを示していますが、アセットを配布せずに完了するようにオークションを更新することがあります。
オークションをリセットして未完了に戻し、cron を再度実行すると、通常は問題なく完了します (2 ~ 3 回リセットする必要がある場合もあります)。