-
Notifications
You must be signed in to change notification settings - Fork 811
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
Is context-async-hooks suitable for production use? #2812
Comments
To be a little more helpful, I got this response on a related question posed on the nodejs repo: nodejs/diagnostics#194 (comment) It looks like Does |
The resons why the one is stable the the other is experimental is that async hooks API "leaks" node internal details. This results in an API surface which may change because of internal changes - so it would break the semver contract if moved to stable. Also node.js domain module uses async hooks internally. In short async_hooks are the base for stable Node.js APIs so they can't be removed completly. But they could be made internal to node.js.
I think every APM tool for node.js - commercial or free - use But to be clear: This "magic" functionality doesn't come for free. Using Otel and/or using AsyncHooks comes with CPU/Memory overhead (exact value depends on your app so please don't ask for numbers). Tracing is not for free. Otel itself can work without AsyncLocalStorage/AsyncHooks. It's the users responsiblitly to pass the current span around in their app. Clearly non of the autoinstrumentations will be useable then but they are not mandatory. |
Thanks! I appreciate that you don't have numbers on the overhead. I would
be curious if you would consider it a major impact in the context of a web
application. I assume if it was really ugly it would somewhat defeat the
purpose of OpenTelemetry as it would mostly be measuring its own impact.
…On Wed, Mar 2, 2022 at 4:59 PM Gerhard Stöbich ***@***.***> wrote:
AsyncLocalStoreage is built on top of async hooks. From Otel point of
view one works as good as the other.
The resons why the one is stable the the other is experimental is that
async hooks API "leaks" node internal details. This results in an API
surface which may change because of internal changes - so it would break
the semver contract if moved to stable.
Also node.js domain module uses async hooks internally.
In short async_hooks are the base for stable Node.js APIs so they can't be
removed completly. But they could be made internal to node.js.
AsyncResource is a different story and for a different use case. Otel
uses AsyncLocalStore to propage transactional context. User libraries
which do stuff that breaks this as of now (e.g. resource reuse/pooling,..)
should use AsyncResource to avoid this breakage.
I think every APM tool for node.js - commercial or free - use AsyncHooks
and/or AsyncLocalStorage since years. As millions applications in
production use such tools the question regarding production readyness is
most likely answered :o).
But to be clear: This "magic" functionality doesn't come for free. Using
Otel and/or using AsyncHooks comes with CPU/Memory overhead (exact value
depends on your app so please don't ask for numbers). Tracing is not for
free.
Otel itself can work without AsyncLocalStorage/AsyncHooks. It's the users
responsiblitly to pass the current span around in their app. Clearly non of
the autoinstrumentations will be useable then but they are not mandatory.
—
Reply to this email directly, view it on GitHub
<#2812 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAH27OQ6UEPAAZ6WQUGHDDU57QE5ANCNFSM5PYKLRFA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
--
THOMAS BOUTELL | CHIEF TECHNOLOGY OFFICER
APOSTROPHECMS | apostrophecms.com | he/him/his
|
In a promise-heavy application, it's not uncommon to see 20-30% cpu overhead when turning on async_hooks or AsyncLocalStorage. Possibly more in extreme cases. It's a lot less than the 200%+ it used to be, but still noticeable. |
Ah, I see. Very important to know. I will definitely avoid bringing it into
production code situations in non-optional ways then. Thanks for the advice.
When just using the ***@***.***/api` module for Open Telemetry
compatibility that's not an issue, correct? It's only an issue if the SDK
is actually in play?
…On Thu, Mar 3, 2022 at 2:46 AM Stephen Belanger ***@***.***> wrote:
In a promise-heavy application, it's not uncommon to see 20-30% cpu
overhead when turning on async_hooks or AsyncLocalStorage. Possibly more in
extreme cases. It's a lot less than the 200%+ it used to be, but still
noticeable.
—
Reply to this email directly, view it on GitHub
<#2812 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAH27IQ3B6IVPSTT45JVOLU6BU35ANCNFSM5PYKLRFA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
--
THOMAS BOUTELL | CHIEF TECHNOLOGY OFFICER
APOSTROPHECMS | apostrophecms.com | he/him/his
|
The API on its own does not enable a context manager and therefore does not enable async_hooks or AsyncLocalStorage. You can also manually propagate context without enabling a context manager if you never register the SDK as a global instance, but then as @Flarna mentioned you will not be able to use any auto-instrumentations. |
Great! I figured but this is helpful to have confirmation on. Appreciate the info. In ApostropheCMS, which I'm working on adding OpenTelemetry support to, we potentially could do it the other way and still get a decent percentage of the information, but the auto instrumentation is just far too useful and our clients should be fine with enabling OpenTelemetry SDK in environments where they need the data and don't mind the performance hit until they are done gathering it. I'm closing my ticket because my question has been more than answered. Maybe this deserves a note in documentation? |
@Flarna Can you please expand a bit more on this ? I've been trying to resolve a related bug for quite some time. Basically the span created during first request is getting attached even in subsequent requests Using |
@gmreads I hacked a small reproducer which includes the creation of import * as http from "http";
import { trace } from "@opentelemetry/api";
import { AsyncResource } from "async_hooks";
const port = 8600;
const tracer = trace.getTracer("sender");
interface Msg {
msg: string;
res?: AsyncResource;
}
const msgQueue: Msg[] = [];
function sendMessage(msg: string): void {
msgQueue.push({
msg,
// if this is removed all three doSendMessage are in the same trace instead
res: new AsyncResource("my.sendMessage")
});
if (msgQueue.length === 1) {
doSendMessage();
}
}
function doSendMessage(): void {
if (msgQueue.length === 0) return;
const doIt = () => {
const span = tracer.startSpan("doSendMessage");
// simulate that this takes a while
setTimeout(() => {
msgQueue.shift();
span.end();
process.nextTick(doSendMessage);
}, 1000);
}
const msg = msgQueue[0];
if (msg.res != null) {
msg.res.runInAsyncScope(doIt);
} else {
doIt();
}
}
let cnt = 0;
http.createServer(function onRequest(req, res) {
sendMessage(`msg ${cnt++}`);
res.end();
}).listen(port);
http.get({ port });
http.get({ port });
http.get({ port }); |
@dyladan I am passing context manually and not using auto-instrumentation, but I can still see that async_hooks are being used from tracer.js from memory dump of production service.
I am passing context manually like this, can you tell if there is some other better way? I don't want to incur any penalty for async hooks, I am ready to write some extra custom code. |
Most likely the context manager is being enabled when you register the SDK. It is automatically enabled when you register the tracing sdk with the global API. |
If you don't want to use a You should either use |
can you show some example of how to do that?
I am not registering SDK, just batchExporter |
I think something like this: const myRequestTracer = api.trace.getTracer("request-instrumentation");
const myWorkTracer = api.trace.getTracer("work-instrumentation");
function myRequestHandler(req: any) {
const inCtx = api.propagation.extract(api.ROOT_CONTEXT, req);
const reqSpan = myRequestTracer.startSpan("some-request", { kind: api.SpanKind.SERVER }, inCtx);
const reqCtx = api.trace.setSpan(inCtx, reqSpan);
doSomeWork(reqCtx);
reqSpan.end();
}
function doSomeWork(ctx: api.Context) {
const workSpan = myWorkTracer.startSpan("some-work", {}, ctx);
const workCtx = api.trace.setSpan(ctx, workSpan);
doSomeOtherWork(workCtx);
workSpan.end();
}
function doSomeOtherWork(ctx: api.Context) {
const workSpan = myWorkTracer.startSpan("other-work", {}, ctx);
const workCtx = api.trace.setSpan(ctx, workSpan);
//... call other functions, pass workCtx to allow them to pick it up
workSpan.end();
} ==> never use Clearly this results in quite some work and impacts the whole implementation. And please note that all auto instrumentations rely one |
Hey @Flarna, Thank you very much. can you also suggest how to mark a trace as a parent of another trace, if we only have the trace_id of child? |
traces are separate entities. The concept of a trace is a parent of another trace does not exist in opentelemetry to my knowledge. |
My mistake, I meant spans itself. My use case looks like this - I am getting trace_id of span over the network, and I want this span to become child of new span I am creating. |
You have to put the received traceId/spanId into a SpanContext and the SpanContext into a Context and this Context should be given as argument to startSpan. |
you mean something like this, right?
Also, I will need both traceId and spanId to create a span context, right? |
Yes, you need traceId and spanId to identify a span. By the way, why do you hijack this long closed issue for this new topic? |
No particular reason. I can delete these if you want. |
no need to delete, but please create a fresh issue if more help is needed. |
Hello,
I'm wondering if context-async-hooks is considered suitable for production use. I see that it enables the automatic instrumentation of traces for mongodb and other libraries that don't receive the express "req" directly, which is absolutely amazing, but async_hooks is still marked "1 - experimental" in the node.js documentation (after several years, yes).
Thanks!
The text was updated successfully, but these errors were encountered: