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
Add customizable error messages on thread shutdown #2182
Conversation
bbe03e1
to
2dea216
Compare
Provided some code feedback but I'm not 100% sure on the usecase. "we want to always guarantee a JSON response" -> Why? Isn't the 503 status code enough here? |
lib/puma/dsl.rb
Outdated
# force_shutdown_error_response(500, {"Content-Type" => "application/json"}, [JSON.generate({message: "Server shutdown."})]) | ||
def force_shutdown_error_response(status, headers, response) | ||
raise "Headers must be a hash" unless headers.is_a?(Hash) | ||
raise "Response must be enumerable" unless response.is_a?(Enumerable) |
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.
Actually, it doesn't have to be an Enumerable. It just has to respond to each
.
lib/puma/server.rb
Outdated
if @options[:force_shutdown_error_response] | ||
status, headers, res_body = @options[:force_shutdown_error_response] | ||
else | ||
status = 503 |
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.
Let's set this as the default value of the @options
, not hard-coded here.
Then, if we decide that yes, it's important that Puma returns a particular content-type on low-level failures, I think instead we should change all low-level errors. There's a lot of them and this only configures one, which isn't that helpful. Something like: lowlevel_error_content_type = :json Maybe we can support :json or :none for now. Existing sites where we render lowlevel errors would be changed to look something like: if @leak_stack_on_error
generate_error_response(status: 500, body: "Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}")
else
generate_error_reponse(status: 500, body: "An unhandled lowlevel error occurred. The application logs may have details").
end |
I can understand the use-case, if you are building an API, and you document that your API return responses in JSON, it is unexpected when the response isn't in JSON. Maybe you want to include more information than the HTTP status code, e.g. your application can return HTTP status 503 for number of different errors, and you want to provide a certain error code in the error response. Then it makes sense to return the body in JSON, including that information. |
2dea216
to
796af0d
Compare
@dentarg has it right. Returning static JSON of Puma's choosing would be an improvement over static plaintext, but it's not compatible with a JSON API if the API expects I think it's ok if I made the changes requested, let me know if you have anymore feedback! |
796af0d
to
06979d7
Compare
Cool, ppl on Twitter agreed this is useful so we'll put it in. I need to pull it down and play w/implementation to see if there's a better way to integrate it w/the rest of our error handling. |
Sounds good! Thanks for the fast response |
Just to double check, I assume you don't need anything from me at this point since it's approved. You're just waiting on having time to play around with alternatives? |
@zanker-stripe yup, thanks! |
OK, so I took another look and here's what I think needs to happen:
def lowlevel_error(e, env, status = 500)
if handler = @options[:lowlevel_error_handler]
if handler.arity == 1
return handler.call(e)
elsif handler.arity == 2
return handler.call(e, env)
elsif handler.arity == 3
return handler.call(e, env, status)
end
end
end and the default config should have the following: DefaultLowlevelErrorHandler = lambda do |_e, _env, status|
[status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]].freeze
end |
This way, if you want JSON responses, you can do that consistently for all error types. |
@nateberkopec Do you have any concerns with the fact that this means people can accidentally run slower code during exit and essentially delay the shutdown timer? I'm fine with it if you are, and I'll do a PR for that. |
I'm OK with that. We can document that this should return ASAP or the shutdown timer may be affected. |
People do talk to external APIs in their lowlevel_handlers often (Sentry, Rollbar, etc) but I don't think that possibly delaying the shutdown timer by up to a second is all that critical of an issue 🤷♂ |
Fair enough! #2203 will handle it that way. |
Description
When threads are forced to shutdown, the response is static and cannot be configured. For our use case, we want to always guarantee a JSON response is returned on the rare case this comes up.
I didn't make this a proc like
lowlevel_error_handler
, since the intent is to be shutting down quickly, a static response felt more appropriate. While I was here, I also added a test for the base case (default message) as I couldn't find any tests which exercised that.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.