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 out_of_band hook #2218
Fix out_of_band hook #2218
Conversation
4f8feb8
to
5a49f22
Compare
I just switched to a different 'apt-get' server, so Ubuntu should consistently install ragel, I think. |
lib/puma/thread_pool.rb
Outdated
@@ -79,6 +79,10 @@ def pool_capacity | |||
waiting + (@max - spawned) | |||
end | |||
|
|||
def busy_threads | |||
@spawned - @waiting + @todo.size |
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.
I think we need to wait for the @mutex
here if not already held. This will work on MRI but won't work on JRuby/etc.
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.
Done in ff28636 - not 100% sure if this approach (calling @mutex.owned?
in a with_mutex
wrapper method) has any small perf implications we care about. Alternative would be to not use a method at all, or introduce a in_mutex
argument.
lib/puma/dsl.rb
Outdated
@@ -494,8 +494,9 @@ def after_worker_fork(&block) | |||
|
|||
# Code to run out-of-band when the worker is idle. | |||
# These hooks run immediately after a request has finished | |||
# processing and there are no busy threads on the worker. | |||
# The worker doesn't accept new requests until this code finishes. |
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.
Whoops, that wasn't ever true, was it?
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.
Also, Q: should this be the actual behavior? Is it really OOB otherwise?
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.
Whoops, that wasn't ever true, was it?
The oob hook previously ran from the acceptor thread, so I think it was true at least for the case where it was working correctly (max threads 1).
should this be the actual behavior? Is it really OOB otherwise?
Hmm, I went back and forth on whether it's better to keep accepting new requests while an OOB hook is running. (Might depend on the hook's use case?) For GC optimization at least, I thought continuing to accept requests could potentially be better under high load (though the MRI GVL could make it a wash). But on second thought, not accepting new requests is probably safer for light loads especially while request-balancing is still uneven. Plus it is 'really OOB' for other use-cases. I can change this to run on the acceptor thread as before.
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.
Updated in 7b36391
Only Q for me is: should the OOB hook work the way it was advertised? "No other requests will be processed until the hook finishes". Both approaches have the potential to add latency to requests. |
c1092b3
to
ff28636
Compare
Hm. I think that if the method is called
|
@wjordan thoughts on #2218 (comment)? |
Add server tests to ensure oob hooks fire as expected.
6be5e0d
to
17ea126
Compare
Prevent overlapping requests from being processed during out of band hook.
64db4f0
to
97e9d06
Compare
Oh OK, that's definitely better than interacting with the threadpool 👍 |
@@ -439,6 +436,12 @@ def process_client(client, buffer) | |||
rescue StandardError => e | |||
@events.unknown_error self, e, "Client" | |||
end | |||
|
|||
if @options[:out_of_band] | |||
@thread_pool.with_mutex do |
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.
Does this actually create a mutex now on thread_pool
? The OOB
hook is executed within a mutex, rather than dispatched to be executed. Does it mean that it creates a contention now due to mutex, till OOB
is executed, pretty much similar to before, stopping acceptance thread.
This also, connected with a test #test_out_of_band_stream
creates a very strong dependency on execution: The test assumes that all connections are in fact processed concurrently, but this might not always be true, because the test does fail if we assume that puma runs with . threads=1
The test does fail in this condition, as this is being executed on down failing edge of the event.
Description
Fixes #2177. (Thanks @GuiTeK for tracking down this bug!)
Modifies the
out_of_band
hook implementation in theServer
class to ensure that hooks are fired as expected when the server is configured with >1 max threads.Compare to #2178, #2180. This implementation is pretty simple. It changes behavior slightly by executing the hooks on the same thread that finished processing the last request (instead of the main acceptor thread), so the server can continue accepting incoming requests if any arrive while the out-of-band hooks are still executing.
I've also added a couple of server tests to ensure out-of-band hooks fire as expected in two scenarios (sequential and parallel requests).
Your checklist for this pull request
[changelog skip]
the pull request title.[ci skip]
to the title of the PR.#issue
" to the PR description or my commit messages.