paper-trail-gem / paper_trail

Track changes to your rails models

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Compatibility problems with optimistic locking / lock_version

saizai opened this issue · comments

commented

Suppose you have a Widget with a lock_version column.

If you try to do widget.previous_version.save!, you'll get a ActiveRecord::StaleObjectError.

I'm not sure how to override this. Ideally what should happen is that the newly reified object should have the same staleness / lock properties as the original object, so that if widget is OK to save then widget.previous_version should be too (since it hasn't changed out from under you).

Any ideas for how to implement this?

@saizai - I know this is a little late, but is there any chance you could provide a failing test to work with?

I have the same issue just now, has anyone figured this out yet? Keep throwing

ActiveRecord::StaleObjectError

whenever I try to undo something with Papertrail. Would be nice to have both functionalities on the same model.

To quote the rails docs for ActiveRecord::StaleObjectError:

Raised on attempt to save stale record. Record is stale when it’s being saved in another query after instantiation, for example, when two users edit the same wiki page and one starts editing and saves the page before the other.

Read more about optimistic locking in ActiveRecord::Locking module RDoc.

When using optimistic locking a lock_version integer is saved in the database, if an update is attempted and the lock version is a lower number that the current lock version you will get the StaleObjectError. The problem with using optimistic locking with paper_trail is that when you go back to a previous version it tries to save with the old lock version, which causes the StaleObjectError. If you don't have paper_trail save the lock_version, then it sets it to nil when you try and reify the previous version, which gets interpreted as 0 which will cause the StaleObjectError. The only workaround I have found to to change the lock_version attribute after reifying to the current lock_version and then saving the object.

@justinlyman - Good to know. I don't know if it makes sense to hard code that into PaperTrail since it is technically monkey hacking around the way that db locking is supposed to work, right? If you think it does make sense, feel free to make a pull request.

using the Rails conventional lock_version, I did:

model.lock_version += 1 if model.respond_to? :lock_version

in version_concern.rb, line 125 just before returning 'model'

The only workaround I have found to to change the lock_version attribute after reifying to the current lock_version and then saving the object. -Justin

I'm not sure I'd describe that as a workaround. I haven't used optimistic locking personally, but based on the documentation it sounds like you're using it as intended; you want to update a record, so you must increment the lock_version.

I'm adding a note to the readme about this. (#550)

I did: model.lock_version += 1 if model.respond_to? :lock_version in version_concern.rb, line 125 just before returning 'model' -Walther

I think this would work in a single-threaded app, but without concurrency control would not be sufficient in a multi-threaded app.

I don't know if it makes sense to .. code that into PaperTrail .. -Ben

I agree. If PaperTrail ever adds a high-level undo function, this is something we'll have to keep in mind.