Last active
June 9, 2022 10:45
-
-
Save isaacs/7269994 to your computer and use it in GitHub Desktop.
Blog post: http://blog.izs.me/post/65712662830/restart-node-js-servers-on-domain-errors-sensible-fud
This file contains 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
// Some bug in the code causes it to crash sometimes. | |
// Gee, that sure is annoying! Server restarts in a | |
// clean state, and spams the error log until we go | |
// in and fix it. | |
function doSomeShiz(n) { | |
shiz.expect(n); | |
for (var i = 0; i < n; i++) { | |
shiz(); | |
} | |
} | |
var shiz = (function() { | |
// this is some internal state etc. | |
var theShiz = 0; | |
var expectShiz = 0; | |
function expect(n) { | |
expectShiz = n; | |
theShiz = 0; | |
} | |
function verify() { | |
console.log('did %d of shiz, expected %d', theShiz, expectShiz); | |
console.log('cleanup shiz'); | |
theShiz = 0; | |
} | |
function shiz() { | |
theShiz++; | |
// oops | |
// such error | |
// many throw uncawt lul | |
if (Math.random() > 0.5) | |
throw new Error('shank shiz'); | |
if (theShiz === expectShiz) | |
verify(); | |
if (theShiz > expectShiz) | |
throw new Error('uh o so error such unexpect'); | |
} | |
shiz.verify = verify; | |
shiz.expect = expect; | |
return shiz; | |
})() | |
doSomeShiz(10); |
This file contains 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
// I know! Since shiz() is throwing, just keep trying! | |
// But make sure we don't accidentally catch some OTHER | |
// kind of error, because that could probably be bad, right? | |
// Like, if it's actually something important. | |
function doSomeShiz(n) { | |
shiz.expect(n); | |
for (var i = 0; i < n; i++) { | |
try { | |
shiz(); | |
} catch (er) { | |
// if it throws the error we expect, back up and try again | |
// even this isn't perfect, because it can throw the error we DONT expect, | |
// and we probably don't want to catch that one! | |
if (er.message === 'shank shiz') | |
i--; | |
else | |
throw er; | |
} | |
} | |
} | |
var shiz = (function() { | |
// this is some internal state etc. | |
var theShiz = 0; | |
var expectShiz = 0; | |
function expect(n) { | |
expectShiz = n; | |
theShiz = 0; | |
} | |
function verify() { | |
console.log('did %d of shiz, expected %d', theShiz, expectShiz); | |
console.log('cleanup shiz'); | |
theShiz = 0; | |
} | |
function shiz() { | |
theShiz++; | |
// oops | |
// such error | |
// many throw uncawt lul | |
if (Math.random() > 0.5) | |
throw new Error('shank shiz'); | |
if (theShiz === expectShiz) | |
verify(); | |
if (theShiz > expectShiz) | |
throw new Error('uh o so error such unexpect'); | |
} | |
shiz.verify = verify; | |
shiz.expect = expect; | |
return shiz; | |
})() | |
doSomeShiz(10); | |
// Turns out, this keeps crashing, because the throw out | |
// of shiz-land futzes the shiz all up. Let's try a bigger | |
// error handler! | |
// | |
// /Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:13 | |
// throw er; | |
// ^ | |
// Error: uh o so error such unexpect | |
// at shiz (/Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:47:13) | |
// at doSomeShiz (/Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:5:7) | |
// at Object.<anonymous> (/Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:56:1) | |
// at Module._compile (module.js:449:26) | |
// at Object.Module._extensions..js (module.js:467:10) | |
// at Module.load (module.js:349:32) | |
// at Function.Module._load (module.js:305:12) | |
// at Function.Module.runMain (module.js:490:10) | |
// at startup (node.js:123:16) | |
// at node.js:1027:3 |
This file contains 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
// This is even worse! | |
// | |
// Now, instead of just crashing, state is fubar! | |
// There's no way to know how far we got through the | |
// loop, no way to continue where we left off, and | |
// the internal state is who knows what. | |
// | |
// Practically *by definition* unexpected exceptions | |
// are going to abruptly jump out of their local | |
// context in a way that cannot be adequately handled. | |
// | |
// In a real application, where presumably the state | |
// of things actually *matters*, this is the worst | |
// possible outcome! A crashed process can simply be | |
// restarted, but an *insane* process might start | |
// acting in unpredictable ways: latency bubbles, | |
// incorrect behavior, etc. These are almost impossible | |
// to debug reasonably, even with the best tools, and | |
// are a huge drain on user experience and dev time. | |
// | |
// Maximum catastrophe! Hit you to death! | |
process.on('uncaughtException', function(er) { | |
console.log('air or lawl!', er.message); | |
}); | |
function doSomeShiz(n) { | |
shiz.expect(n); | |
for (var i = 0; i < n; i++) { | |
shiz(); | |
} | |
} | |
var shiz = (function() { | |
// this is some internal state etc. | |
var theShiz = 0; | |
var expectShiz = 0; | |
function expect(n) { | |
expectShiz = n; | |
theShiz = 0; | |
} | |
function verify() { | |
console.log('did %d of shiz, expected %d', theShiz, expectShiz); | |
console.log('cleanup shiz'); | |
theShiz = 0; | |
} | |
function shiz() { | |
theShiz++; | |
// oops so unepxect | |
// such error | |
// many throw uncawt lul | |
if (Math.random() > 0.5) | |
throw new Error('shank shiz'); // resource unvailable, etc. | |
if (theShiz === expectShiz) | |
verify(); | |
// What if, instead of simply throwing here, we just kept | |
// incrementing theShiz as long as it's !== expectShiz? | |
// Must be an Apple employee, because it's going to be in | |
// one infinite loop! | |
if (theShiz > expectShiz) | |
throw new Error('uh o such error so excetpional!'); | |
} | |
shiz.verify = verify; | |
shiz.expect = expect; | |
return shiz; | |
})() | |
doSomeShiz(10); |
In these examples shiz
itself should use try..catch block.
Anyway, the lessons for the audience are:
- state is awful
- make stateless functions where it is possible
- when you have to deal with state, program it keeping in mind that every external call can rewind your stack
- learn Haskell
I understand if node.js core have a state and can't use try..catch..finally for performance reasons. But when people with no performance constrains write such code, it's their own fault.
PS: express.js wraps all callbacks with try..catch, and nobody complained about it yet.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Blog post on this: http://blog.izs.me/post/65712662830/restart-node-js-servers-on-domain-errors-sensible-fud