opencensus-cpp icon indicating copy to clipboard operation
opencensus-cpp copied to clipboard

any plan to support in multi thread?

Open billowqiu opened this issue 6 years ago • 17 comments

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

malkia avatar Jul 30 '18 20:07 malkia

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

billowqiu avatar Jul 31 '18 02:07 billowqiu

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).

malkia avatar Aug 04 '18 00:08 malkia

I do the same work

billowqiu avatar Aug 07 '18 08:08 billowqiu

Hi all,

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

bogdandrutu avatar Sep 05 '18 16:09 bogdandrutu

Hava any progress report? @bogdandrutu

billowqiu avatar Sep 17 '18 14:09 billowqiu

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

bogdandrutu avatar Sep 19 '18 21:09 bogdandrutu

@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?

billowqiu avatar Oct 11 '18 14:10 billowqiu

@g-easy please follow on this

bogdandrutu avatar Oct 12 '18 03:10 bogdandrutu

@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?

g-easy avatar Oct 12 '18 03:10 g-easy

thanks

发自我的 iPhone

在 2018年10月12日,上午11:47,easy [email protected] 写道:

@billowqiu Best practices:

Always have the correct Context installed. 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?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

billowqiu avatar Oct 12 '18 04:10 billowqiu

I'm trying to document this in #224

g-easy avatar Oct 12 '18 05:10 g-easy

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.

billowqiu avatar Oct 16 '18 14:10 billowqiu

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 avatar Oct 17 '18 04:10 g-easy

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)

g-easy avatar Oct 17 '18 04:10 g-easy

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 avatar Oct 17 '18 11:10 billowqiu

gRPC integration

look forward to see this integration 👍

billowqiu avatar Oct 17 '18 16:10 billowqiu