diff --git a/History.md b/History.md index f741174..7ce6883 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +7.1.0 / 2020-03-31 +================== + + * Make integration requests compatible with async_hooks by creating an AsyncResource for each request + 7.0.0 / 2020-03-31 ================== diff --git a/package.json b/package.json index f9c38c9..eade43e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@segment/integration", - "version": "7.0.0", + "version": "7.1.0", "description": "Segment.io integration base prototype", "main": "./src", "keywords": [], diff --git a/src/proto.js b/src/proto.js index 07b2a84..de97573 100644 --- a/src/proto.js +++ b/src/proto.js @@ -3,6 +3,7 @@ * Module dependencies. */ +const { AsyncResource } = require('async_hooks') var ResourceLockedError = require('./errors').ResourceLockedError var ValidationError = require('./errors').Validation var normalize = require('to-no-case') @@ -242,6 +243,24 @@ exports.request = function (method, path) { return fn(err, res) } + // Captures the current async context by creating an async resource for the + // superagent request. + // + // This is necessary because async_hooks does not follow "Thenable" types, + // depsite promises being compatible with them. The "then" method is invoked + // asynchronously on a "nextTick", where the async context that originally + // created the "Thenable" object has been lost. + // + // By creating the async resource here and overriding the "then" method, we + // bind the request object to the async execution context, and can restore + // it via a call to runInAsyncScope in the overriden "then" function. + const asyncResource = new AsyncResource('com.segment.integration.Request') + const thenFunction = Reflect.get(req, 'then') + const thenAsyncHook = function then (resolve, reject) { + return asyncResource.runInAsyncScope(thenFunction, this, resolve, reject) + } + + req.then = thenAsyncHook return req }