diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2010-10-27 02:10:14 -0700 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2010-10-27 02:12:25 -0700 |
commit | ac54272218a4d82f486aa163369962c98ba614c2 (patch) | |
tree | 9e566b71d651bd17a891bce89341f516d64a3b6a /lib | |
parent | 79ecc8e9b7f82b5159e04a42118f6d8b87dee2ee (diff) | |
download | nodejs-ac54272218a4d82f486aa163369962c98ba614c2.tar.gz nodejs-ac54272218a4d82f486aa163369962c98ba614c2.tar.bz2 nodejs-ac54272218a4d82f486aa163369962c98ba614c2.zip |
Gracefully handle EMFILE
Implementing a tip from Marc Lehmann:
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_accept_ing_wh
Keep an extra FD around for every server. When you hit EMFILE, destroy that
FD, accept a connection, close it; in this way you can clear the connection
queue and let people know that you're overload.
No more timeout needed.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/net.js | 61 |
1 files changed, 41 insertions, 20 deletions
diff --git a/lib/net.js b/lib/net.js index f25012364..1e11c4aa2 100644 --- a/lib/net.js +++ b/lib/net.js @@ -922,36 +922,38 @@ function Server (/* [ options, ] listener */) { self.connections = 0; - self.paused = false; - self.pauseTimeout = 1000; - self.allowHalfOpen = options.allowHalfOpen || false; - function pause () { - // We've hit the maximum file limit. What to do? - // Let's try again in 1 second. - self.watcher.stop(); - - // If we're already paused, don't do another timeout. - if (self.paused) return; - - setTimeout(function () { - self.paused = false; - // Make sure we haven't closed in the interim - if (typeof self.fd != 'number') return; - self.watcher.start(); - }, self.pauseTimeout); - } - self.watcher = new IOWatcher(); self.watcher.host = self; self.watcher.callback = function () { + // Just in case we don't have a dummy fd. + if (!self._dummyFD) self._getDummyFD(); + while (self.fd) { try { var peerInfo = accept(self.fd); } catch (e) { if (e.errno == EMFILE) { - pause(); + // Output a warning, but only at most every 5 seconds. + var now = new Date(); + if (now - self._lastEMFILEWarning > 5000) { + console.error("(node) Hit max file limit. Increase 'ulimit -n'."); + } + self._lastEMFILEWarning = now; + + // Gracefully reject pending clients by freeing up a file + // descriptor. + if (self._dummyFD) { + close(self._dummyFD); + self._dummyFD = null; + while (true) { + peerInfo = accept(self.fd); + if (!peerInfo) break; + close(peerInfo.fd); + } + self._getDummyFD(); + } return; } throw e; @@ -1076,9 +1078,23 @@ Server.prototype._startWatcher = function () { this.emit("listening"); }; + +Server.prototype._getDummyFD = function () { + try { + this._dummyFD = socket("tcp"); + } catch (e) { + this._dummyFD = null; + } +}; + + Server.prototype._doListen = function () { var self = this; + // Grab a dummy fd for EMFILE conditions. + self._getDummyFD(); + self._lastEMFILEWarning = 0; + try { bind(self.fd, arguments[0], arguments[1]); } catch (err) { @@ -1120,6 +1136,11 @@ Server.prototype.close = function () { close(self.fd); self.fd = null; + if (this._dummyFD) { + close(this._dummyFD); + this._dummyFD = null; + } + if (self.type === "unix") { fs.unlink(self.path, function () { self.emit("close"); |