-
Notifications
You must be signed in to change notification settings - Fork 19
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 request_id option to client #133
Conversation
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.
This is missing the crucial part, which is including the request_id in the ConnectionTimeout
, ConnectionError
etc. so that when the customer gets the timeout they can correlate it with our logs. Otherwise there's no point, we get the request id but don't have anything to correlate it with
lib/ably/rest/client.rb
Outdated
@@ -146,6 +150,7 @@ def initialize(options) | |||
@custom_host = options.delete(:rest_host) | |||
@custom_port = options.delete(:port) | |||
@custom_tls_port = options.delete(:tls_port) | |||
@add_request_ids = options.delete(:add_request_ids) == false ? false : true |
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.
As an optional extra debug option, this should default to false (unless and until it's added to the spec). So just @add_request_ids = options.delete(:add_request_ids)
is fine.
(Also, cond ? false : true
is a pointless pattern, it's equivalent to !cond
)
lib/ably/rest/client.rb
Outdated
path_with_params.query_values = {request_id: SecureRandom.hex(20)} | ||
query = path_with_params.query | ||
path = "#{path}?#{query}" | ||
end |
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.
Why are you manually munging the path? This method takes a params hash. You can add it to the params hash.
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.
@SimonWoolf because this is Ruby and not Elixir. So the params
object would be changed by just calling this method, which is bad.
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.
So the params object would be changed by just calling this method, which is bad.
@mattheworiordan fair enough, but imo the solution to that is to clone the hash (as Cesare has done in the latest version of this pr), not to start munging a param into the path (meaning faraday has to parse it out again to combine with the params -- assuming it even does that rather than give you a path with two ?
s). (for one thing the original approach assumes the path doesn't already have a querysting, so fails the 'what if two things did this' test)
spec/unit/rest/client_spec.rb
Outdated
it 'includes request_id in URL' do | ||
expect(subject.add_request_ids).to eql(true) | ||
end | ||
end |
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.
If you're going to add a test, may as well be one that checks the result (i.e. uses webmock to check that a request_id
actually gets added to the querystring for rest requests, and checks timeout exceptions to make sure that the request ID gets printed with those), rather than just checks that an instance variable got set correctly, no?
@SimonWoolf re-ready for review. |
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.
Almost there
lib/ably/rest/client.rb
Outdated
@@ -434,6 +439,10 @@ def send_request(method, path, params, options) | |||
max_retry_duration = http_defaults.fetch(:max_retry_duration) | |||
requested_at = Time.now | |||
retry_count = 0 | |||
if @add_request_ids | |||
params = {} if params.nil? | |||
params[:request_id] = SecureRandom.hex(20) |
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.
Hex takes 2 characters per byte, which means this adds 40 characters, which is just unnecessary. Go for SecureRandom.urlsafe_base64(10)
instead, that'll be 14 characters
lib/ably/exceptions.rb
Outdated
def initialize(message, status = nil, code = nil, base_exception = nil, options = {}) | ||
super message, status, code, base_exception, options | ||
def initialize(message, status = nil, code = nil, base_exception = nil, options = {}, request_id = nil) | ||
super message, status, code, base_exception, options, request_id |
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.
Getting there, but just having the request ID as a property of the exception isn't enough -- it'll be printed to a log file, it needs to be in the string representation, next method below this one.
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.
If we leave request_id
as an option of options, then no change here is needed.
lib/ably/rest/client.rb
Outdated
@@ -456,12 +465,12 @@ def send_request(method, path, params, options) | |||
logger.warn { "Ably::Rest::Client - Retry #{retry_count} for #{method} #{path} #{params} as initial attempt failed: #{error}" } | |||
retry | |||
end | |||
|
|||
request_id = params[:request_id] if @add_request_ids |
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.
you generated and set the request ID only a few lines earlier; just make a local var there, it'll still be in scope, no need to fetch from the params hash
lib/ably/rest/client.rb
Outdated
when Faraday::ClientError | ||
raise Ably::Exceptions::ConnectionError.new(error.message, nil, 80000, error) | ||
raise Ably::Exceptions::ConnectionError.new(error.message, nil, 80000, error, {}, request_id) | ||
else | ||
raise error |
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.
One of the errors they want to correlate was the nonce value one, which'll be in this branch, that also needs the requestId set.
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 am also not keen on a var called random_request_id
, that sounds more like a method. It's no longer random once it's assigned, and thus it's consistent for the request.
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.
In addition to @SimonWoolf's feedback...
lib/ably/exceptions.rb
Outdated
|
||
def initialize(message, status = nil, code = nil, base_exception = nil, options = {}) | ||
def initialize(message, status = nil, code = nil, base_exception = nil, options = {}, request_id = nil) |
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 would have preferred for request_id
to be an option given it's a flexible hash for this reason.
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.
Didn’t know the supposed usage of that hash, that’s why I didn’t touch it. Good to know that I can use it.
lib/ably/exceptions.rb
Outdated
def initialize(message, status = nil, code = nil, base_exception = nil, options = {}) | ||
super message, status, code, base_exception, options | ||
def initialize(message, status = nil, code = nil, base_exception = nil, options = {}, request_id = nil) | ||
super message, status, code, base_exception, options, request_id |
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.
If we leave request_id
as an option of options, then no change here is needed.
lib/ably/rest/client.rb
Outdated
@@ -434,6 +439,10 @@ def send_request(method, path, params, options) | |||
max_retry_duration = http_defaults.fetch(:max_retry_duration) | |||
requested_at = Time.now | |||
retry_count = 0 | |||
if @add_request_ids |
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.
It's bad practice to read an instance variable like this when it's available as a reader i.e. if @add_request_idds
would always be false due to a type, whereas if add_request_idds
would fail.
lib/ably/rest/client.rb
Outdated
case error | ||
when Faraday::TimeoutError | ||
raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error) | ||
raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error, {}, request_id) |
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.
Based on my proposed change, it would be raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error, { request_id: request_id })
.
I think raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error, request_id: request_id)
may work too, although I recall 1.9.2 having some constraints with pseudo named args.
spec/unit/rest/client_spec.rb
Outdated
@@ -50,4 +50,11 @@ | |||
end | |||
end | |||
end | |||
|
|||
context 'request_id generation' do | |||
let(:client_options) { {key: 'appid.keyuid:keysecret', add_request_ids: true} } |
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.
Unusual spacing, in all other places {
has a space after, and }
a space before
Ready for another look. |
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.
Quite a few comments I am afraid.
lib/ably/rest/client.rb
Outdated
@@ -449,6 +449,7 @@ def send_request(method, path, params, options) | |||
use_fallback = can_fallback_to_alternate_ably_host? && retry_count > 0 | |||
|
|||
connection(use_fallback: use_fallback).send(method, path, params) do |request| | |||
request.options.context = {request_id: random_request_id} if add_request_ids |
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.
Inconsistent spacing here again.
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, shouldn't this append/merge into the request options context in case there are other attributes already? This will always overwrite them which feels wrong and is error prone.
spec/acceptance/rest/client_spec.rb
Outdated
@@ -961,6 +961,7 @@ def encode64(text) | |||
end | |||
|
|||
context 'request_id generation' do | |||
context 'Timeout error' 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.
Indentation issues here. Why did you add context & end without indenting the code?
begin | ||
client2.auth.request_token(token_params) | ||
rescue Ably::Exceptions::UnauthorizedRequest => err | ||
expect(err.request_id).to_not eql(nil) |
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.
Do we have any test to ensure that when add_request_ids
is false, that the request_id
is nil
?
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.
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.
Ah, yes!
lib/ably/rest/client.rb
Outdated
path_with_params.query_values = {request_id: SecureRandom.hex(20)} | ||
query = path_with_params.query | ||
path = "#{path}?#{query}" | ||
end |
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.
@SimonWoolf because this is Ruby and not Elixir. So the params
object would be changed by just calling this method, which is bad.
lib/ably/rest/client.rb
Outdated
@@ -470,9 +470,9 @@ def send_request(method, path, params, options) | |||
end | |||
case error | |||
when Faraday::TimeoutError | |||
raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error, {}, random_request_id) | |||
raise Ably::Exceptions::ConnectionTimeout.new(error.message, nil, 80014, error, {request_id: random_request_id}) |
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.
Spacing issues again, here and many times below
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 am not a big fan of this. random_request_id
is defined in line 445, yet is within a conditional. I realise Ruby allows this sort of thing, but it's messy and error prone. Why are you defining random_request_id
as a varaiable when you have access to the params
variable in this scope which is available anyway?
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.
In fact, I am not sure I have seen a test that confirms that a) a request is sent to Ably with a request_id
, b) it fails (intentionally), c) the test confirms that the exception raised has that SAME request_id
, d) the Logger is showing the request_id
in the output. This is the crux of what we're doing here. Given this code path, I fear our tests so far are just testing that a request_id
is present, but not ensuring it is consistent throughout.
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.
This makes sense, but I am struggling a bit to inspect the url of a request, say client.stats
. Any suggestion?
@@ -27,7 +27,7 @@ def on_complete(env) | |||
end | |||
|
|||
message = 'Unknown server error' if message.to_s.strip == '' | |||
request_id = env.request.context[:request_id] | |||
request_id = env.request.context[:request_id] if env.request.context |
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.
Spacing issues below.
lib/ably/rest/client.rb
Outdated
when Faraday::ClientError | ||
raise Ably::Exceptions::ConnectionError.new(error.message, nil, 80000, error) | ||
raise Ably::Exceptions::ConnectionError.new(error.message, nil, 80000, error, {}, request_id) | ||
else | ||
raise error |
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 am also not keen on a var called random_request_id
, that sounds more like a method. It's no longer random once it's assigned, and thus it's consistent for the request.
@funkyboy what is going on with your indentation? |
f99e186
to
8b87e5f
Compare
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.
LGTM 👍
lib/ably/rest/client.rb
Outdated
@@ -434,11 +439,22 @@ def send_request(method, path, params, options) | |||
max_retry_duration = http_defaults.fetch(:max_retry_duration) | |||
requested_at = Time.now | |||
retry_count = 0 | |||
request_id = nil | |||
if add_request_ids | |||
params = {} if params.nil? |
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.
This is a tiny comment, but it seems unnecesary to dup an empty hash, so perhaps instead just:
params = if params.nil?
{}
else
params.dup
end
lib/ably/rest/client.rb
Outdated
when Faraday::ClientError | ||
raise Ably::Exceptions::ConnectionError.new(error.message, nil, 80000, error) | ||
raise Ably::Exceptions::ConnectionError.new(error.message, nil, 80000, error, { request_id: request_id }) |
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.
May be nice for future us to add a note about the raise error
not explicitly passing the request_id
, but in the Middleware it's available through the requext context. WDYT?
Adds a
request_id=random_value
query string to any path if the client is initialized with options{ add_request_ids: true }
. Handy for debugging.