15:34 | <littledan> | hello! |
15:35 | <Chengzhong Wu> | Hey, dan! |
15:35 | <littledan> | Hey, good to be in touch with you again |
15:35 | <littledan> | I'm very excited about seeing this proposal move forward |
15:36 | <littledan> | Maybe Justin communicated this to you, my main feedback at this point is we should have a method enabling userspace queuing |
15:36 | <Chengzhong Wu> | Just for people who are not able to join the SES meeting yesterday like me, this is the recording of the discussion about the async context on the meeting: https://youtu.be/Y6hQLM08Ig8?t=891 The discussion is very helpful for people to catch up with the latest status of the proposal |
15:41 | <Chengzhong Wu> | yeah. It can be useful and I am willing to know more about the requirement of this method. We recently stripped the proposed API aiming to reduce the surface of the proposal and pushing it forward with the essential part. |
15:41 | <littledan> | yeah I am really happy about the direction of making the API as minimal as possible, and I like how this is currently done |
15:42 | <littledan> | I'd add one static method, AsyncContext.wrap (or, name tbd), which takes a callback, and eagerly stores a snapshot of all the async contexts, and returns a new callback which restores that snapshot through the duration of its execution |
15:43 | <littledan> | if we don't have this, then the platform has superpowers (to wrap its callbacks, not just promise reactions) which user code does not have |
15:44 | <littledan> | Kris gave the example of the Q promise library that he maintains. Maybe that's an edge case. But I imagine that this would come up for some larger RPC client libraries for example. |
15:45 | <littledan> | I'd add one static method, AsyncContext.wrap (or, name tbd), which takes a callback, and eagerly stores a snapshot of all the async contexts, and returns a new callback which restores that snapshot through the duration of its execution |
15:53 | <Chengzhong Wu> | Kris gave the example of the Q promise library that he maintains. Maybe that's an edge case. But I imagine that this would come up for some larger RPC client libraries for example. |
15:54 | <Chengzhong Wu> | A well defined method can definitely alleviates the burden for those libraries with complex use cases, and avoid restricting them to snapshot with asynchronous flows. |
15:55 | <littledan> | I'm suggesting that we do this as a single method rather than another class |
15:55 | <littledan> | so this could remain minimal-feeling |
15:56 | <littledan> | the thing is, Kris identified this gap in our SES call, so I think it's a bit difficult to delay for later; we may have others asking about this |
15:56 | <littledan> | also I think it makes it easier to explain what's going on, to have a concrete name for this wrapping, with a pseudo-js implementation on the slides |
15:57 | <littledan> | and then say, yeah this is what we apply to promise reactions as well as all built-in callbacks |
15:59 | <littledan> | I don't think taking advantage of promises is a working way through, as you might want a synchronous callback to restore the context (think addEventListener) |
15:59 | <littledan> | anyway it's fine to make this change after Stage 1 |
17:05 | <Kris Kowal> | Beyond Q, some other promise libraries also use my ASAP queueMicrotask shim, which is a more isolated example of a library that is obliged to wrap async context in a “user mode queue”. You can see the treatment we were obliged to integrate for Node.js domains https://github.com/kriskowal/asap/blob/master/asap.js#L44 |
17:06 | <Kris Kowal> | Dan’s right about RPC libraries. It’s common to have work shedding or prioritization middleware that are also user mode queues. |
17:09 | <Kris Kowal> | User mode queueing problems are both a reason for and against AsyncLocal. On the one hand, I don’t like being obliged to accommodate this kind of action-at-a-distance, where a library must account how it will compose with another library. But on the other hand, I would rather there be just one such thing to accommodate, and for it to be design well enough to address problems analogous to hygienic dynamic scope, as AsyncLocal does and domains did not. |
17:55 | <Andreu Botella> | As I mentioned on the WinterCG room, in a talk at BlinkOn yesterday, Yoav Weiss was talking about task attribution, which sounded somewhat similar to AsyncContext to me |
17:55 | <Andreu Botella> | The video for the talk is up at https://www.youtube.com/watch?v=NOWC6M0MS2o |
18:42 | <Justin Ridgewell> | Yeah, it does seem like task attribution is a subset of a AsyncContext |
18:42 | <Justin Ridgewell> | Yoav mentions in the last question in the video that task attribution did not have a performance penalty after they trimmed it down |
18:42 | <Justin Ridgewell> | That might be a good sign for us |
18:44 | <littledan> | so I guess it's not too soon to trace through the implementation and see if it could be extended... |
21:14 | <littledan> | just got off the phone with Mark Miller. He supports continuing the discussion here at Stage 1! |
21:14 | <Justin Ridgewell> | Whoot! |
21:14 | <littledan> | He agreed on a sort of stretch goal of having his analysis complete in 2 months, or in the worst case in 4 months, but this is the kind of analysis that would be more of a Stage 2 precondition |
21:15 | <littledan> | I don't think we should push him to go faster than that; that is a very reasonable pace IMO |
21:15 | <Justin Ridgewell> | I'll add it to the agenda as as advancement item |
21:15 | <littledan> | also I wanted to suggest that we get someone from MS in this discussion, like Ron Buckton was expressing support for this last time, right? |
21:15 | <littledan> | saying that there was a similar feature in C# that was very important |
21:16 | <Justin Ridgewell> | I don't remember, but I'll check the minutes later today |
21:41 | <littledan> | yeah, watching that BlinkOn video, I think citing exactly what Yoav is talking about would be a great way to explain the relevance to frontend |
21:41 | <littledan> | he's talking about both prioritization and recording timing |
21:52 | <littledan> | Stefan is actually suggesting that Yoav's work move in a direction which is more similar to this proposal, where there's no big tree of all the ancestors always tracked, but rather only certain pieces of code explicitly start a task with tracking |
21:52 | <Kris Kowal> | For RPC, this stuff is usually used for distributed tracing and measuring latency. Trace headers and TTL from inbound requests get captured as a deadline and inbound span on the context, which in turn gets written back as an updated TTL and span for dependent outbound requests. In Go, this is done explicitly with a context.Context, which is a slow but small KV store. |
21:53 | <Kris Kowal> | An immutable KV store, to be clear. |
21:54 | <littledan> | note that the talk links to this document: https://docs.google.com/document/d/1_m-h9_KgDMddTS2OFP0CShr4zjU-C-up64DwCrCfBo4/edit# |
21:54 | <littledan> | does someone want to take the action item to have a more detailed conversation about this relationship with Yoav? Andreu Botella ? |
21:55 | <Kris Kowal> | Tracing: https://opentelemetry.io/docs/concepts/observability-primer/#distributed-traces |
22:07 | <Justin Ridgewell> | I took his question to mean that a flat mapping should be used instead of a tree? |
22:08 | <Justin Ridgewell> | Which is exactly how AsyncLocal does it |
22:08 | <Justin Ridgewell> | (Though, you could store an array to track the ancestry if you wanted) |
22:09 | <littledan> | It sounded like Yoav's thing tracked the ancestry for all promise/callback reactions, so it could be traced backwards, not just forwards |
22:09 | <littledan> | could you talk through the relationship between AsyncLocal and AsyncContext? |
22:13 | <Justin Ridgewell> | The current proposal doesn't have an AsyncContext (though my personal one does). AsyncLocal is the same as an AsyncLocalStorage (allows propagating a value through an async call stack). AsyncTask is the snapshotting API (allows you to snapshot all AsyncLocal s at the time of the snapshot and run later a function with that snapshot). Together, those make up the "Async Context" APIs |
22:13 | <Justin Ridgewell> | My personal design has a AsyncContext namespace which holds a Value class (AsyncLocal ) and a Snapshot class (AsyncTask ) |
22:14 | <littledan> | why do we need all three of those classes? |
22:15 | <littledan> | Maybe we should debate the APIs here at https://github.com/legendecas/proposal-async-context/issues/9 |
22:15 | <Justin Ridgewell> | The current design just has 2, but my personal has a namespace class purely for organization |
22:15 | <littledan> | AsyncLocal and AsyncContext seem very similar |
22:15 | <littledan> | the differences are really superficial, right? |
22:16 | <Justin Ridgewell> | AsyncLocal and AsyncContext.Value are the same |
22:18 | <Justin Ridgewell> | If we offer a wrapping/snapshotting API, I think we need a distinction between AsyncLocal (or Value ) and the the namespace |
22:18 | <Justin Ridgewell> | Eg, we would need AsyncContext.Local class if we wanted to have a AsyncContext.wrap static function |
22:19 | <littledan> | yeah I'm proposing that we have no explicit snapshot, just wrap with the current stuff |
22:20 | <littledan> | also, yeah, I think run is a lot better than flat get/set |
22:20 | <littledan> | it's more structured |
22:20 | <littledan> | I'm not sure why we need the observer thing--you can just build that yourself |
22:21 | <littledan> | I think the API should be `class AsyncContext { run(value, callback); get(); static wrap(callback); } |
22:21 | <littledan> | you can express everything with just that |
22:21 | <littledan> | right? |
22:22 | <Justin Ridgewell> | I worry about the confusion of wrapping a particular local vs the all the locals, which is why I suggest that the class and wrap be on a common namespace |
22:23 | <Justin Ridgewell> | Though I guess it could work that way |
22:23 | <Justin Ridgewell> | Using the static vs instance distinction (and not offering the instance wrap) |
22:23 | <littledan> | well, I don't care about naming, but I think we should minimize the number of classes and things we reify |
22:23 | <littledan> | (I mean, I totally care about naming, but...) |
22:24 | <littledan> | the idea is you are always wrapping for all of the locals/contexts |
22:24 | <littledan> | we're just going for Stage 1, so we really don't need to be settled on any of this |
22:39 | <littledan> | If anyone wants to try to prototype AsyncContext in V8/Chrome, I'd start by looking at https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h;l=24 |