2. Agenda
• Problems caused by the event loop of Node.js
• Solving problems with AsyncListener API
• How AsyncListener works
3. Mo Node Mo Problems
Things that are hard with the event loop:
• Having a request ID propagate throughout code
• Short stack traces from errors in async functions
• Profiling async functions is painful
4. Mo Node Mo Problems
Things that are hard with the event loop:
• Having a request ID propagate throughout code
• Short stack traces from errors in async functions
• Profiling async functions is painful
9. Mo Node Mo Problems
Things that are hard with the event loop:
• Having a request ID propagate throughout code
• Short stack traces from errors in async functions
• Profiling async functions is painful
10. Short Stack Traces
users.save(attrs, function(error, userId) {
if (error) { return callback(error); }
users.load(userId, function(error, user) {
if (error) { return callback(error); }
mailers.sendWelcome(user, function(error) {
if (error) { return callback(error); }
});
});
});
11. Short Stack Traces
users.save(attrs, function(error, userId) {
if (error) { return callback(error); }
users.load(userId, function(error, user) {
if (error) { return callback(error); }
mailers.sendWelcome(user, function(error) {
if (error) { return callback(error); }
});
});
});
12. Short Stack Traces
Error: ER_PARSE_ERROR You have an error in your SQL syntax...
at getError (lib/models/database.js:479:24)
at Query._callback (lib/models/database.js:110:34)
at Query.Sequence.end (node_modules/mysql/lib/protocol/sequences/Sequence.js:75:24)
at Query.ErrorPacket (node_modules/mysql/lib/protocol/sequences/Query.js:93:8)
at Protocol._parsePacket (node_modules/mysql/lib/protocol/Protocol.js:192:24)
at Parser.write (node_modules/mysql/lib/protocol/Parser.js:62:12)
at Socket.ondata (stream.js:51:26)
at Socket.emit (events.js:117:20)
at Socket. (_stream_readable.js:748:14)
at Socket.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:410:10)
at emitReadable (_stream_readable.js:406:5)
at readableAddChunk (_stream_readable.js:168:9)
at Socket.Readable.push (_stream_readable.js:130:10)
at TCP.onread (net.js:528:21)
13. Short Stack Traces
Error: ER_PARSE_ERROR You have an error in your SQL syntax...
at getError (lib/models/database.js:479:24)
at Query._callback (lib/models/database.js:110:34)
at Query.Sequence.end (node_modules/mysql/lib/protocol/sequences/Sequence.js:75:24)
at Query.ErrorPacket (node_modules/mysql/lib/protocol/sequences/Query.js:93:8)
at Protocol._parsePacket (node_modules/mysql/lib/protocol/Protocol.js:192:24)
at Parser.write (node_modules/mysql/lib/protocol/Parser.js:62:12)
at Socket.ondata (stream.js:51:26)
at Socket.emit (events.js:117:20)
at Socket. (_stream_readable.js:748:14)
at Socket.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:410:10)
at emitReadable (_stream_readable.js:406:5)
at readableAddChunk (_stream_readable.js:168:9)
at Socket.Readable.push (_stream_readable.js:130:10)
at TCP.onread (net.js:528:21)
14. Mo Node Mo Problems
Things that are hard with the event loop:
• Having a request ID propagate throughout code
• Short stack traces from errors in async functions
• Profiling async functions is painful
20. Where Do These Problems
Come From?
• Asynchronicity and the Event Loop
21. Where Do These Problems
Come From?
• Asynchronicity and the Event Loop
process
.nextTick(myFunc);
Event Loop myFunc Executed
22. Where Do These Problems
Come From?
• Asynchronicity and the Event Loop
process
.nextTick(myFunc);
Event Loop myFunc Executed
State and stack lost
23. Where Do These Problems
Come From?
• Asynchronicity and the Event Loop
process
.nextTick(myFunc);
Event Loop myFunc Executed
State and stack lost
24. How do we solve it?
• AsyncListener API
process.createAsyncListener
process.addAsyncListener
process.removeAsyncListener
• Available in Node >0.11.9; Polyfill for Node <= 0.11.8
31. AsyncListener
process
.nextTick(myFunc);
Event Loop myFunc Executed myFunc returns
myFunc errors
Some time later…
Create Callback Before Callback After / Error Callback
41. continuation-local-storage
Bag Of Holding
(CLS makes this)
async function 1
async function 2
async function 3
Each async chain gets it’s own bag
42. continuation-local-storage
Bag Of Holding
(CLS makes this) async function 1
async function 2
async function 3
async function 1
async function 2
async function 3
Each async chain gets it’s own bag
44. Propagate Request IDs
Want
2013-12-20T18:12:20.037Z - Executing SQL BEGIN statement
2013-12-20T18:12:20.385Z - Values and label are different datasources
2013-12-20T18:12:20.530Z - Executing SQL COMMIT statement
2013-12-20T18:12:20.531Z - Picking filter implementation: DEFAULT
45. Propagate Request IDs
Want
2013-12-20T18:12:20.037Z - Executing SQL BEGIN statement -
3dd4ee56-e8e3-4270-bda7-90c5ee409782
2013-12-20T18:12:20.385Z - Values and label are different datasources -
02671cb7-d22b-4a3e-9bb8-282608999688
2013-12-20T18:12:20.530Z - Executing SQL COMMIT statement -
3dd4ee56-e8e3-4270-bda7-90c5ee409782
2013-12-20T18:12:20.531Z - Picking filter implementation: DEFAULT -
02671cb7-d22b-4a3e-9bb8-282608999688
46. Propagate Request IDs
How?
• Create continuation-local-storage namespace
• Middleware to create/assign Request ID
• Look up Request ID when logging
48. Propagate Request IDs
Create namespace
var createNamespace =
require(‘continuation-local-storage')
.createNamespace;
var namespace = createNamespace('com.datahero');
49. Propagate Request IDs
Create namespace
var createNamespace =
require(‘continuation-local-storage')
.createNamespace;
var namespace = createNamespace('com.datahero');
Once at top of application
73. Profiling Async Code
process
.nextTick(myFunc);
Event Loop myFunc Executed myFunc returns
myFunc errors
Record Time
Compute / Record
Time Difference
74. Profiling Async Code
Use async-profile
• https://www.npmjs.org/package/async-profile
76. Profiling Async Code
Use async-profile
• https://www.npmjs.org/package/async-profile
var AsyncProfile =
require('async-profile')
function () {
…
new AsyncProfile();
doAsyncWork();
}
77. Summary
• Problems
• Can we solve them? Yes we can!
• Use continuation-local-storage, stackup, and
async-profile
• All built on AsyncListener
• Prepare for more tracing packages