Skip to content

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

Open
tlbraams wants to merge 3 commits into
WeAreFarmGeek:masterfrom
tlbraams:query-create-faraday-error
Open

Handle Faraday::ServerError in Diplomat::Query#create#228
tlbraams wants to merge 3 commits into
WeAreFarmGeek:masterfrom
tlbraams:query-create-faraday-error

Conversation

@tlbraams

Copy link
Copy Markdown

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

@pierresouchay pierresouchay left a comment

Copy link
Copy Markdown
Member

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

Comment thread lib/diplomat/query.rb Outdated
@raw = send_post_request(@conn, ['/v1/query'], options, definition, custom_params)
parse_body
rescue Faraday::ClientError
rescue Faraday::ClientError, Faraday::ServerError

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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.

@pierresouchay pierresouchay left a comment

Copy link
Copy Markdown
Member

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

Comment thread lib/diplomat/query.rb
@raw = send_post_request(@conn, ['/v1/query'], options, definition, custom_params)
parse_body
rescue Faraday::ClientError
rescue Faraday::TimeoutError => e

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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

Comment thread lib/diplomat/query.rb
rescue Faraday::ClientError
rescue Faraday::TimeoutError => e
raise e
rescue *faraday_error_classes

Copy link
Copy Markdown
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
Copy Markdown
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