Skip to content
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

Handle Faraday::ServerError in Diplomat::Query#create #228

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

tlbraams
Copy link

Fixes #227

Builds on #203

To support Faraday 1.0, Faraday::ServerError handling was added to the Diplomat::RestClient#send_get_request method. The Diplomat::Server#create` method should also be updated.

Change passed rake with 1.10.1 and 0.17.3

Copy link
Member

@pierresouchay pierresouchay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @tlbraams ,

Thanks a lot for you contribution, that's great!

However, this fix is currently causing issues with Faraday 0.9.x -> so would break those installations.

I made 2 suggestions to avoid that, feel free to fix and I'll be glad to merge your changes

@@ -32,7 +32,7 @@ def create(definition, options = {})
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
@raw = send_post_request(@conn, ['/v1/query'], options, definition, custom_params)
parse_body
rescue Faraday::ClientError
rescue Faraday::ClientError, Faraday::ServerError
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is now using the same path for both Faraday::ClientError and Faraday::ServerError

The distinction between both started in version 1.0 as you noted and has been introduced with this commit:
https://github.com/lostisland/faraday/pull/841/files#diff-c5f031a4be2bbda4d7befb7c1604ffee77d3bc86038f280aca9b14f1fc00350e

Both Faraday::ClientError and Faraday::ServerError inherit the same Faraday::Error class and this is the case before and after this change.

The problem with your fix is that rescuing both will create a NameError on versions of Faraday lower than 1.x (assuming that some users are using it, but it is definitely the case from my experience), if you look at dependencies in GemFile, 0.9 is still supported.

I would suggest 2 possible solutions here:

  • either simply do a rescue Faraday::Error => would work for both ServerError and ClientError for Faraday 1+ but would also work on Faraday 0.9.x
  • either check that if Faraday::ServerError is defined, catch it, otherwise, catch ClientError

The second solution is probably better, because the message would be more accurate with Faraday 1.x because a timeout would never trigger a "QuertAlreadyExists" Exception which is misleading

This could be done by something like this:

# This method returns the correct Exception Class for ServerError depending of Faraday version being installed
# see https://github.com/WeAreFarmGeek/diplomat/issues/227
def serverExceptionToCatch()
  Faraday.const_defined?(:ServerError) ? Faraday::ServerError : Faraday::Error
end

[...]
def create(definition, options = {})
      custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
      @raw = send_post_request(@conn, ['/v1/query'], options, definition, custom_params)
      parse_body
    rescue serverExceptionToCatch
      ...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was actually wondering if that might cause issues. I did see that in the PR I linked, the same change was made:

rescue Faraday::ClientError, Faraday::ServerError => e

This caused me to believe this change should be fine. Is it actually broken in the rest client aswell then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, indeed, it has not been reported, but pointing to a non existing class will actually fail.

Example:

irb
 :001 > require 'faraday'
 => true
 :002 > begin
 :003 >   1/0
 :004 > rescue Faraday::ClientError, Faraday::ServerError, Faraday::WeirdError, ZeroDivisionError
 :005 >   puts "bye"
 :006 > end
Traceback (most recent call last):
        5: from /Users/b188lf/.rvm/rubies/ruby-2.7.2/bin/irb:23:in `<main>'
        4: from /Users/b188lf/.rvm/rubies/ruby-2.7.2/bin/irb:23:in `load'
        3: from /Users/b188lf/.rvm/rubies/ruby-2.7.2/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
        2: from (irb):2
        1: from (irb):4:in `rescue in irb_binding'
NameError (uninitialized constant Faraday::WeirdError)

Comment on lines +311 to +313
def faraday_error_classes
Faraday.const_defined?(:ServerError) ? [Faraday::ClientError, Faraday::ServerError] : [Faraday::ClientError]
end
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure about the name/placement of this method, but is this like what you had in mind?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, i think it is bad to catch both ClientError and ServerError when ServerError exists, because it would thow a QueryAlreadyExists when a timeout occurs.

So I would use your code, but only return ServerError in such case, not ClientError, to avoid code calling this that this query exist while it is just that the agent did timeout for instance.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, Faraday::TimeoutError is a client error in faraday < 1 and a server error in >= 1, even though the documentation claims it's a unified client error. As such, I decided to explicitly catch and reraise timeout errors.

Copy link
Member

@pierresouchay pierresouchay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was not clear last time (confusing about timeout), the best is probably to just catch Faraday::Error everywhere and not try to be too clever

@@ -32,7 +32,9 @@ def create(definition, options = {})
custom_params = options[:dc] ? use_named_parameter('dc', options[:dc]) : nil
@raw = send_post_request(@conn, ['/v1/query'], options, definition, custom_params)
parse_body
rescue Faraday::ClientError
rescue Faraday::TimeoutError => e
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not put this, simply let the exception Faraday::Timeout error raise by itself... my example with timeout was a bad example, I was talking on ClientError on Faraday 1+

So, remove the :

    rescue Faraday::TimeoutError => e
        raise e

rescue Faraday::ClientError, Faraday::ServerError => e
rescue Faraday::TimeoutError => e
raise e
rescue *faraday_error_classes => e
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of existing code sucks because it raises a Diplomat::PathNotFound in most cases, but users might rely on it... just rescue Faraday::Error and don't catch Faraday::TimeoutError

#
# @return [Array<Class>] Faraday error classes that should be caught
def faraday_error_classes
Faraday.const_defined?(:ServerError) ? [Faraday::ClientError, Faraday::ServerError] : [Faraday::ClientError]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, we probably misunderstood each other:

My point was:

  • Either we just want to catch all, in such case, we just catch Faraday::Error and this method is not useful => which is exactly what you do (because in Faraday 1+: Faraday::Error := Faraday::ClientError + Faraday::ServerError while in 0.9, Faraday::Error := Faraday::ClientError which includes also all errors of Faraday::ServerError )
  • either you create this method, but in such case, it should only return Faraday::ServerError when defined and Faraday::ClientError on Faraday 0.9

rescue Faraday::ClientError
rescue Faraday::TimeoutError => e
raise e
rescue *faraday_error_classes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment below on faraday_error_classes

@pierresouchay
Copy link
Member

Hello @tlbraams,

Sorry, this MR takes some time, I hope you are not upset or afraid of contributing :-)

If you get lost or need help, feel free to ask

Regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Faraday 1.x can bypass Diplomat::QueryAlreadyExists
2 participants