EMFILE error in Async file reading

954 views
Skip to first unread message

Afshin Mehrabani

unread,
Apr 29, 2013, 10:13:40 AM4/29/13
to nod...@googlegroups.com
Today when I tried to implement an example of using async/sync I/O methods in NodeJs, I faced an strange problem. When I'm trying to send requests with ab, I get this error in Async method:

{ [Error: EMFILE, open 'sample.txt'] errno: 20, code: 'EMFILE', path: 'sample.txt' }

But the same functionality in Sync mode works well, without any errors.

This is my ab command for running the test: ab -n 10000 -c 1000 -vhr http://localhost:8080/

Here is my both codes:

Async:

http.createServer(function (req, res) {
  fs.readFile('sample.txt', function (err, data) {
    if(err) {
      res.writeHead(500, {'Content-Type': 'text/plain'});
      res.end();
      console.log(err);
    } else {
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end(data);
    }
  });
}).listen(8080, '127.0.0.1');
 
Sync:

http.createServer(function (req, res) {
  var fileOutput = fs.readFileSync('sample.txt').toString();
  if(!fileOutput) {
      res.writeHead(500, {'Content-Type': 'text/plain'});
      res.end('Error in reading the file.');
  } else {
    res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end(fileOutput);
  }
}).listen(8081, '127.0.0.1');

What's the matter? Is there any problem in using Async methods?

Ben Noordhuis

unread,
Apr 29, 2013, 10:38:17 AM4/29/13
to nod...@googlegroups.com
There is in the way you use them.

The synchronous version is effectively serial, i.e. it processes
requests one by one (IOW, there is only one file descriptor at any
point in time that refers to sample.txt) and that's why you don't hit
the open file descriptor limit. It also means that fs.readFileSync()
is now the single largest bottleneck in your application.

The asynchronous version runs concurrently, meaning there are 1,000
file descriptors - or however many simultaneous connections you're
serving - referring to sample.txt. Raise `ulimit -n` and the problem
goes away (or cache the contents of sample.txt, of course.)

Alex Kocharin

unread,
Apr 29, 2013, 3:39:24 PM4/29/13
to nod...@googlegroups.com

Raising ulimit smells like a bad solution, doesn't it?

I'd suggest to use https://github.com/isaacs/node-graceful-fs instead. Why isn't something like that included in the core by the way? Issue seems to be quite common.

... or write a wrapper to readFile to explicitly limit an amount of open files...

On the other hand, I might be missing a point here. Why is this limit of 1024 opened files enforced by default in the first place?

--
// alex


29.04.2013, 18:38, "Ben Noordhuis" <in...@bnoordhuis.nl>:
> On Mon, Apr 29, 2013 at 4:13 PM, Afshin Mehrabani <afshi...@gmail.com> wrote:
>
>> О©╫Today when I tried to implement an example of using async/sync I/O methods
>> О©╫in NodeJs, I faced an strange problem. When I'm trying to send requests with
>> О©╫ab, I get this error in Async method:
>>> О©╫{ [Error: EMFILE, open 'sample.txt'] errno: 20, code: 'EMFILE', path:
>>> О©╫'sample.txt' }
>> О©╫But the same functionality in Sync mode works well, without any errors.
>>
>> О©╫This is my ab command for running the test: ab -n 10000 -c 1000 -vhr
>> О©╫http://localhost:8080/
>>
>> О©╫Here is my both codes:
>>
>> О©╫Async:
>>> О©╫http.createServer(function (req, res) {
>>> О©╫О©╫О©╫fs.readFile('sample.txt', function (err, data) {
>>> О©╫О©╫О©╫О©╫О©╫if(err) {
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫res.writeHead(500, {'Content-Type': 'text/plain'});
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫res.end();
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫console.log(err);
>>> О©╫О©╫О©╫О©╫О©╫} else {
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫res.writeHead(200, {'Content-Type': 'text/plain'});
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫res.end(data);
>>> О©╫О©╫О©╫О©╫О©╫}
>>> О©╫О©╫О©╫});
>>> О©╫}).listen(8080, '127.0.0.1');
>> О©╫Sync:
>>> О©╫http.createServer(function (req, res) {
>>> О©╫О©╫О©╫var fileOutput = fs.readFileSync('sample.txt').toString();
>>> О©╫О©╫О©╫if(!fileOutput) {
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫res.writeHead(500, {'Content-Type': 'text/plain'});
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫res.end('Error in reading the file.');
>>> О©╫О©╫О©╫} else {
>>> О©╫О©╫О©╫О©╫О©╫res.writeHead(200, {'Content-Type': 'text/plain'});
>>> О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫О©╫res.end(fileOutput);
>>> О©╫О©╫О©╫}
>>> О©╫}).listen(8081, '127.0.0.1');
>> О©╫What's the matter? Is there any problem in using Async methods?
>
> There is in the way you use them.
>
> The synchronous version is effectively serial, i.e. it processes
> requests one by one (IOW, there is only one file descriptor at any
> point in time that refers to sample.txt) and that's why you don't hit
> the open file descriptor limit. О©╫It also means that fs.readFileSync()
> is now the single largest bottleneck in your application.
>
> The asynchronous version runs concurrently, meaning there are 1,000
> file descriptors - or however many simultaneous connections you're
> serving - referring to sample.txt. О©╫Raise `ulimit -n` and the problem
> goes away (or cache the contents of sample.txt, of course.)
>
> --
> --
> Job Board: http://jobs.nodejs.org/
> Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> You received this message because you are subscribed to the Google
> Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com
> To unsubscribe from this group, send email to
> nodejs+un...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/nodejs?hl=en?hl=en
>
> ---
> You received this message because you are subscribed to the Google Groups "nodejs" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to nodejs+un...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Matt

unread,
Apr 29, 2013, 3:52:41 PM4/29/13
to nod...@googlegroups.com

On Mon, Apr 29, 2013 at 3:39 PM, Alex Kocharin <al...@kocharin.ru> wrote:
Why is this limit of 1024 opened files enforced by default in the first place?

Because history.

Every open file uses some amount of kernel space to keep track of it. Plus you had things like select(2) with FD_SETSIZE equal to 1024 by default, so these limits made sense, especially on older computers with limited memory.

Nowadays we use epoll/kqueue and don't need those arbitrary limits quite so much, but some level of limit is still useful in case you leak file descriptors.

Matt.

Afshin Mehrabani

unread,
Apr 30, 2013, 2:05:44 AM4/30/13
to nod...@googlegroups.com
Thank you guys, I got it. Also fs-graceful seems a good solution.

Afshin Mehrabani

unread,
Apr 30, 2013, 2:08:48 AM4/30/13
to nod...@googlegroups.com
Just another question guys, what about opening a link to the file and then use that link in all reads? I mean use fs.open and hold the fd parameter in a global scope to use it in all other situations. 

Will this solution fix the problem?


On Monday, April 29, 2013 6:43:40 PM UTC+4:30, Afshin Mehrabani wrote:

Ben Noordhuis

unread,
Apr 30, 2013, 4:42:18 AM4/30/13
to nod...@googlegroups.com
On Tue, Apr 30, 2013 at 8:08 AM, Afshin Mehrabani <afshi...@gmail.com> wrote:
> Just another question guys, what about opening a link to the file and then
> use that link in all reads? I mean use fs.open and hold the fd parameter in
> a global scope to use it in all other situations.
>
> Will this solution fix the problem?

Yes, as long as you use positional reads: fs.read() with position >=
0. Non-positional reads (position=null or undefined, i.e. read from
current offset) will give unexpected results with multiple readers.

Afshin Mehrabani

unread,
Apr 30, 2013, 6:15:45 AM4/30/13
to nod...@googlegroups.com
Sorry I didn't get it. Why should read an undefined position? or zero?

Fedor Indutny

unread,
Apr 30, 2013, 6:21:31 AM4/30/13
to nod...@googlegroups.com
Because you're reading file from start to end, and position changes at every read?

Cheers,
Fedor.


--

Isaac Schlueter

unread,
Apr 30, 2013, 12:53:51 PM4/30/13
to nodejs
As Ben said, as long as you're using positional reads (which
fs.ReadStream does) then having multiple readers with the same fd is
totally fine.

However, if you're doing fs.read() without an explicit position, then
the OS will just read from the "current offset" (ie, usually the last
offset + last read length) so that calling read() repeatedly without
an offset will scan through the file as you'd expect.

If you are trying to save yourself fs.open() calls, and reuse fd's
correctly, check out how "st" uses the "fd" module.
https://github.com/isaacs/st and https://github.com/rvagg/node-fd
Reply all
Reply to author
Forward
0 new messages