summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2010-10-27 02:10:14 -0700
committerRyan Dahl <ry@tinyclouds.org>2010-10-27 02:12:25 -0700
commitac54272218a4d82f486aa163369962c98ba614c2 (patch)
tree9e566b71d651bd17a891bce89341f516d64a3b6a /lib
parent79ecc8e9b7f82b5159e04a42118f6d8b87dee2ee (diff)
downloadnodejs-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.js61
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");