What is a good session store for a single-host Node.js production app?
Spent the day looking into this. Here are the options I've discovered. Requests/second are performed via ab -n 100000 -c 1 http://127.0.0.1:9778/
on my local machine.
- no sessions - fast (438 req/sec)
-
cookieSession: requires no external service, minor speed impact (311 req/sec) - fastest, sessions will expire with the cookie (customised by
maxAge
) -
connect-redis: requires redis server, large speed impact (4 req/sec with redis2go and redisgreen) - faster than mongo, sessions will be deleted after a while (customised by
ttl
) -
connect-mongo - requires mongodb server, large speed impact (2 req/sec with mongohq) - slower than redis, requires manual
clear_interval
to be set to cleanup sessions
Here is the coffeescript I used for cookieSession:
server.use express.cookieSession({
secret: appConfig.site.salt
cookie: maxAge: 1000*60*60
})
Here is the coffeescript I use for redis:
RedisSessionStore ?= require('connect-redis')(express)
redisSessionStore ?= new RedisSessionStore(
host: appConfig.databaseRedis.host
port: appConfig.databaseRedis.port
db: appConfig.databaseRedis.username
pass: appConfig.databaseRedis.password
no_ready_check: true
ttl: 60*60 # hour
)
server.use express.session({
secret: appConfig.site.salt
cookie: maxAge: 1000*60*60
store: redisSessionStore
})
Here is my coffeescript for mongo:
server.use express.session({
secret: appConfig.site.salt
cookie:
maxAge: 100*60*60
store: new MongoSessionStore({
db: appConfig.database.name
host: appConfig.database.host
port: appConfig.database.port
username: appConfig.database.username
password: appConfig.database.password
auto_reconnect: appConfig.database.serverOptions.auto_reconnect
clear_interval: 60*60 # hour
})
})
Now of course, the remote redis and mongo databases will be slower than their local equivalents. I just couldn't get the local equivalents working, especially considering the installation and maintenance time for me was far more than what I was willing to invest when compared with hosted remote alternatives, something I feel is true for others too hence why these hosted remote database services exist in the first place!
For local database benhmarks, see @Mustafa's answer.
Happy for someone to edit this answer to add their local database benchmarks to the mix.
Since the accepted answer is only connecting to remote hosts, it is obvious that it will be always slower than localhost. Even if it is the next computer in your home, it would take milliseconds to read from that computer, but local memory takes only nanoseconds. You should compare them by using locally installed servers.
Here are my results from my local pc: You see, redis is almost as fast as in-memory in under high load. You can clone my the repo that these test codes are available: https://github.com/mustafaakin/express-session-store-benchmark
Concurrency: 1
none 4484.86 [#/sec]
memory 2144.15 [#/sec]
redis 1891.96 [#/sec]
mongo 710.85 [#/sec]
Concurrency: 10
none 5737.21 [#/sec]
memory 3336.45 [#/sec]
redis 3164.84 [#/sec]
mongo 1783.65 [#/sec]
Concurrency: 100
none 5500.41 [#/sec]
memory 3274.33 [#/sec]
redis 3269.49 [#/sec]
mongo 2416.72 [#/sec]
Concurrency: 500
none 5008.14 [#/sec]
memory 3137.93 [#/sec]
redis 3122.37 [#/sec]
mongo 2258.21 [#/sec]
The session used pages are very simple pages;
app.get("/", function(req,res){
if ( req.session && req.session.no){
req.session.no = req.session.no + 1;
} else {
req.session.no = 1;
}
res.send("No: " + req.session.no);
});
Redis store config:
app.use(express.session({
store: new RedisStore({
host: 'localhost',
port: 6379,
db: 2,
}),
secret: 'hello'
}));
Mongo store config:
app.use(express.cookieParser());
app.use(express.session({
store: new MongoStore({
url: 'mongodb://localhost/test-session'
}),
secret: 'hello'
}));
Another good option is memcached. The session states are lost if memcached is restarted, but there is virtually never any reason to do that. You can leave the cache running all the time even when you restart your app server. Access to the session data is virtually instantaneous and memcached will run happily with whatever (appropriate) amount of memory you give it. And I've never seen memcached crash (on Linux).
https://github.com/elbart/node-memcache
Things to keep in mind about memcached generally:
- Never have whitespace in your cache keys
- Be aware that there is a maximum cache key length, including any namespace prefix you might use. If your cache key is too long, use a 1-way hash of it instead.
Neither of these should be an issue with session storage; just with generalized caching.
I've gone with a MongoDB session store using connect-mongo.
Install with npm install connect-mongo
and replace the existing MemoryStore with
app.use(express.session({ store: new MongoStore({ db: 'some-database' }) }));
It manages the database side of sessions automatically.