-
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
Thread safety for #without_versioning #328
Conversation
@dwbutler - Thanks for the PR. I'm trying to wrap my head around this, as I don't deal with multithreaded Ruby/JRuby very often. When you say that slow_thread = Thread.new do
Widget.new.without_versioning do
sleep(0.01)
enabled = PaperTrail.enabled_for_model?(Widget)
sleep(0.01)
end
enabled
end
fast_thread = Thread.new do
sleep(0.005)
enabled = PaperTrail.enabled_for_model?(Widget)
enabled
end Are you suggesting that
So if I'm not mistaken, until that time has expired, and that |
Hi @batter, The problem I'm trying to solve is that I did not attempt to fix Just to clarify, MRI is indeed truly multi-threaded, and anyone who runs a threaded server (such as Puma) will experience the same issue. There is a global interpreter lock, but that will not protect against this problem. |
@dwbutler - I tried switching |
@batter - I also tried class Widget
# Will only hold its value for a single instance
attr_accessor :paper_trail_enabled_for_model
class << self
# Will hold its value for all instances of the class
attr_accessor :paper_trail_enabled_for_model
end
end In regards to thread safety, it's "safe" in the sense that it uses instance variables under the hood and so there shouldn't be any errors. But it could still be "unsafe" in the sense that setting a class level attribute will make the change visible in other threads. The only way to correctly turn paper trail on and off per-thread is to either use instance variables, or thread-local variables. Hope that made sense. |
@dwbutler - Here is the work that I did in an attempt to move away from It looks like your solution may be the preferred one since it actually works properly. I'd still like to maintain the |
@batter - Just took a look at your branch. I can see that you replicated the functionality that The problem with this approach is that all threads "see" the same value for I think your approach of keeping the same API is cleaner, and just needs to be modified to read from the thread-local |
Ahh ok, so basically the only variables you can assume are thread-safe / independent per thread are class variables (indicated by |
This fixes #326 and #307.
The thread-local
paper_trail_store
is now used to track whether Paper Trail is enabled for a particular model. Note that this results in a small API change.Model.paper_trail_enabled_for_model
is now set at runtime and doesn't change. (Changing the value of aclass_attribute
is not thread-safe; see #307).PaperTrail.enabled_for_model?(Model)
is now the preferred way to query if Paper Trail is enabled. If not specified for the current thread, it falls back to queryingModel.paper_trail_enabled_for_model
.I considered trying to leave the API unchanged, but it's very difficult to mimic all the behaviors of
class_attribute
without usingclass_attribute
.I would appreciate any feedback! I'm not sure how "public" the
Model.paper_trail_enabled_for_model
API is.