-
Notifications
You must be signed in to change notification settings - Fork 375
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
fix: Correctly set mutex #2757
fix: Correctly set mutex #2757
Conversation
lib/datadog/core/workers/async.rb
Outdated
@@ -17,9 +17,24 @@ def self.included(base) | |||
|
|||
# Methods that must be prepended | |||
module PrependedMethods | |||
def initialize(*args, **kwargs, &block) |
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.
Is there a reason this needs to be done in the constructor?
Generally speaking its not great to prepend onto initialize
, because not all objects have the same constructor signature. It introduces a kind of unspoken coupling (expects implementing objects to have a very specific constructor) and makes the module inflexible.
I suppose if you're doing a wildcard super
, I guess it technically does work with any constructor. Just seems a bit weird. Then again, maybe its the original design is what's weird... I probably would have to take credit for that 😅
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.
Attempting to answer my own question, I'm going to guess this has something to do with lazy initialization not being sufficient if that field is accessed concurrently prior to initialization...
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.
FWIW the breakage I'm seeing in CI right now is related to the constructor definition:
68.2) Failure/Error:
def initialize(&block)
@task = block
end
ArgumentError:
wrong number of arguments (given 1, expected 0)
# ./lib/datadog/core/worker.rb:8:in `initialize'
But it's only on Ruby 2.1-2.4. Probably the kwargs
, so we may need to introduce some compatibility checks there.
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.
Attempting to answer my own question, I'm going to guess this has something to do with lazy initialization not being sufficient if that field is accessed concurrently prior to initialization...
Yeah, there's a chance that one thread will use one mutex instance, and then another will use another one. If we can move mutex into a constant — that would solve the issue entirely :D but can cause an issue with performance (haven't looked into details how those classes are used).
@delner I have an alternative idea how to ensure all threads are using correct mutex, not sure how will you like the idea though: module Async
GLOBAL_MUTEX = Mutex.new
def mutex
@mutex || GLOBAL_MUTEX.synchronize { @mutex ||= Mutex.new }
end
end That will definitely work on all Ruby versions and will avoid modifying initializer :D Pushed the idea as a commit :D |
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.
Hey @ixti 👋
I spoke with @delner and we're very happy to accept this version -- it's a great solution and contribution.
Indeed creating a mutex is so fast and specific that having a single shared lock for it does not seem like it will ever be a scalability limit.
I've left a couple of suggestions, but otherwise happy to merge this in :)
@ivoanjo Thank you! Applied the suggestions and cleaned-up commits :D |
Co-authored-by: Ivo Anjo <[email protected]>
Thanks again, merging away! |
@ixti thanks for the contribution! We really appreciate it. :) |
What does this PR do?
Fixes Mutex assignment to be thread-safe.
Motivation
Tried to find what might be causing #2045 and found (unfortunately unrelated) but somewhat problematic code. We've tried this patch in production, unfortunately as I said, it's not fixing #2045, but still worth fixing.
Additional Notes
This is a simple demo, why
@mutex ||= Mutex.new
is a bad idea. Save the below ruby script asdemo.rb
:Run:
At some point you will see that there are more than one mutex. Now, run:
There should be no such issue.
How to test the change?
It's not very feasible to automatically test this. But the change is small enough.