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

Adopt Sender.append() and flush() #1

Merged

Conversation

rmfitzpatrick
Copy link

Implements the Sender.append() and Sender.flush() methods requested in jaegertracing#186 (review) in an attempt to get those changes sourced.

Signed-off-by: Ryan Fitzpatrick rmfitzpatrick@signalfx.com

@rmfitzpatrick
Copy link
Author

@ProvoK, if you're interested in continuing with these changes, do you mind rebasing your branch to update the dependencies for successful CI runs?

@rmfitzpatrick rmfitzpatrick force-pushed the send_spans_over_http branch 2 times, most recently from 99b5bd5 to d43a89e Compare July 12, 2018 21:18
@ProvoK
Copy link
Owner

ProvoK commented Jul 13, 2018

@rmfitzpatrick Hello there,

thanks for continuing the work! Yes I'm interested, I was a lot busy in the last period, that's why I was not working on it.

I'll update my branch and review your change asap 😉

@ProvoK
Copy link
Owner

ProvoK commented Jul 13, 2018

@rmfitzpatrick I've fetched upstream master and merged into my branch. This is all I can do till Monday, sorry!

@codecov-io
Copy link

codecov-io commented Jul 13, 2018

Codecov Report

Merging #1 into send_spans_over_http will increase coverage by 0.36%.
The diff coverage is 100%.

Impacted file tree graph

@@                   Coverage Diff                    @@
##           send_spans_over_http       #1      +/-   ##
========================================================
+ Coverage                 94.86%   95.23%   +0.36%     
========================================================
  Files                        26       26              
  Lines                      1812     1824      +12     
  Branches                    227      227              
========================================================
+ Hits                       1719     1737      +18     
+ Misses                       59       55       -4     
+ Partials                     34       32       -2
Impacted Files Coverage Δ
jaeger_client/senders.py 98.43% <100%> (+4.68%) ⬆️
jaeger_client/reporter.py 95.86% <100%> (+2.52%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 435b532...073d2ba. Read the comment docs.

Signed-off-by: Ryan Fitzpatrick <rmfitzpatrick@signalfx.com>
@@ -168,14 +160,15 @@ def _report_span_from_ioloop(self, span):

@tornado.gen.coroutine
def _consume_queue(self):
spans = []
spans_appended = 0
Copy link
Owner

Choose a reason for hiding this comment

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

Keeping the count of an internal state of the sender outside of it doesn't seem to me like a good idea.
It's better that the sender expose a property like spans_count which returns the real and actual count of its spans.
With that, you can easily get rid of updating this spans_appended variable (line 183, 187), with a more secure and less error prone behaviour.

Copy link
Author

Choose a reason for hiding this comment

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

Great idea, updated.


raise tornado.gen.Return((num_spans, exc, error_msg))
Copy link
Owner

Choose a reason for hiding this comment

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

I'm not very skilled with tornado: is there a valid reason to handle exceptions in this manner?

Copy link
Author

Choose a reason for hiding this comment

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

This is a special exception that allows coroutines to return values in older versions of python: http://www.tornadoweb.org/en/stable/gen.html#tornado.gen.Return. It's necessary to get the exception info from the send generator.

Copy link
Owner

@ProvoK ProvoK Jul 18, 2018

Choose a reason for hiding this comment

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

Ok, I got it. Still I'm not sure this is necessary.

The only return value used is num_spans, which is everytime equal to sender.span_count with the actual implementation.

This means that you can simply save a variable here (something like num_spans = self._sender.span_count) and then use it later for reporting the number of success/error spans.

Everything else (exceptions and theirs messages) can be handled normally, I suppose.
@rmfitzpatrick

Copy link
Author

@rmfitzpatrick rmfitzpatrick Jul 19, 2018

Choose a reason for hiding this comment

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

Of course, makes more sense to fully adopt span_count and I've updated. However, I did retain the Sender._flush() generated exception info so that transport-specific exception handling is possible in Sender and its subclasses, without having to maintain a complete, sender-aware try/except block in Reporter._consume_queue(). Does that work for you, @ProvoK?

def send(self, batch):
"""
Send batch of spans out via thrift transport. Any exceptions thrown
Copy link
Owner

Choose a reason for hiding this comment

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

This docstring does not belongs to this method

Copy link
Author

Choose a reason for hiding this comment

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

Good call. Updated.

def __init__(self, host, port, io_loop=None):
self.host = host
self.port = port
def __init__(self, io_loop=None):
Copy link
Owner

Choose a reason for hiding this comment

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

host and port are likely to be needed in every sender implementations. I would keep them in the base class

Copy link
Author

Choose a reason for hiding this comment

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

In my early HTTPSender implementation I found only a url is necessary, and constructing one via host, port, and trace endpoint seems overkill (other jaeger clients use JAEGER_ENDPOINT env var to specify the collector location).

Copy link
Owner

Choose a reason for hiding this comment

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

Ok, let's see if jaeger maintainers are ok with this interface

Copy link
Author

Choose a reason for hiding this comment

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

Do you mean before merging into your PR branch?

Copy link
Owner

Choose a reason for hiding this comment

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

Nono, I mean after merging in my PR branch. The only one remaining issue is the one related to tornado.gen.Return, I replied to you there yesterday

Copy link
Author

Choose a reason for hiding this comment

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

Sorry, I missed that comment.

@rmfitzpatrick
Copy link
Author

@ProvoK, thanks for the feedback, which I believe I've addressed as requested with the exception of #1 (comment) to match the logic of other clients. Let me know if you still request that change and I'll be happy to do so, or if you have any other notes.


if self._sender.span_count:
num_spans = self._sender.span_count
exc, error_msg = yield self._sender.flush()
Copy link
Owner

Choose a reason for hiding this comment

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

I don't get the benefit of that, honestly. What you are doing now is a try ...except Exception ... in the Sender.flush(), returning the exception plus a message. Which is the same as an raise AnyException("message")

In the Reporter you just check if any kind of exc is raised, and the use the message returned.

This is the very same of:

try:
    yield self._sender.flush()
except Exception as exc:
   self.metrics.reporter_failure(num_spans)
   self.error_reporter.error(error_msg, exc)
else:
    self.metrics.reporter_success(num_spans)

but this is the most common and standard way to deal with exceptions, in Python.

The point is to keep the code "idiomatic".
In other languages, as Go, the standard is to have the pattern you are doing: multiple returns, one for the error/ok-boolean and the other for the "real" value.

Copy link
Author

Choose a reason for hiding this comment

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

Where does error_msg come from in this excerpt? Providing error_reporter.error() with str(exc) and exc loses the information provided by the initial local-agent submission failure: https://github.com/jaegertracing/jaeger-client-python/blob/master/jaeger_client/reporter.py#L201

This pattern could be repeated with all manner of clients in the future, who idiomatically catch their transport specific exceptions and provide a relevant exception message.

Copy link
Owner

@ProvoK ProvoK Jul 19, 2018

Choose a reason for hiding this comment

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

I was writing in this little github editor so I lost the context

try:
    yield self._sender.flush()
except Exception as exc:
   self.metrics.reporter_failure(num_spans)
   self.error_reporter.error("Failed to submit traces to jaeger: {}".format(exc), exc)
else:
    self.metrics.reporter_success(num_spans)

You can still catch below, in the sender, the specific error and provide a relevant message, carried with the exception itself.

Copy link
Author

@rmfitzpatrick rmfitzpatrick Jul 19, 2018

Choose a reason for hiding this comment

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

Providing a custom message in send() seems effective. Are you comfortable with using something like six.reraise to add a custom value with the traceback from sys.exc_info()?

Copy link
Owner

Choose a reason for hiding this comment

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

I would not add six as dependency just for that. And probably jaeger maitaners wouldn't too

Copy link
Author

@rmfitzpatrick rmfitzpatrick Jul 19, 2018

Choose a reason for hiding this comment

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

It already is one via thrift.

edit: Not to speak in circles, but I'm not aware of a version agnostic way to override an exception value without making Reporter sender detail aware or less informative otherwise.

Copy link
Owner

Choose a reason for hiding this comment

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

Oh, didn't know it was included in thrift. Then I suppose it's allright to use it

try:
yield self.send(batch)
except Exception as e:
exc = e
Copy link
Owner

Choose a reason for hiding this comment

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

Here you can just raise e("my message") from e and it's done! (alongwith the previous comment modifications)

The from e part preserve the exception stacktrace

Copy link
Author

Choose a reason for hiding this comment

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

from e is not supported in python 2.

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah, you're right. I'm too used to python3

@@ -81,6 +134,24 @@ def _create_local_agent_channel(self, io_loop):
io_loop=io_loop
)

@tornado.gen.coroutine
def _flush(self, spans, process):
exc = None
Copy link
Owner

Choose a reason for hiding this comment

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

Again here, you can save a lot of lines of code

@rmfitzpatrick rmfitzpatrick force-pushed the send_spans_over_http branch 2 times, most recently from 5e9d687 to 361b5f5 Compare July 19, 2018 20:45
@rmfitzpatrick
Copy link
Author

rmfitzpatrick commented Jul 19, 2018

edit: User error with a stale test.

I really appreciate your thorough feedback and believe I've addressed it all, @ProvoK.

@rmfitzpatrick rmfitzpatrick force-pushed the send_spans_over_http branch 3 times, most recently from b8a9656 to 29ead94 Compare July 19, 2018 22:24
Also handle exceptions normally over marshalling as future result.

Signed-off-by: Ryan Fitzpatrick <rmfitzpatrick@signalfx.com>
@ProvoK
Copy link
Owner

ProvoK commented Jul 22, 2018

Thanks @rmfitzpatrick for your work, I'm going to merge this, and I'll quote in in the original PR.

@ProvoK ProvoK merged commit fbefb07 into ProvoK:send_spans_over_http Jul 22, 2018
@rmfitzpatrick rmfitzpatrick deleted the send_spans_over_http branch August 10, 2018 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants