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
Batch touch parent records #19324
Batch touch parent records #19324
Conversation
this is still missing more tests and CHANGELOG, but I want to validate the idea first. |
@_touch_time = current_time_from_proper_timezone | ||
|
||
@_defer_touch_attrs.each { |attr| write_attribute attr, @_touch_time } | ||
clear_attribute_changes @_defer_touch_attrs |
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.
Would keep #surreptitiously_touch
to encapsulate this bit of trickery. Easier to understand
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.
TIL a new word!
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.
me too =)
One issue about this implementation is that if we have a 3 level |
@jeremy Thanks a lot for your review . ❤️ 💛 💚 |
(travis was not running on this PR, so I closed and opened again) |
Is this good to go? Would be nice to have it committed then. (cc @jeremy) |
[fixes rails#18606] Make belongs_to use touch over touch_later when running the callbacks. Add more tests and small method rename Thanks Jeremy for the feedback.
🤘 |
This breaks great-grandparent touching chains, like: Comment.belongs_to :message, touch: true
Message.belongs_to :project, touch: true
Project.belongs_to :account, touch: true
Account When we do Comment.create!, it'll only trigger a touch on its parent (message) and grandparent (project), not the great-grandparent (account). This is because we're not doing a full save on the Project, so the touch: true callback that Project has against Account is never triggered. I've committed a skipped, but failing test for this in 5f5e6d9. |
thanks @dhh , I will take a look at it. |
Is it rails 5 only? |
@brodock Rails 5 |
The problem was that when saving an object, we would call touch_later on the parent which wont be saved immediteally, and it wont call any callbacks. That was working one level up because we were calling touch, during the touch_later commit phase. However that still didnt solve the problem when you have a 3+ levels of parents to be touched, as calling touch would affect the parent, but it would be too late to run callbacks on its grand-parent. The solution for this, is instead, call touch_later upwards when the first touch_later is called. So we make sure all the timestamps are updated without relying on callbacks. This also removed the hard dependency BelongsTo builder had with the TouchLater module. So we can still have the old behaviour if TouchLater module is not included. [fixes 5f5e6d9] [related #19324]
Is there a way to disable this feature? I'm changing the schema search path during a transaction and when it finishes, the queued touch operations try to access tables, which are no longer available... |
@daniel-rikowski I think that sounds probably like a bug. Do you mind opening a new issue with a script that simulates this situation? |
I don't think it's a bug. A missing feature perhaps... Rails would have to monitor changes to the schema search path (is that even possible?) and when it changes the delayed touch must be performed beforehand. Much too specialized if you ask me. Anyway, I created a runnable script which demonstrates the problem: https://gist.github.com/daniel-rikowski/fea2a87dbab975bd5d72556f0a426e34 (You'll need PostgreSQL and an existing database) If you still think this is a bug I'll gladly open a new issue. |
@dhh just discovered this great feature today, works so well with caching of api compound documents. Thanks for all the work <3 |
I've found the internal `without_transaction_enrollment` callbacks which have not been newly used over five years, when I tried to work reverting rails#9068 (rails#36049 (comment)). I think that we will never make that callbacks public, since the mechanism of `without_transaction_enrollment` is too implementation specific, at least before rails#9068, records in a transaction had enrolled all into the transaction. That callbacks was introduced at rails#18936 to make `touch_later` rails#19324, but I think that the internal callbacks is overkill to just make the `touch_later` only, and invoking the extra callbacks also have a little overhead even if we haven't used that. So I think we can remove the internal callbacks for now, until we will come up with a good use for that callbacks.
Batch the parent touches. So we doing something like
post.comments.each(&:save!)
it will only touch the Post parent once, and only before the transaction is sent to the DB.[fixes #18606]
review @dhh @jeremy
This is heavily inspired from Jeremy's script https://gist.github.com/jeremy/84134ad08f2a137aa1ef . Now that we have the
before_commit
hooks and thetouch(*, time:)