Skip to content
This repository has been archived by the owner on Jul 31, 2023. It is now read-only.

any plan to support in multi thread? #165

Open
billowqiu opened this issue Jun 24, 2018 · 17 comments
Open

any plan to support in multi thread? #165

billowqiu opened this issue Jun 24, 2018 · 17 comments

Comments

@billowqiu
Copy link

like this link Passing Segment Context between Threads in a Multithreaded Application

@malkia
Copy link

malkia commented Jul 30, 2018

I was also wondering this, as in our case we use TBB (actually ConCRT) spawning parallel_for and std::thread, but then tried to pass just the context - e.g. span.context() and it worked. Because after all the context is simply three "integer" values (of different sizes): thrace id, span id and options

@billowqiu
Copy link
Author

@malkia you can see this link context.
Java and go,c# have official implement,but cpp have none.

@malkia
Copy link

malkia commented Aug 4, 2018

I ended up with hacky, but workable (for my case) way: a thread_local s_activeSpanContext; that gets saved (in RAII object), then restored back. I also ave another special one s_rootSpanContext - created at the begining, which gets used if the !s_activeSpanContext.IsValid() (just to handle edge cases). It's not perfect, and I know I should be handing off actual context, but at the same time this means changing a lot of the code - basically adding an additional argument to a lot of places (still the proper thing to do, but need to evaluate a bit more and then do it).

@billowqiu
Copy link
Author

I do the same work

@bogdandrutu
Copy link

Hi all,

@g-easy will start working on adding a Context support soon. I will keep this issue updated.

@billowqiu
Copy link
Author

Hava any progress report? @bogdandrutu

@bogdandrutu
Copy link

bogdandrutu commented Sep 19, 2018

Hi billowqiu, @g-easy has a prototype we are trying to polish the public API for the moment. This week the PR should land.

@billowqiu
Copy link
Author

@bogdandrutu if i have multiple threads to handle incoming requests
May be use the "std::function<void()> Wrap(std::function<void()> fn) const;" to warp a task or new thread func can do the work to propagation the context.
But i want to know there are any best-practices?

@bogdandrutu
Copy link

@g-easy please follow on this

@g-easy
Copy link
Contributor

g-easy commented Oct 12, 2018

@billowqiu Best practices:

  • Always have the correct Context installed as the "current" context.
  • To set a current Span, use a WithSpan object.
  • Treat WithSpan as RAII - always stack-allocate it.
  • For callbacks, as you said, Wrap the callback while the correct context is installed.

e.g.

{
  WithSpan ws(span);
  executor.add(Context::Current()::Wrap(callback_fn));
}

Does this help?

@billowqiu
Copy link
Author

billowqiu commented Oct 12, 2018 via email

@g-easy
Copy link
Contributor

g-easy commented Oct 12, 2018

I'm trying to document this in #224

@billowqiu
Copy link
Author

I have a scene,like the diagram:
async-tracing
All request are async,server need wait recv rsp-3 then send req-4.
I must tracing all span as a one tracer:1->2->3->4->5->6,now i via hold client-span's parent span before request,and restore the span as Context's current span when response,to accomplish this task.

Undoubtedly, there are other ways of accomplishing the same thing (and maybe some of them are even easier).
@g-easy I want your advice.

@g-easy
Copy link
Contributor

g-easy commented Oct 17, 2018

To do this in an async way, I would suggest the server component does something like this:

void Server1(RequestFromClient req) {
  // Server receives trace context and tagging context from client.
  SpanContext parent_ctx = req.GetContext();
  TagMap tags = reg.GetTags();

  // Create the server-side span.
  Span span = Span::StartSpanWithRemoteParent("ServerSpan", parent_ctx);

  // Create a Context for this operation.
  WithSpan ws(span);
  WithTagMap wt(tags);

  // Send request to s1 with the current Context installed.
  // When that request completes, it will run a callback.
  DownstreamRequest req1(...);
  req1.Run(/* callback = */ Context::Current()::Wrap(Server2));
}

void Server2(ReplyFromDownstream reply1) {
  // The current Context is correct.
  Process(reply1);

  // Send request to s2 with the current Context installed.
  // When that request completes, it will run a callback.
  DownstreamRequest req2(...);
  req2.Run(/* callback = */ Context::Current()::Wrap(Server3));
}

void Server3(ReplyFromDownstream reply2) {
  // The current Context is correct.
  Process(reply2);
  ReplyToClient();

  // Operation is complete, remember to end the span.
  GetCurrentSpan().End();
}

Where DownstreamRequest::Run() sends the currently installed Context to the downstream service.

Does this make sense? Does it look okay to you? Do you think there is a better and/or simpler approach?

@g-easy
Copy link
Contributor

g-easy commented Oct 17, 2018

My plan for the gRPC integration is that the gRPC plugin will take care of:

  • Decoding the incoming trace and tagging contexts. (trace is already implemented)
  • Creating a server-side Span. (this is already implemented)
  • Installing the Span and tags in Context::Current().
  • Child RPCs take the current Context and propagate it on the wire.
  • End the server-side Span after sending the reply. (this is already implemented)

@billowqiu
Copy link
Author

To do this in an async way, I would suggest the server component does something like this:

void Server1(RequestFromClient req) {
  // Server receives trace context and tagging context from client.
  SpanContext parent_ctx = req.GetContext();
  TagMap tags = reg.GetTags();

  // Create the server-side span.
  Span span = Span::StartSpanWithRemoteParent("ServerSpan", parent_ctx);

  // Create a Context for this operation.
  WithSpan ws(span);
  WithTagMap wt(tags);

  // Send request to s1 with the current Context installed.
  // When that request completes, it will run a callback.
  DownstreamRequest req1(...);
  req1.Run(/* callback = */ Context::Current()::Wrap(Server2));
}

void Server2(ReplyFromDownstream reply1) {
  // The current Context is correct.
  Process(reply1);

  // Send request to s2 with the current Context installed.
  // When that request completes, it will run a callback.
  DownstreamRequest req2(...);
  req2.Run(/* callback = */ Context::Current()::Wrap(Server3));
}

void Server3(ReplyFromDownstream reply2) {
  // The current Context is correct.
  Process(reply2);
  ReplyToClient();

  // Operation is complete, remember to end the span.
  GetCurrentSpan().End();
}

Where DownstreamRequest::Run() sends the currently installed Context to the downstream service.

Does this make sense? Does it look okay to you? Do you think there is a better and/or simpler approach?

This is helpful.

@billowqiu
Copy link
Author

gRPC integration

look forward to see this integration 👍

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants