-
Notifications
You must be signed in to change notification settings - Fork 73
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
[WIP] added ThreadLocalScopeManager #88
base: master
Are you sure you want to change the base?
[WIP] added ThreadLocalScopeManager #88
Conversation
Thx for the PR. Could you please explain in more detail why the builtin |
@cwe1ss yeah, in this case it's that all of the work for a "logical" operation all occurs inside the same thread, but executed by disconnected parts in an event-processing pipeline. So there's no real async flow across multiple contexts and threads. But each thread does need to maintain its own isolated context since you might have multiple operations like this executing in parallel. The
I wouldn't use this implementation for a traditional TPL application where a single logical operation can be executed across multiple threads (i.e. each time there's an Nor would I use an This use case is probably less common than the |
I still do not really understand the usability of this because:
Given these constraints, using |
That's not really for you to judge, though. The goal of this project is to provide infrastructure for being able to do tracing, not to be perspective in how to do it in all cases. To give you some background, I'm one of the authors of Akka.NET, an implementation of the actor model in .NET. Actors are a concurrency primitive and eliminate the need for things like locks, critical regions, and even In a project I'm developing we use OpenTracing-enabled tracers to correlate messages between actors, and each actor runs on its own thread and holds onto that thread while it processes a burst of N messages at a time. While that actor processes messages it might do other things synchronously in that processing scope, such as emit log events and so forth. Logging infrastructure and actors are disconnected by implementation, but connected temporally at the time of event publication - therefore using ThreadLocal / ThreadStatic parking is an extremely common technique inside concurrent systems. It's also how we do things like buffer pooling inside DotNetty. So I'm intending this PR for developers with use cases like mine, where they are familiar with the execution model of their application and aren't working inside the TPL. |
Hey Aaron, Might I suggest putting this into its own Contrib library? You’re totally right that code is code and if it has a use, it’s useful! I lean personally towards caution when anywhere near async/await though since many professional programmers get confused in that area (again, a what is rather than a what should be). Would this be an okay compromise? I think Christian could help get an official repo set up. (Other random thoughts as I composed this: thread seems more often to be used by framework developers than application developers, but I’ve come to realize most of the tracing code, especially propagation, won’t be used/written by application developers directly. If that’s the hard philosophy of OT, then I’d be okay keeping it here, but the warnings that would need to accompany it still make me feel safer having it factored out) |
I know who you are. You are doing great OSS work and I really appreciate discussing this with you. Any new topic is an opportunity for me to learn something new! I hope I'm not annoying you but I want to make sure I properly understand this (and the risks associated with it). Hopefully, someone else will jump in as well and provide additional feedback! I understand how the synchronous execution of the actor model works and I understand that your use case "which actor owns this thread right now?" can be done using My concern still is that Akka.NET is just one part of an application and that using one global IScopeManager that's based on Let's take an example: We have OpenTracing.Contrib.NetCore which auto-instruments .NET Core applications by subscribing to These actions usually involve async/await under the hood. If someone using Akka.NET and this proposed global scopemanager, also uses the contrib project and if she/he does e.g. a single HttpClient call/SqlClient call/EF Core call etc somewhere in the application, that instrumentation might be broken. Do you know how much slower using the existing Another potentially interesting thing is that if the framework/library is under your control, you could also try to completely avoid using a scope manager by storing the parent/root span in some kind of context dictionary (e.g. HttpContext.Items in ASP.NET Core). This obviously comes at a cost because you manually have to set the parent whenever you create a new span, but it would probably give you the best performance. |
A note on the code itself: I think it would be better to call this "ThreadStaticScopeManager" as |
@ndrwrbgs @cwe1ss this
It works for Java developers and for C# developers who already use thread-local storage for other stuff, because it's the responsibility of the developer to wrap up whatever they're working on before their turn on the thread cycles onto a new piece of work, which will clear the active scope back to whatever it was before. It's up to the end-user or the library maintainer to use the right tool for the job. I had to abandon one OpenTracing implementation that tried to use Don't assume that there's going to be a single paradigm for any given platform - |
My two cents:
I think this would be fine (as an example:
Agreed with that. Still, I'm curious about those aforementioned problems with |
The default in opentracing-java uses Java's
Could you elaborate on that? Which implementation do you mean? Have you used the built-in One thing I still couldn't read/understand from your comments is the following: Does using the built-in |
fyi, I'm currently not adding this to the next release (targeting next week) for the following reasons:
I'd also be interested in a benchmark that compares AsyncLocal with other mechanisms. Anyone interested in this? I do not really have much time right now. |
Need to add some specs for this still, but since I was already working on this in another project I thought I'd contribute it back.
When I'm using OpenTracing in combination with Akka.NET or DotNetty, it's been really helpful to me to have a
ThreadStatic
means of looking up active scopes since the operations inside one actor message process or the processing of one parsed message on the socket occur within the same thread but the handlers for those operations might be implemented in different, disconnected stages potentially. Propagating active scope data down to all of the handlers viaThreadStatic
storage has been very helpful, rather than trying to do it through something I implemented at the application layer.