ASP.NET Session State: Architectural and Performance Considerations

I recently came across a great post on one of the internal forums describing the strengths
and weaknesses of different ASP.NET session state strategies. Its author, J.D. Meier,
has graciously given me permission to use it as the basis of a blog entry.

As you're probably aware, state management is an important consideration for web developers.
The HTTP protocol is connectionless, so any web application that persists data across
multiple web page requests needs some mechanism to ensure this session-based data
is maintained. Examples of session-based data include shopping carts (on an ecommerce
site), the currently logged-on user credentials, and other application-specific data
that would normally be held on a client. Various strategies have been utilised over
the last few years, including hidden forms, cookies and server-based state engines.
Of these choices, the latter is perhaps architecturally most attractive since it reduces
the dependence on a client; unfortunately, maintaining session state on the server
can be expensive on resources and makes it hard to scale out using web farms or secure
pages with HTTPS.

Enter the ASP.NET session state engine, which attempts to make server-side session
state a much more viable option. ASP.NET offers three separate choices for session
state storage when it's switched on: locally on the server (InProc), remotely in a
SQL Server database (SqlServer), or remotely using a session state service (StateServer).
I'll go through each of those options and compare the benefits of each.

InProc

The local option is not dissimilar to that provided by ASP, in that it requires all
requests to come to the same physical server rather than being randomly split across
multiple IIS machines. The upside is that it's very fast (it runs in-process), and
it's simple to implement. If you've just got one web server, this is the best choice
in most scenarios. The only time when you might want to consider something else in
this scenario is when the session data is expensive to rebuild, since any InProc session
data is flushed when the ASP.NET worker process or IIS restarts. If, for example,
you're maintaining a shopping cart as local session data that gets cleared out, you
risk annoying customers sufficiently that they take their business elsewhere.

For multiple servers, InProc is unsuitable unless you can implement some form of server
affinity, so that every time a particular client requests a web page they are directed
back to that same server. Services such as Network
Load Balancing
(part of Windows Server 2003 Enterprise Edition) can provide this,
although they inevitably add their own overhead.

StateServer

I've come across a few developers who haven't come across this choice. The state server
relies on a Windows service which is disabled by default, which perhaps explains why
people haven't noticed it - go to Administrative Tools / Services and enable the ASP.NET
State Service to get it working. You can run the state service on a dedicated machine
or shared with a web server host; the main requirement to make this service work efficiently
is plenty of RAM. Because the service is isolated from IIS, you can restart IIS without
losing the session data contained within it, and you can point multiple distributed
ASP.NET boxes at the same service, thereby removing server affinity issues. However,
the session data isn't persisted onto disk, so it can't be backed up and a server
reboot will lose the data. On the plus side, you don't need SQL Server or anything
beyond a Windows licence to run it, making this option easy to set up and maintain.
This option is however signficantly slower than using InProc, due to network latency
and roundtrip costs. Keeping the state server on a dedicated private LAN with the
other web server boxes will help.

Finally, don't forget that you can run the ASP.NET State Service in a single web server
environment - this provides durability against IIS restarts, but at a performance
cost due to it running out of process.

SqlServer

This is the high-end option in terms of flexibility and reliability, but is also the
most expensive to build and maintain. Rather than storing session data in memory,
it is persisted to a SQL Server instance. It's more reliable than any of the other
approaches, since the data is persisted in a more durable form (e.g. it will survive
a server restart and can be backed up). You can even use SQL Server clustering support
to increase session state reliability still further. From a performance point of view,
it's generally comparable to the StateServer service when you've got multiple web
servers using it for session state. The session state itself is stored as a BLOB in
a persisted or temporary table, which can reduce the serialization cost but makes
it harder to view the session data itself.

In most cases, integrated Windows authentication is the best choice, preferably utilising
Kerberos (NTLM requires an extra roundtrip for authentication). Connection pooling
can be used to minimise the initial performance hit.

Further Reading

The MSDN
documentation
is quite good at describing the configuration of these options;
meanwhile, Rob Howard has a short
white paper
that is now a little old but still mostly appropriate. Finally, keep
an eye out for a new guide that will shortly be released from the Patterns & Practices
group, entitled "Improving .NET Application Performance and Scalability". This paper
will go into these topics and others in far greater detail.