-
Notifications
You must be signed in to change notification settings - Fork 901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added transaction support for restoring associations #44
Conversation
This should solve all the problems with restoring associations.
Changed rollback save to save!, will raise exception when validation fails
Thanks for this. I was away last week on holiday and am now pretty busy catching up, so I hope to look at this properly in a day or two. |
I played with it some more, but having transaction support doesn't solve all of the issues with associations. I put some more thought into this and i think i have a solution. Like i said before, it's very hard to build all the associations in memory, especially when you get multiple levels of associations. It's alot easier by using a restore method, which reverts the object with his has_one/has_many relations to a specific time directly. Shouldn't be too hard implementing this, i will see if i can submit a patch next week. |
I agree with your logic, and also that the problem is harder than it looks ;) I'll be interested to see what you come up with... |
I finally got around to implementing my idea. When restoring the assocations, the transaction code i already added came in handy. I didn't add any tests yet and only tested it with 1-level of associations. |
This looks really promising. I think introducing a new model (VersionAssociation) was the breakthrough we needed. I agree it makes sense to turn off association restoration by default at this stage. This'll need some solid tests... |
@egoh what's the status of your implementation? |
Added delete support (add :autosave=>true to association in model)
Only mark for destruction if child still exists
The querys i used for fetching the first version for every child was wrong. Group by returns arbitrary results when not using group_by aggregate functions on columns. Therefor i switched to a subquery with fetches the minimum version id's for each child. To delete childs from the model that did not exist at the time of the version, add :autosave => true to the association in the model I also turned off association restore by default use has_one and has_many options to turn this on I wanted to write some tests today, but i can't get the tests inside the gem to run. @saimonmoore: i think i just fixed the major bugs. I tested most common cases by hand, but there are so many combinations that it's easy to overlook something. You can try it out and see if it gives expected results, but wouldn't use it in production yet before testing is added. |
To test: Might be worth clearing out the sqlite3 databases in |
Already saw that comment in the readme, but it doesn't run anything. I have the gem installed like this: Do i need to add anything to the general rake file? |
It sounds like you're trying to test paper_trail within your app. I test PaperTrail independently of any app by changing into its root directory and running that command. The |
@egoh what's the status of your proposed patch? Is it stable enough to test with a small application? |
Yes it's stable enough to test in a small application. Still haven't got around to writing tests, the weather here is way too good lately.. |
Haha, the weather's been great here too. You have to make the most of it! |
@augustinV The reason the implementation isn't working with multiple version tables is that the feature didn't exist at the time i made the fork! I already wrapped up some code to make the class_name of the version_association configurable, just like it's implemented for the version table. Once i get to test my changes i'll commit the changes. |
I'm working on an app that uses a homemade temporal-persistence lib.
This requires retrieving historical data through many levels of relations. Question : Am I missing something? |
You can already get the version of an object at a particular time with
The tricky part is restoring the associations as they were at that time. If this issue/pull request's code works out, you'll be able to do that too. It sounds like you've been thinking about how to restore historic associations. Do you have any working code for this? |
I'm afraid not: the design of the lib we use is way too specific. It was written some time ago - not by me - and is not active_record compatible : everything (and I mean EVERYTHING: 70+ models) is stored in 3 tables : "models", "relations", and "properties". Each record in "properties" represent 1 attribute in 1 object => a User has 50 attributes -> 50 records in "properties" to represent this user attributes + 1 record in 'models', to represent this User record. It does the job (the "temporal DB" aspect), but the performance is "not that great", as you could expect from this brief design description. That's why I'm looking for more standard/off-the-shelf solutions. |
I saw a database like that once. The design appeals to theoreticians because it's so general, but to those of us trying to use the wretched thing it's so general it's very hard to work with. |
EgoH, I just checked out your fork and tried to run your CreateVersionAssociations and got
This was running against MySQL 5.1. Obviously, this is easily fixed but I thought I'd mention it. |
EgoH, Further to the db index comment, I've just plugged your version of Paper Trail into my application. It is just awesome. Great job! |
airblade, can this pull request be merged to upstream? I'm looking to use papertrail in my app, and this functionality is a requirement for my case (the ability to restore associations). |
I want to look through the code properly before I merge it. I know it's been outstanding for a long time but I can't look at it right now -- I've just moved house and am swamped with unpacking, admin, etc. Since you need it for your app, I suggest you use the fork until I get myself organised again. |
big +1 for this, and happy to write tests/test it out in my app if would be useful. |
I tried using this in my app, but couldn't quite get it working. Ultimately what I want is to be able to do this: https://gist.github.com/1175753 Is this what this issue is heading towards? Could you post some example code? |
@@ -137,22 +158,52 @@ class Version < ActiveRecord::Base | |||
# The `lookback` sets how many seconds before the model's change we go. | |||
def reify_has_ones(model, lookback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to change lookback to "options"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was running the tests against this pull request and got the error:
NameError: undefined local variable or method `options' for #Version:0x10f99c978, at line 165
So it looks like this method is expecting an options hash to be passed in, and not a loopback. This seems to be confirmed at line 80, where 'options' is passed as the second argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, OK, thanks. I'm running out the door on holiday now so I'll fix this when I get back...
dd4280e
to
a683be8
Compare
Closed by #439, which looks like it was heavily influenced by the work done for this PR.. cheers! |
I extended the gem to add transaction support for restoring associations. It stores a transaction id for every version change done inside of a transaction. (transaction id is version id of first change). It works pretty well for me, but haven't tested it with complex scenerios yet.
You can list other versions in same transaction by
version.transact
When you want to restore model with associations (eg other changes made in the same transaction) do
It's like impossible to put all associations in memory with correct relations. Therefor the rollback function, that undoes the initial transaction in reverse order. The rollback function uses save! to throw an exception on invalid validations. It should however pass validations if you never have failing validations on models in your database. As a last resort you could also skip validations by changing to save(false). But i don't want to remove validation if it doesn't cause any problems
You can also do a virtual transaction by:
PaperTrail.start_transaction
#make changes
PaperTrail.stop_transaction