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
this design might be a little heavy in that it requires allocating a new closure. But, on the positive, it avoids the need to reify the snapshot in a more complicated way
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.
True, the snapshotting API is very helpful to those larger libraries. I had an example shows how it can be achieved with built-in Promise: https://github.com/legendecas/proposal-async-context/pull/8#issuecomment-1246309357 (limited to asynchronous flows, yes :( ).
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 AsyncLocals 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