Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ExpressJS - How to handle simultaneous requests? Requests seem to block one another.

I have the following piece of code

var express = require('express');
var routes = require('./routes');    
var http = require('http');
...
app.get('/a',function(){
  Card.findCards(function(err, result){  //Mongoose schema
    res.send(result); //Executes a query with 9000 records
  })  
});
app.get('/b', function(req, res){
  res.send("Hello World");
});

I find that when I make a get on localhost/a, it takes around 2.3 seconds to complete. This isn't really surprising since it fetches quite a bit of data from the database. However I find that if I GET /b while /a is loading, b will not display. It is as if the call to /a is blocking the call to /b.

Is this how express is supposed to work? I've always operated on the assumptions that individual routes are asynchronous since they take in callbacks but it seems like express can only process one request at a time. Until res.end() is called, no other request gets processed. Am I missing any configuration that I need to do?

For reference, this is how I connect to mongoose

mongoose.connect(dbConnectionString, {server:{poolSize:25}});

And this is my http server initialization part

http.globalAent.maxSockets = 20; // or whatever

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

EDIT: Here is the code for the Card Model and associated schema + functions

//Card.js
var mongoose = require('mongoose')
  , Schema = mongoose.Schema;

var CardSchema = new Schema({
  _id : {type: String},
  stores : [{
        store: {type: Schema.Types.ObjectId, ref:'StoreModel', required: true}
      , points: {type: Number, required: true}
  }]
});

exports.findCards = function(callback){
  var query = Card.find({}, callback); 
}
like image 842
pauloadaoag Avatar asked Jan 21 '14 10:01

pauloadaoag


1 Answers

I've been experiencing the same problem, with a setup similar to yours. There are two issues, both of them with the same root cause: Node has non-blocking I/O operations, but (as bbozo points out) CPU-intensive operations do block it.

The first problem lies in your mongoose call. After mongoose retrieves the documents from your collection, it converts them into mongoose objects. If you get 9000 records, it will do this 9000 times. The lines in question are in mongoose's query.js library; check out the for loop on its completeMany function to find the relevant blocking operations.

The second problem comes when Express stringifies the resulting JSON objects to send your response. The culprit is the res.json function under Express's response.js library. For a large response the blocking nature of stringify will be noticeable.

I am not quite sure how to solve this issue. You could probably try to use mongodb's native library, instead of mongoose. You could also try and patch Express so it uses JSON stream calls instead of the blocking stringify. Pagination of your query and response would also help, though I know it's not very simple to implement.

like image 111
Jorge Aranda Avatar answered Nov 15 '22 03:11

Jorge Aranda