Feature Proposal - async variables (like thread local storage from C)

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Feature Proposal - async variables (like thread local storage from C)

Guy Margalit
Hey

It's my first attempt to contribute to ECMAScript so would love your feedback if I better off change how I go about it.

The problem I want to highlight is that besides argument passing it is impossible to have any information associated with a flow of async code as any async code will create its own context and will only get the arguments passed to it from its caller.

A simple use case for information that is not easy to pass as arguments is "transaction ID" where unique IDs are generated at the beginning of a transaction, and propagate that ID to any function that is being called as part of that transaction.

However as a javascript codebase grows, it becomes hard to introduce new project-wide information such as transaction ID when the only mechanism to pass such information is function argument passing, and this becomes impossible as many times you are using other libraries of code inside the code stack, and one cannot change all the code just to add arguments.

Few examples:
  • Identify HTTP requests and print that a request ID in every log print that was fan-out from that request.
  • Find how many database calls one async flow created.
  • Keep pools of resources (buffers / connections / tokens) per request.
In C/C++ one can use thread local storage (https://en.wikipedia.org/wiki/Thread-local_storage) to keep information that automatically switches with the threads context switch.

I imagine a similar mechanism, in the spirit of javascript - define a variable as `async`:

```
async var name = <initial value>;
```

Async variables will be saved and restored automatically when the context is resumed. It should probably have a more precise definition in ECMAScript terms.

```
async var reqid = '';

http_server.on('request', handler);

async function handler(req, res) => {

  // setting async variable concurrently be different requests 
  // will retain the generated value for the other calls made by this context (sync or async).
  reqid = `REQ-${uuid()}`;

  await load_data_from_db(req);
  await send_analytics_info(req);
  send_reply(res);
});

async function load_data_from_db(req) {
  console.log(`${reqidload_data_from_db`);
  try {
     ...
  } catch (err) {
    ...
    if (err.code === 'ACCESS_DENIED') {
      console.log(`${reqid} ACCESS_DENIED. this incident will be reported.`);
    }
  }
}

async function send_analytics_info(req) {
  console.log(`${reqidsend_analytics_info`);
  ...
  analytics.send(reqid, ...);
  ...
}

function send_reply(res) {
  console.log(`${reqidsend_analytics_info`);
  ...
  res.setHeader('reqid', reqid); // for supportability
  res.send(...);
  ...
}
```

Technically speaking, async variables should be replaced whenever there is a context switch - either when a sync stack returns to the top stack level, or when an async function returns from await.

However, this means that every registration of a function for later, such as setTimeout(func), should also keep the async variables which sounds difficult.

Would be great to hear if you think this is valuable and feasible, or maybe there's a way to design this with less overhead.

Thanks,
Guy Margalit


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Feature Proposal - async variables (like thread local storage from C)

Thomas Grainger
I'm pretty sure this is Zones

On 11 Mar 2018 15:35, "Guy Margalit" <[hidden email]> wrote:
Hey

It's my first attempt to contribute to ECMAScript so would love your feedback if I better off change how I go about it.

The problem I want to highlight is that besides argument passing it is impossible to have any information associated with a flow of async code as any async code will create its own context and will only get the arguments passed to it from its caller.

A simple use case for information that is not easy to pass as arguments is "transaction ID" where unique IDs are generated at the beginning of a transaction, and propagate that ID to any function that is being called as part of that transaction.

However as a javascript codebase grows, it becomes hard to introduce new project-wide information such as transaction ID when the only mechanism to pass such information is function argument passing, and this becomes impossible as many times you are using other libraries of code inside the code stack, and one cannot change all the code just to add arguments.

Few examples:
  • Identify HTTP requests and print that a request ID in every log print that was fan-out from that request.
  • Find how many database calls one async flow created.
  • Keep pools of resources (buffers / connections / tokens) per request.
In C/C++ one can use thread local storage (https://en.wikipedia.org/wiki/Thread-local_storage) to keep information that automatically switches with the threads context switch.

I imagine a similar mechanism, in the spirit of javascript - define a variable as `async`:

```
async var name = <initial value>;
```

Async variables will be saved and restored automatically when the context is resumed. It should probably have a more precise definition in ECMAScript terms.

```
async var reqid = '';

http_server.on('request', handler);

async function handler(req, res) => {

  // setting async variable concurrently be different requests 
  // will retain the generated value for the other calls made by this context (sync or async).
  reqid = `REQ-${uuid()}`;

  await load_data_from_db(req);
  await send_analytics_info(req);
  send_reply(res);
});

async function load_data_from_db(req) {
  console.log(`${reqidload_data_from_db`);
  try {
     ...
  } catch (err) {
    ...
    if (err.code === 'ACCESS_DENIED') {
      console.log(`${reqid} ACCESS_DENIED. this incident will be reported.`);
    }
  }
}

async function send_analytics_info(req) {
  console.log(`${reqidsend_analytics_info`);
  ...
  analytics.send(reqid, ...);
  ...
}

function send_reply(res) {
  console.log(`${reqidsend_analytics_info`);
  ...
  res.setHeader('reqid', reqid); // for supportability
  res.send(...);
  ...
}
```

Technically speaking, async variables should be replaced whenever there is a context switch - either when a sync stack returns to the top stack level, or when an async function returns from await.

However, this means that every registration of a function for later, such as setTimeout(func), should also keep the async variables which sounds difficult.

Would be great to hear if you think this is valuable and feasible, or maybe there's a way to design this with less overhead.

Thanks,
Guy Margalit


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Feature Proposal - async variables (like thread local storage from C)

Guy Margalit
Yes, it is. Thanks for referring me to the right point. Anything I can do to help advance that proposal?

On Mar 11, 2018 5:40 PM, "Thomas Grainger" <[hidden email]> wrote:
I'm pretty sure this is Zones

On 11 Mar 2018 15:35, "Guy Margalit" <[hidden email]> wrote:
Hey

It's my first attempt to contribute to ECMAScript so would love your feedback if I better off change how I go about it.

The problem I want to highlight is that besides argument passing it is impossible to have any information associated with a flow of async code as any async code will create its own context and will only get the arguments passed to it from its caller.

A simple use case for information that is not easy to pass as arguments is "transaction ID" where unique IDs are generated at the beginning of a transaction, and propagate that ID to any function that is being called as part of that transaction.

However as a javascript codebase grows, it becomes hard to introduce new project-wide information such as transaction ID when the only mechanism to pass such information is function argument passing, and this becomes impossible as many times you are using other libraries of code inside the code stack, and one cannot change all the code just to add arguments.

Few examples:
  • Identify HTTP requests and print that a request ID in every log print that was fan-out from that request.
  • Find how many database calls one async flow created.
  • Keep pools of resources (buffers / connections / tokens) per request.
In C/C++ one can use thread local storage (https://en.wikipedia.org/wiki/Thread-local_storage) to keep information that automatically switches with the threads context switch.

I imagine a similar mechanism, in the spirit of javascript - define a variable as `async`:

```
async var name = <initial value>;
```

Async variables will be saved and restored automatically when the context is resumed. It should probably have a more precise definition in ECMAScript terms.

```
async var reqid = '';

http_server.on('request', handler);

async function handler(req, res) => {

  // setting async variable concurrently be different requests 
  // will retain the generated value for the other calls made by this context (sync or async).
  reqid = `REQ-${uuid()}`;

  await load_data_from_db(req);
  await send_analytics_info(req);
  send_reply(res);
});

async function load_data_from_db(req) {
  console.log(`${reqidload_data_from_db`);
  try {
     ...
  } catch (err) {
    ...
    if (err.code === 'ACCESS_DENIED') {
      console.log(`${reqid} ACCESS_DENIED. this incident will be reported.`);
    }
  }
}

async function send_analytics_info(req) {
  console.log(`${reqidsend_analytics_info`);
  ...
  analytics.send(reqid, ...);
  ...
}

function send_reply(res) {
  console.log(`${reqidsend_analytics_info`);
  ...
  res.setHeader('reqid', reqid); // for supportability
  res.send(...);
  ...
}
```

Technically speaking, async variables should be replaced whenever there is a context switch - either when a sync stack returns to the top stack level, or when an async function returns from await.

However, this means that every registration of a function for later, such as setTimeout(func), should also keep the async variables which sounds difficult.

Would be great to hear if you think this is valuable and feasible, or maybe there's a way to design this with less overhead.

Thanks,
Guy Margalit


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss