In a current project (kind of a shop system), I use node.js with expressJS and connect-mongo as session store. On client side, I use a single request at startup to create a new session and send multiple parallel requests to the node.js server afterwards. Because these parallel requests change the session, those changes seem to overwrite each other, although they change different objects of the session, of course.
Example (all 3 requests start at the same time):
- Request A pushes some products to the array
req.session.productHist['abc']
- Request B pushes products to
req.session.productHist['def']
- Request C takes some time, but doesn't change the session
Because request C finishes after request A and B, but starts before they finished, it seems to overwrite session.productHist
with the value it held when request C started (null).
How can I fix this?
Update:
Some example code with console output:
var url = require('url'),
express = require('express'),
MongoStore = require('connect-mongo');
var aDay = 24*60*60*1000;
var app = express.createServer();
app.configure(function(){
app.use(express.cookieParser());
app.use(express.session({
secret: "secret",
store: new MongoStore({ db: 'lmsCache' }),
maxAge: aDay
})
);
app.use(express.methodOverride()); app.use(express.bodyParser());
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
app.use(app.router);
app.use(express.logger());
});
function sendStringified(req, res, data) {
data = JSON.stringify(data);
if (req.url_query.callback) { data = req.url_query.callback + "(" + data + ");"; }
res.send(data);
}
function parseParams(req,res,next) {
req.url_query = url.parse(req.url,true).query;
next();
}
function doExpensiveStuff(req,res,next) {
console.log("######################### init start");
[...]
}
app.get('/init', parseParams, doExpensiveStuff, function(req,res) {
console.log("init: session.productHist: " + JSON.stringify(req.session.productHist));
console.log("######################### init end");
sendStringified(req,res,null);
});
app.get('/products', parseParams, function(req,res) {
console.log("######################### products "+req.url_query.category+" start");
if(!req.session.productHist[req.url_query.category])
req.session.productHist[req.url_query.category] = [];
for(var i=0;i<2;i++) {
req.session.productHist[req.url_query.category].push({ "id": new Date().toGMTString() });
}
console.log("products: session.productHist: " + JSON.stringify(req.session.productHist));
console.log("######################### products "+req.url_query.category+" end");
sendStringified(req,res,[]);
});
app.get('/newSession', parseParams, function(req,res) {
console.log("######################### newSession");
req.session.productHist = {};
sendStringified(req,res,true);
});
app.listen(8080);
time = new Date().toGMTString();
console.log('Server starting at: ' + time);
Console log:
Server starting at: Thu, 15 Dec 2011 15:50:37 GMT
################### newSession
################### init start
################### products -1 startproducts: session.productHist: {"-1":[{"id":"Thu, 15 Dec 2011 15:50:40 GMT"},{"id":"Thu, 15 Dec 2011 15:50:40 GMT"}]}
################### products -1 end################### init end
init: session.productHist: {}
################### products -1 start
[...]
products: session.productHist: {"-1":[{"id":"Thu, 15 Dec 2011 15:50:53 GMT"},{"id":"Thu, 15 Dec 2011 15:50:53 GMT"}]}
################### products -1 end
I think I have found the answer to this tricky problem.
From the Express.js documentation:
Properties on req.session are automatically saved on a response
Short version
When you set a session variable (
req.session.my_var = value
), it's not actually saved then (at that exact moment), but later (when you send the response, in your case it's when you dores.send
). That caused your problem.Long version
So what does that mean exactly?