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

Akka Actor and Akka HTTP bindAndHandle instrumentation #2050

Merged
merged 6 commits into from Nov 13, 2020

Conversation

bantonsson
Copy link
Contributor

@bantonsson bantonsson commented Nov 4, 2020

A number of changes to support bindAndHandle in akka-http:

  • Change akka-actor instrumentation to not leak context between actors, and attach context to the messages instead of the mailboxes.
  • Instrument the flow in bindAndHandle for akka-http to have one single place that covers all server bind* variants.
  • Change the client instrumentation in akka-http to not rely on a thread local when checking for nesting to avoid repeated spans in preparation for client flow support.

@bantonsson bantonsson self-assigned this Nov 4, 2020
@bantonsson bantonsson force-pushed the ban/akka-http-bah branch 3 times, most recently from 89649cd to 7eac56a Compare November 9, 2020 16:58
@bantonsson bantonsson marked this pull request as ready for review November 10, 2020 12:36
@bantonsson bantonsson requested a review from a team as a code owner November 10, 2020 12:36
@bantonsson bantonsson changed the title [WIP] Running tests after akka changes Akka Actor and Akka HTTP bindAndHandle instrumentation Nov 10, 2020
Comment on lines +61 to +85
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void exit(@Advice.Enter TraceScope scope) {
// Clean up any leaking scopes from akka-streams/akka-http et.c.
TraceScope activeScope = activeScope();
while (activeScope != null && activeScope != scope) {
activeScope.close();
activeScope = activeScope();
}
while (activeScope == scope) {
scope.close();
activeScope = activeScope();
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems rather scary and will likely result in unexpected bugs elsewhere. Do you know why this is necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are two things here. One is the intentional leak in the akka-http server instrumentation that you commented on, and the second is just plain defensive programming. There should never ever be anything left on the scope stack after the actor has processed a message.


public static class ConstructAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void afterInit(@Advice.This Envelope zis) {
Copy link
Contributor

Choose a reason for hiding this comment

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

lol... zis

Comment on lines +65 to +88
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void exit(@Advice.Enter final AgentScope scope) {
// Clean up any leaking scopes from akka-streams/akka-http et.c.
TraceScope activeScope = activeScope();
while (activeScope != null && activeScope != scope) {
activeScope.close();
activeScope = activeScope();
}
while (activeScope == scope) {
scope.close();
activeScope = activeScope();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Same concern applies here.

Comment on lines +178 to +206
// Since we haven't instrumented the akka stream state machine, we can't rely
// on spans and scopes being propagated during the push and pull of the
// element. Instead we let the scope leak intentionally here and clean it
// up when the user response comes back, or in the actor message processing
// instrumentation that drives this state machine.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is interesting. I guess this is the reason you need that logic to clear the scope stack. I don't think I understand though why the scope can't be closed after the push.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So akka streams are run by a state machine, and the only thing you know after the push is that this specific element has been put in the slot representing the requestOutlet. Nothing more has happened, and no request handling code has run yet. If we close the scope at that point, there will be nothing in the scope. Since the whole stream machinery has been fused we can be certain that this element will be picked up by the same thread that is running this onPush method, and then the request handling code will be run. If the request handling code completes synchronously, then the handler for the responseInlet will close the scope directly in that onPush method. Otherwise the scope will be closed by the actor messaging instrumentation cleaning the scope stack.

I'll add a more detailed comment about how things work.

I'll write a

Copy link
Contributor

@devinsba devinsba left a comment

Choose a reason for hiding this comment

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

Looks good to me. I wonder if there is a way for us to assert correctness with regards to the scope stack clearing but I understand the why

}
// If there is no scope created from the envelope, we create our own noopSpan to make sure
// that we can close all scopes up until this position after exit.
AgentSpan span = new AgentTracer.NoopAgentSpan();
Copy link
Contributor

Choose a reason for hiding this comment

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

AgentTracer.noopSpan() would be better here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason that I construct a new span instance is that I want to minimize the chance that the cleaning code messes up the scope stack reference count for any scope that is active when we come in to this code, and using the AgentTracer.noopSpan() could easily do that since I can't distinguish between this span/scope and any that was active when calling this method.

@tylerbenson
Copy link
Contributor

tylerbenson commented Nov 13, 2020

Thanks for the additional comments/docs.

@bantonsson bantonsson merged commit 7c3ce96 into master Nov 13, 2020
@bantonsson bantonsson deleted the ban/akka-http-bah branch November 13, 2020 16:45
@github-actions github-actions bot added this to the 0.68.0 milestone Nov 13, 2020
@richardstartin richardstartin added the inst: others All other instrumentations label Nov 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
inst: others All other instrumentations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants