Wednesday 31 July 2013

Writing high performance web applications with server side sessions

If you are writing a web server that requires server side session management there are a number of issues that can kill performance or blow up in complexity, sometimes literally thinking out of the box can solve these issues.
Typically with applications that require server session state you need a way of finding a session object created in a previous call.  The classic way of doing this is to have a concurrent or mutex protected hash map to find the session object, easy peasy.  However you will find that you will get your knickers in a twist when you start having to have garbage collection, typically you will need a number of timeouts that get triggered between calls and hertbeats etc that quickly complicate things.
Sure it is possible to use reference counted smart pointers to protect these objects, but these timeouts need cancelling, it can get messy very quickly especially when you need to start managing the mutexes concurrently involved on the session lookup and on the session object, without hitting deadlocks.
Even if you have the time to solve these problems, if you are on a busy server with lots of cores running these locks can start to seriously affect the throughput possible. Amdahl's law starts rearing its ugly head.
However do you need all your threads to have access to the same session object? Could we somehow restrict access to a session to a partcular thread?  If so we could ditch the locking around the session and its lookup.  But how?
The solution that I have come up is surprisingly simple and it really thinks outside the box and into the router.  The setup that I use is this:
  • You set up a series of internal ports and assign each port to a dedicated thread, that thread then uses that port asynchronously so that it does not block between calls to IO.  This way it can run at 100% utilising that core to its fullest extent.
  • Also it is recommended that you give each thread its own dedicated core that it runs at a high priority, thereby maximising performance of the caching on the cores.
  • On initial connection the router distributes a client to one of the ports and a session id is assigned. That session id then used by the router to send subsequant request back to the same port.
By having this we have a shared nothing architecture between sessions, which is great.  Your sessions do not need to see each other, so having a shared session pool makes no sense.  Your code is much cleaner and you will find that the complexities around locking and garbage collection disapear.  You no longer need mutexes and smart pointers no longer need to use memory barriers all is good and all is very fast!

No comments:

Post a Comment