Skip to content
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

Merged
merged 1 commit into from Apr 8, 2015
Merged

Batch touch parent records #19324

merged 1 commit into from Apr 8, 2015

Conversation

arthurnn
Copy link
Member

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 the touch(*, time:)

@arthurnn
Copy link
Member Author

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
Copy link
Member

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL a new word!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

me too =)

@arthurnn
Copy link
Member Author

One issue about this implementation is that if we have a 3 level belongs_to, once we touch the grand-child , that will touch_later the child, which will not touch the parent, until the transaction is about to be committed. I am not sure how to fix that, @jeremy any ideas?

@arthurnn
Copy link
Member Author

@jeremy Thanks a lot for your review . ❤️ 💛 💚
I did the changed you suggested, also added tests for the touch_later module.
Let me know what else you think I am missing..

@arthurnn arthurnn closed this Mar 30, 2015
@arthurnn arthurnn reopened this Mar 30, 2015
@arthurnn
Copy link
Member Author

(travis was not running on this PR, so I closed and opened again)

@dhh
Copy link
Member

dhh commented Apr 8, 2015

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.
arthurnn pushed a commit that referenced this pull request Apr 8, 2015
@arthurnn arthurnn merged commit 991875f into rails:master Apr 8, 2015
@arthurnn arthurnn deleted the batch_touch branch April 8, 2015 17:32
@dhh
Copy link
Member

dhh commented Apr 8, 2015

🤘

@dhh
Copy link
Member

dhh commented Jun 25, 2015

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.

@arthurnn
Copy link
Member Author

thanks @dhh , I will take a look at it.

@brodock
Copy link

brodock commented Oct 27, 2015

Is it rails 5 only?

@kaspth
Copy link
Contributor

kaspth commented Oct 27, 2015

@brodock Rails 5

arthurnn added a commit that referenced this pull request Dec 6, 2015
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]
arthurnn added a commit that referenced this pull request Mar 7, 2016
@daniel-rikowski
Copy link

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...

@arthurnn
Copy link
Member Author

@daniel-rikowski I think that sounds probably like a bug. Do you mind opening a new issue with a script that simulates this situation?
thanks

@daniel-rikowski
Copy link

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.

@pirtlj
Copy link

pirtlj commented Jul 19, 2019

@dhh just discovered this great feature today, works so well with caching of api compound documents. Thanks for all the work <3

kamipo added a commit to kamipo/rails that referenced this pull request May 1, 2020
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Limit touching to once per transaction
9 participants