Maintaining state across async/await boundaries.

40 views
Skip to first unread message

b.clem...@gmail.com

unread,
Mar 13, 2017, 5:06:36 PM3/13/17
to v8-dev
Hello,

I am trying to figure out how to maintain state associated with async functions across await boundaries. We currently have instrumentation for promises which allows us to maintain state in each step of the promise chain, unfortunately for us async/await is implemented using only internal functions with no exposure to JS-land. This makes it impossible for us to instrument it without requiring either user or V8 code modification. The app pasted below shows a simple use case where using promises directly maintains the correct state while using async/await loses it.

I was exploring this using Node 7.7.1 which uses V8 5.5.372 and found a naive solution (see attached await.patch). This shows the spirit of what we would like to do. We need async/await to use a JS-land function to add its continuation, or otherwise expose some kind of hook, so that we can get in there and maintain our state. I looked into upgrading my patch to the latest V8 before bringing it here to discuss, but it looks like async/await was completely refactored and the JS function AsyncFunctionAwait which my patch modified became the C++ function AsyncBuiltinsAssembler::Await (in builtins-async.cc) and I am not at all familiar with V8's assemblers.

I would like to know what is the best way forward to get a change into V8 making async/await instrumentable?

-Bryan

'use strict';

// Some environment variables to make newrelic keep quiet.
process.env.NEW_RELIC_NO_CONFIG_FILE = true;
process.env.NEW_RELIC_APP_NAME = 'async await example';
process.env.NEW_RELIC_LICENSE_KEY = 'this is not a license';

var newrelic = require('newrelic');
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');

var pkgPath = path.resolve(__dirname, './package.json');

// A simple async function to show transaction state loss.
var asyncAwait = newrelic.createBackgroundTransaction('asdf', async function() {
  console.log('async', !!newrelic.agent.tracer.segment);
  await fs.readFileAsync(pkgPath, 'utf8'); // <-- State lost here.
  console.log('async', !!newrelic.agent.tracer.segment);

  // Just to show pure promises keeps state.
  promises();
});

// Same thing but just with promises.
var promises = newrelic.createBackgroundTransaction('asdf', function() {
  console.log('promises', !!newrelic.agent.tracer.segment);
  return fs.readFileAsync(pkgPath, 'utf8').then(() => {
    console.log('promises', !!newrelic.agent.tracer.segment);
    process.exit(0);
  });
});

asyncAwait();
await.patch

Yang Guo

unread,
Mar 13, 2017, 5:11:40 PM3/13/17
to v8-dev

Does PromiseHook not offer what you need? Take a look in v8/include/v8.h and look for PromiseHook.

Cheers,

Yang


--
--
v8-dev mailing list
v8-...@googlegroups.com
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to v8-dev+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Bryan Clement

unread,
Mar 13, 2017, 6:49:46 PM3/13/17
to v8-...@googlegroups.com
Ah! This looks like it'll be perfect for the use case. Thanks!
> You received this message because you are subscribed to a topic in the
> Google Groups "v8-dev" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/v8-dev/lKsMY3Nmwg4/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
Reply all
Reply to author
Forward
0 new messages