acts_as_list doesn’t work in a typical production deployment. It pretends to for a while, but every application will eventually have issues with it that result in real problems for your users. Here is a short 4 minute long screencast showing you how it breaks, and also a quick fix which will prevent your data from becoming corrupted.
(View it over at Vimeo if embedding doesn’t work for you)
Here is the “quick fix” I apply in the screencast. It’s ugly, but it will work.
1 2 3 4 5 6 7 8
def move_down Tractor.transaction do Tractor.connection.execute("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE") @tractor = Tractor.find(params[:id]) @tractor.move_to_bottom end redirect_to(tractors_path) end
Some things to note when fixing your application in a nicer way:
- This is not MySQL specific, all databases will exhibit this behaviour.
- The isolation level needs to be set as the first statement in the transaction (or globally, but you don’t want serializable globally!)
- For bonus points, add a unique index to the position column, though you’ll have to re-implement most of
acts_as_listto make it work.
- It’s possible to do this under read committed, but it’s pretty complicated and optimised for concurrent access rather than individual performance.
- Obtaining a row lock before moving will fix this specific issue, but won’t address all the edge cases.
_This is a small taste of the type of thing I cover in my DB is your friend training course. July through September I am running full day sessions in the US and UK. Chances are I’m coming to your city. Check it out at http://www.dbisyourfriend.com _