ASP.Net session state scaling and performance issues
ASP.Net session state scaling and performance issues
In this post we will in depth discuss:
- What is the ASP .Net Session State and why is it flawed
- What are the different ASP .Net Session State provider alternatives
- Session State Server
- SQL Session State
- SQL Session State In Memory tables
- SQL Session State Reddis
- SQL Session State Token
- Comparing state providers
ASP. Net session was introduced in .NET Framework 1.1 back in the year 2002. In very simple words, the session was (and is) and automatically managed subsystem of the framework that allows you to "identify" a user as it moves through your application, and is used as ground for most authentication systems in ASP .Net. You can read more about how the session works from the official Microsoft documentation:
Why is it flawed?
The session subsystem was designed back in a time where the web did not have the need to reach hyperscale as it does now a days. At that time they made a couple of design decisions that have turned out to be huge bottlenecks in ASP.Net based applications that need to scale.
Session is blocked per request
That means that if an incoming request for a session is being processed, any additional requests for that same session are queued until the current request ends. This design decision makes sense in order to make session access reliable in terms of concurrency. It prevents two concurrent requests from the same user to make modifications to the session.
There are several workarounds for this issue such as starting to flag all your interactions/pages with the ReadOnly session attribute, which produces a non-blocking session request:
This becomes more difficult when you are using ASMX/SOAP web services or any non ASP.Net Page means of accessing your application. Basically, the regular .Net API for Web Services will let you choose between having and not having a session, but it won't offer the possibliity of having a read-only session:
There is a workaround for this, and is by analyzing (i.e. by using the request path) the incoming request in your Application.BeginRequest and manually setting the session mode:
Overall, if you already have reached a scaling problem, having to analyze a huge codebase and decide where can you use readonly session to mitigate the scaling issue can be time consuming and introduce new bugs or issues in your application.
Over time, some session providers have been developed to be non-blocking, considering that the chance in most applications of having two requests modify the session store at the same time is extremely remote (depending a lot on how your application uses the session) and is a risk that can be assumed to gain some scaling overhead.
Session needs a centralized and persistent storage
The client is delivered (i..e through a cookie) and takes care of a session identifier, when the server receives this identifier, your application uses it to retrieve the actual session data from a particular storage.
This means that you need a place to store all this information, and that if you are scaling horizontally your application, this storage needs to be shared between webheads.
This storage needs to be fast and realiably, mostly because the session is used on almost every request of you application, so it will become one of the transactions with the highest (if not the highest) throughput.
Traditional stores such as In-Proc o ASP. Net session state server quickly become a huge bottleneck or scaling pains.
Stores session state in the same process that handles the request. This is the default behaviour. A huge issue with this approach is that any time that the process is reset (making configuration changes, deploying new code, health monitoring and memory contention, etc.), all session data is lost.
Session State Server
Stores session state in a special windows service called ASP.Net State Service. The ASP.Net process talks to the ASP.Net State Service over the network, so theoretically you can have a centralized ASP.Net State Service and scale you web servers horizontally. ASP.Net State Service is not designed as a persistent serivice, so any restart to the service or the host containing the service will vanish all session data.
SQL Session State
Stores session state in SQL Server tables. This makes sure session state is preserved (persitent) and can be shared among web servers.
SQL Session State In Memory tables
Because SQL Session State can quickly turn into a bottle neck, this variant uses In-Memory tables. This stores data in a special types of SQL Server storage, in which all data is kept in memory and sincronized to disk in a different way when compared to traditional tables making them much faster. Beware that in-memory tables are extremly dangerous because the moment SQL Server is unable to allocate memory for these tables it will start failing. Still, you won't be able to scale the session state itself horizontally.
SQL Session State Reddis
Stores session state in a Reddis backend. Reddis can scale horizontally and is very fast to interact with.
SQL Session State Token
The session data is encrypted, signed and stored in a cookie sent to the client. There is no need for a storage backend at all. It has a much simpler design, it is more reliable, and scales better than the other alternatives.
The following table compares the main features of the different storage backends:
|Service||Persistent||Scales horizontally||Allows webheads to scale horizontally||Performance|
|Session State Server||No||No||Yes||Medium|
|SQL Server / SQL Server in memory||Yes||No||Yes||Good|