Closed film42 closed 7 years ago
r? @matthewd mind to review this?
Just dropping a "ping" comment. Thanks 😄 ❤️ 💟 😘 😚 !!
Just dropping another ping on this. Thanks! ❤️ 💟 🍍 🏓 🤗 🥇 💯
@matthewd Just wondering if you're going to have time to review this.
:heart: :green_heart: :blue_heart: :yellow_heart: :purple_heart:
This is an alternative to https://github.com/rails/arel/pull/465
We experienced this under jruby in production, and it looks like we're not alone:
rails/rails#26571 jruby/activerecord-jdbc-adapter#739
This change doesn't change behavior, but it does fix the following problem:
dispatch[object.class]
and aNoMethodError
is raised.respond_to?(dispatch[object.class])
and returnsfalse
.dispatch[object.class]
and aNoMethodError
is raised.dispatch[object.class] = superklass
.respond_to?(dispatch[object.class])
and returnstrue
, because the dispatch cache has changed, and an error is raised 😱 .This PR makes each visit method store the
dispatch[object.class]
into adispatch_method
variable to prevent either thread from being impacted by thedispatch
cached change.So what happens when two threads hit the visit method now? With this change, both threads will walk through the ancestors, find a matching super class, and write it to the dispatch class. When either retries, they'll get the correct info. In other words, they'll both end up updating the
dispatch
cache with the same value. This is much better than an error being raised for one of them.TESTING:
I was able to reproduce this by adding some random sleeps into the visitor to path like @abrandoned did. Wherever you see
HERE
, it means aNoMethodError
was raised and we're about to checkrespond_to?
. The test starts 20 threads and each thread runs the dispatch contamination test 10 times.Before:
After:
EDIT: Forgot to link to the test gist: https://gist.github.com/film42/6e43ce2f27d2f91ee4daf011a20e1c0d