Created
August 23, 2012 01:50
-
-
Save lancejpollard/3431201 to your computer and use it in GitHub Desktop.
node.js domain module async error handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(req, res, next) -> | |
# req and res are also created in the scope of serverDomain | |
# however, we'd prefer to have a separate domain for each request. | |
# create it first thing, and add req and res to it. | |
reqd = domain.create() | |
reqd.add(req) | |
reqd.add(res) | |
reqd.on "error", (er) -> | |
console.error "Error", er, req.url | |
try | |
res.writeHead 500 | |
res.end "Error occurred, sorry." | |
res.on "close", -> | |
# forcibly shut down any other things added to this domain | |
reqd.dispose() | |
catch er | |
console.error "Error sending 500", er, req.url | |
# tried our best. clean up anything remaining. | |
reqd.dispose() | |
next() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here are some usefule links:
I got excited about that "api/domain" link because it's new and seems to solve the problem. But after digging into it (nodejs/node-v0.x-archive#3908) I'm still at the same place as where I started...
The issue is you essentially have to pass around the "context" in every method in your app, which means rewriting every method in tower.
Here is why (briefly):
When an HTTP request comes in, you can create a "domain" for it and attach it to the request. Perfect. This is the "context". So if an error is thrown, set
error.context = request.context
and you can respond with an error message in the request for that user.The issue though is the way these "per-request domains" are stored. They're stored globally in a stack. If you think about it, that's the only way (unless you pass the context to every method in all your code).
So say a request comes in and runs an async callback that takes 5 seconds. Then another request comes in that has an async callback that takes 10 seconds. If you push and pop from the stack then when the first request finishes it's going to pop off the second request, which is wrong. This would mean the second user would get the error response from the first user's request! You definitely do NOT want to go down this road.
So, node's "domain" code doesn't allow for async callbacks. What they do is this:
That function passed to
bind
is actually async, but the waybind
is implemented is, it calls the passed function in a "synchronous" way:When
domain.enter
is called, it pushes to the global stack, whendomain.exit
is called, it pops. That wont cause the problems described before (first request is 5 seconds, second request is 10 seconds) because thatcb.apply
is "supposed to be" a synchronous function (it's the function withthrow new Error('an error')
above, which is synchronous).So it seems to work there... But if you have a nested async function, it fails:
This is because that
domain.enter(); cb(); domain.exit()
code which calls the function: it's synchronous, so the active domain exits before the nested callback is called. That is the main issue.You can get around that by wrapping every function with
globalDomain.bind
, but then we're back to the initial problem: now I'd have to rewrite every function in tower to use this new convention. That's what this guys plugin did: https://github.com/ypocat/laeh2#3-laeh2-an-elegant-solutionSo, before I consider rewriting all of the code to use something like that plugin, we must see if there is an alternative. This will take time, maybe a long time.