3

I have my nodejs app hosted on Openshift. Here are my specs:

node v0.10.35, express v3.4.8

My package.json dependencies:

"dependencies": {
"angular-loading-bar": "^0.9.0",
"async": "^2.0.0-rc.5",
"bcrypt-nodejs": "0.0.3",
"body-parser": "~1.0.0",
"connect-flash": "^0.1.1",
"connect-mongo": "^1.2.0",
"cookie-parser": "~1.0.0",
"ejs": "^2.4.1",
"express": "~3.4.4",
"lodash": "^4.12.0",
"method-override": "~1.0.0",
"mongodb": "~2.x",
"mongoose": "~4.4.12",
"morgan": "~1.0.0",
"nodemailer": "^2.3.2",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"recaptcha2": "^1.0.8"
},

And here is my server.js

#!/bin/env node

var express = require('express');
var fs      = require('fs');
var mongoose = require('mongoose');
var passport = require('passport');
var flash    = require('connect-flash');

var morgan       = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser   = require('body-parser');

var MongoStore = require('connect-mongo/es5')(express);

var app = express();

var server_port = process.env.OPENSHIFT_NODEJS_PORT || 8080;
var server_ip_address = process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0';

//MongoD
mongodb_connection_string = process.env.OPENSHIFT_MONGODB_DB_URL + "tenders";
mongoose.connect(mongodb_connection_string);
var dbconn = mongoose.connection;
dbconn.on('error', console.error.bind(console, 'connection error:'));
dbconn.once('open', function(){
    console.log('Connected to Mongoose Database.');
});

// Close MongoD connection when app is terminated
process.on('SIGINT', function (){
   mongoose.disconnect();
   dbconn.close(function (){
       console.log("Server halted: Mongoose default connection disconnected.");
       process.exit(0);
   }); 
});

/* Configuration */
app.set('view engine', 'ejs'); // set up ejs for templating

/* Middlewares */
app.use(express.static(__dirname + "/views"));
app.use(express.static(__dirname + "/public"));

// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(bodyParser()); // get information from html forms
app.use(cookieParser()); // read cookies (needed for auth)

/** Persistent database backed session **/
app.use(express.session({ 
    secret: process.env.SECRET,
    store: new MongoStore({mongooseConnection : mongoose.connection}) 
}));

app.use(passport.initialize());
app.use(passport.session());
app.use(flash()); 

require('./routes/routes')(app, passport); 
require('./config/passport')(passport);  configuration

app.use(function(req, res) {
    res.redirect('/')
});

app.use(function (err, req, res, next) {
  if (err.name === 'UnauthorizedError') {
    res.status(401);
    res.json({"message" : err.name + ": " + err.message});
  }
});

/* Start server */
app.listen(server_port, server_ip_address, function(){
    console.log("Listening on " + server_ip_address + ":" + server_port);
});

The problem is connect-mongo is creating a new session every second as I can see from counting the number of entries in sessions collection in my database. The sessions are being created even when there are no active users currently using the website. Is this normal ?

Edit : Is it due to the middleware I use to check if user is logged in using passport isauthenticated method for most of the api call. But the strange thing is its being called even when there are no users making requests to server as you can see from the node.log below that keeps redirecting

GET / 302 3ms - 40b
GET / 302 3ms - 40b
GET / 302 2ms - 40b
GET / 302 2ms - 40b
GET / 302 3ms - 40b
GET / 302 20ms - 40b
GET / 302 3ms - 40b
GET / 302 2ms - 40b
GET / 302 3ms - 40b
GET / 302 4ms - 40b
thethakuri
  • 509
  • 5
  • 16
  • Do you have a `setInterval/setTimeout` in your frontend code? – robertklep Jun 08 '16 at 13:19
  • Well actually I'm using Angularjs on my frontend and I do have some $timeouts there if that is what you mean – thethakuri Jun 08 '16 at 14:53
  • Also, its working fine in my localhost. – thethakuri Jun 08 '16 at 15:07
  • It looks like something is requesting `/` every second. Assuming that your server-side code isn't making those requests, your front-end might be. – robertklep Jun 08 '16 at 15:22
  • But like I said, its working fine in my localhost. Node version in my local machine is v0.10.37 while that in Openshift is v0.10.35. I guess that shouldn't be the problem ! – thethakuri Jun 08 '16 at 15:29
  • Does OpenShift provide a service where it checks if your app is still running? – robertklep Jun 08 '16 at 15:30
  • How do you mean ? I can ssh to application root and tail -f log file. – thethakuri Jun 08 '16 at 15:34
  • Could OpenShift be the source for those requests, by "pinging" your app every second by performing a request for some URL? – robertklep Jun 08 '16 at 15:35
  • Okay, I figured out the culprit. Openshift uses HAProxy for load balancing which is continuously hitting the api to verify that the backend servers are healthy. I somehow need to prevent mongo-connect from creating sessions for these httpchk. – thethakuri Jun 08 '16 at 16:30

4 Answers4

2

I just dealt with this issue as well, and for me it turned out to be resulting from my AWS healthcheck. Every healthcheck ping created and stored a new session. I fixed this by bypassing store whenever the request is a healthcheck:

app.use(function(req, res, done) {
  var isHealthcheck = req.url.indexOf('healthcheck') > -1;
  session({
    secret: config.secrets.session,
    saveUninitialized: true,
    resave: false,
    store: isHealthcheck || new MongoStore({
      mongooseConnection: mongoose.connection,
      db: 'myDb'
    })
  })(req, res, done);
});

So, when isHealthcheck is true, it passes store nothing. When it's not, it does a normal store of the session. The key part here is isHealthCheck ||.

Hope this helps someone!

Ryan
  • 784
  • 6
  • 8
  • 1
    This helped a lot, thank you! I would also mention that the AWS healthcheck path needs to be changed for this code to work. I noticed that you posted this awhile back, have you found any other ways to handle the healthcheck ping and session store mulitple entries? – stromyc Dec 02 '21 at 19:42
  • Thanks for adding the tip on the healthcheck path! I quit working in mongo since then, so I'm sorry to say I have not... :( – Ryan Dec 03 '21 at 21:07
1

I know this is an old question but I had the same problem and after trying solutions that were way too complicated I ended up just putting a middleware function BEFORE my express session middleware. It checks the request user agent to see if it is the health checker and if it then it responds and so never hit the session middleware that ends up storing all that unnecessary info in the DB. Again, make sure the line of code appears in your express app before any session initialization. Hope this helps.

app.use((req, res, next) => {
  if (req.headers['user-agent'] === 'ELB-HealthChecker/2.0') {
    res.sendStatus(200);
  } else {
    next();
  }
});
0

If you are using express-session >= 1.10.0 and don't want to resave all the session on database every single time that the user refresh the page, you can lazy update the session, by limiting a period of time. Because you're are using a newer version of connect-mongo and an older version of express im not 100% sure,but i think this is because of either the cookie or the Uninitialized session.

// Configuring sessions
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
app.use(session({
    secret: 'JohnSecret',
    saveUninitialized: false, // don't create session until something stored
    resave: false, //don't save session if unmodified
    store: new MongoStore({
      url: 'mongodb://localhost/John',
      autoRemove: 'interval',
      autoRemoveInterval: 10 // In minutes. Default
    })
}));
AJ_
  • 3,787
  • 10
  • 47
  • 82
  • Like I said sessions are created every second or so even when there are no active users. My nodejs log continuously output "GET / 302" and somehow session is created which might be due to isauthenticated check on api access. – thethakuri Jun 08 '16 at 14:29
0

Okay, it is the issue with HAProxy continuously checking the backend server to see that its up and working. In doing so it is creating a session a second and cluttering my database. So here is my (dirty) fix:

  1. Create an api /ping that handles HAProxy's httpchk by destroying each session

    app.get('/ping', function(req, res){ req.session.destroy(); res.send(200); });

  2. Configure haproxy/conf to change option httpchk GET / to option httpchk GET /ping

  3. Restart HAProxy cartridge using RHC rhc cartridge-restart --cartridge haproxy

thethakuri
  • 509
  • 5
  • 16
  • 1
    You should move that route to _before_ the `express-session` middleware. That way, it won't be called for that particular route, and hence no session will be created. – robertklep Jun 08 '16 at 19:06
  • As you can see from my server.js file, I have defined all my routes in /routes/routes.js file. I removed the '/ping' route from that file and I used a middleware app.use('/ping') before express-session in server.js as you suggested. It seems to be working so far. – thethakuri Jun 09 '16 at 04:10
  • Also, is it okay to define middleware without defining http method, i.e app.use('/ping') without app.get('/ping') ? – thethakuri Jun 09 '16 at 04:38
  • Yes, you can use middleware like that, but `app.use('/ping', ...)` will match any request where the url _starts_ with `/ping`. If you want to just "catch" all HTTP methods to `/ping`, use [`app.all()`](http://expressjs.com/en/4x/api.html#app.all) – robertklep Jun 09 '16 at 06:37
  • The thing is if I define http methods like `get` or `all` before `express-session` in my server.js, the server doesn't respond as the page gets stuck at 'waiting for localhost' . However, middleware `app.use` works. – thethakuri Jun 09 '16 at 06:49