after_destroyは削除回数と実行回数が一致する訳ではない
例えば投稿Aに対するFavの数を表示しようと思った際に、都度SQLでCOUNT
を実行するのは避けたいという時に、Fav数をレコードに書き込む実装をとあるシステムでやっていました。
具体的にはcounter_cultureを使っていました。
しかし、カウンターが壊れる(過剰に減算される)ことが頻発していました。
この記事は、それを調査した際の備忘録です。
TL; DR
after_destroy
はdestroy!
が失敗しなかったら実行されるafter_comomit on: :destroy
はレコードの削除に成功した際に実行される- ただし、
Rails >= 5.1.0
の挙動
- ただし、
counter_cultureが使っているのはCOALESCE
まずtransaction
を使っていなかったので、それによって同時に+1
した値を書き込んで、本来合計+2
されるべきところが+1
しかされなかったというケースを疑いました。
ただ、counter_cultureはCOALESCEを使っているので、それはなさそうです。
before_destroy vs after_destroy vs after_commit
次に減算に偏っていたので、-1
のトリガーを疑いました。
counter_cultureはbefore_destroyで判定を行なっていたので、これは駄目そうです。
そこでafter_destroy
にする方針を考えましたがこれもうまくいきません。
after_destroy
はdestroy!
によってレコードが削除されなくても(つまり、既に削除されている)実行されますが、after_comomit on: :destroy
はレコードを削除した際にしか呼ばれないという違いがあるようです。
ちなみにこれはRails >= 5.1.0
の挙動であり、それ以前はafter_commit on: :destory
もafter_destroy
と同じ挙動をするようでした。
まとめ
after_destroy
ではなくafter_comomit on: :destroy
を使うようにしましょう。
ちなみに、counter_cultureの修正PRはこちら
記事が気に入ったらチップを送ることができます!
You can give me a cup of coffee :)
Kyash ID: soranoba
Amazon: Wish List
GitHub Sponsor: github.com/sponsors/soranoba
PayPal.Me: paypal.me/soranoba
(Updated: )