LANGUAGES: C# | VB.NET
ASP.NET VERSIONS: 1.x | 2.0
Measuring Your Usage of Session State
Effective State Management Is One of the Keys to Building Effective Pages
By Dino Esposito
If you really want to overhaul your ASP.NET application to increase performance, you should look at the overall state management policies before you surf the Web for unleashed tips and tricks. Effective state management is certainly not the only factor that influences an application s performance but it is one of the most important. Simply using a bunch of optimization tricks applied to a page that make excessive or bad use of state information doesn t significantly change performance. Fine-tuning the state management is the key factor to fine-tuning the performance of individual pages and the overall application. In this article, I ll focus on session state management and discuss some of the aspects that are often overlooked by developers. Want an example? Raise your hand if you ve ever distinguished between read/write and readonly session state in an ASP.NET application.
Session State Refresher
In ASP.NET, the internal architecture of the session state manager has been revolutionized, although the same programming interface has been maintained. You feel at home when you meet the familiar Session dictionary, which is just what the Microsoft designers intended. Under the public name of the page object, though, a brand new model pulses and lives. Session state is serialized and deserialized to and from a variety of storage media to serve each request. This is the first big difference from classic ASP, where session state had just one possible location: the Web server s memory. In ASP.NET, the session state can be stored in the Web server s memory (default option), the memory of an external process, or a SQL temporary or persistent table.
Clearly, cross-process serialization and deserialization of the session data has a cost measured in an additional 15% if you use the memory of an external process (the state server). If you opt for a SQL Server, the extra cost raises up to 25% (at a minimum). It is worth noting that extra costs apply to each and every request that doesn t explicitly turn off session state. Note also that the type of data stored in a session influences the serialization process. Simple types such as strings, numbers, dates, and arrays of simple types are processed faster. For custom types and complex classes such as the DataSet, ASP.NET resorts to the binary formatter, which requires more time and memory space. If complex types are used, the extra cost per request can go up in a measure that depends on actual types used. (Be aware that in the .NET Framework 1.x the DataSet serializes in XML, which can make the required time and space for out-of-process solutions soon unacceptable.)
When setting up session state, you can choose one of the possible working modes: InProc, StateServer, and SqlServer. The first option, InProc, is the default and the fastest. It makes session state work mostly (but not exactly) like in classic ASP. By assigning data to an executable named aspnet_state.exe, StateServer keeps your data safe in case of IIS failures. The executable uses .NET Remoting through a customizable port to exchange data with the ASP.NET application. Finally, SqlServer is the database option that is ideal when you need extreme robustness, as well as data persistence. Session data is stored in a made-to-measure database table that may even survive SQL Server crashes. (To be precise, in ASP.NET 1.1 you can choose between temporary and persistent tables.) When in SqlServer mode you can store data on any connected machine, as long as the machine runs SQL Server 7.0 or newer. The database name is ASPState and scripts are available to create all necessary tables. Expired sessions are deleted using a job that requires the SQL Server Agent to be up and running.
Overview of the Session State Architecture
In ASP.NET, session state management is governed by a system-provided HTTP module named SessionStateModule. The module filters each and every incoming request that doesn t disable session state. It first determines the required working mode and instantiates the proper state provider component. The state provider is the internal component that actually retrieves the state associated with a particular session ID. To guarantee transparency, and enable the HTTP module to call into any supported component, the state provider implements the IStateClientManager interface. This interface is not intended for use by developers, and as such is not documented. Suffice it to say that IStateClientManager exposes a bunch of methods by means of which the HTTP module retrieves the so-called session state item a single object that contains all the name/value pairs for the specified session ID.
The HTTP module works by hooking the system BeginAcquireState and CompleteAcquireState events. Handling the former, it asks the state provider to retrieve the session state item. Handling the latter, it fills the Session dictionary with the pairs read from the session state item. Bear in mind that the real object behind the public name of Session (a property on the HTTP context) is a collection of type HttpSessionState.
At the end of the request, the current content of the dictionary, as modified by the page, is flushed back to the state provider s storage.
One thing that ASP.NET session state has in common with classic ASP session state is the serialized access to the state. In other words, two different requests within the same session will never execute concurrently. Admittedly, this may not be a relevant problem unless you have framesets or know that your users tend to open multiple browser windows. More in general, serialized access to session state poses a potential performance problem if one of the involved pages or frames starts a lengthy task. If this is the case, the risk is that others will wait until the first one completes. Figure 1 shows the internal serialization mechanism to ensure that each request sees up-to-date and consistent data.
Figure 1: Serialized access to the session state.
Take Advantage of Smart Code
Serialized access is a good thing provided that you really need it. And when is it that you can t do without serialized access? Concurrent access to a shared block of data (in the end, this is what session state is all about) should be prohibited as long as one of the accessors has the permission to modify it. That s what happens with ASP.NET session state, and that can be slightly optimized.
Too many developers often overlook the fact that ASP.NET session state can be disabled or configured to request read-only access. If a particular page doesn t touch session state, then there s no reason for leaving it turned on. You disable session state on individual pages by using the EnableSessionState attribute on the @Page directive:
<%@ Page EnableSessionState="false" %>
To understand the scope of this little change, think of a page used to download an image read from a database or dynamically created or modified page. Most of the times, a similar page doesn t need to perform any access to the session state. Nonetheless, session state is retrieved and attached to the Session property unless you mark the page as shown above. If the page runs in a Web Farm application that employs SQL Server session state, you perform unnecessary access to the database each and every time the page is requested. Implementing this trick doesn t even require you to touch the code-behind class.
Likewise, a page that only reads from the session state should be properly configured so that the ASP.NET runtime doesn t pose any lock on the session state and allows concurrent reads:
<%@ Page EnableSessionState="readonly" %>
To feel the difference, consider the page shown in Figure 2. It contains three buttons, the first of which simply adds a value to the session state. Run the page and click this button. Next, open a second window by pressing Ctrl-N in Internet Explorer. Resize the two browser windows and lay them out as shown in Figure 3. When done, click to block the page on one of the windows and try to read from the other. The second page won t be able to read until the first has completed its tasks simply waiting for six seconds.
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Threading" %>
Figure 2: Add a value to the session state.
Figure 3: No reads are allowed until the previous request has completed.
If you modify the page to use readonly session state (assuming there s something to read in the session state), you ll see that reading is immediate, no matter if another page request is ongoing. Be aware that session state is read/write by default. Furthermore, no exception is thrown if you attempt to write to a session from within a page marked for readonly access; the code simply has no effect.
ASP.NET provides some very cool, and very smart features. But you need to be smart also; take advantage of these features and the benefit will show in your code.
The value assigned to the EnableSessionState attribute is processed by the ASP.NET runtime when creating the dynamic class to render the page. In particular, an interface is added to the page declaration to indicate the type of support requested for the session. A page named default.aspx with full access to the session state looks like this to the HTTP runtime:
public default_aspx : Page, IRequiresSessionState
IRequiresSessionState is a marker interface, meaning that it has no methods to implement. The interface specifies that the target page has read and write access to session-state values. Likewise, readonly access is characterized by another marker interface: the IReadOnlySessionState interface.
Although stronger and more reliable than in classic ASP, session state remains a delicate subsystem of any Web application. Optimizing session state is one of the major points in the process of building effective and high-performance applications. Session state should be used because it is a form of data caching without most of the drawbacks of classic ASP. However, be forewarned: To avoid taxing the Web server s memory or communication between internal components of the ASP.NET puzzle, it shouldn t be overused.
As a final note, keep in mind that in case of empty session state, a brand new session ID is generated per each request and the session state is not persisted to the state provider. This is what happens unless you define a Session_Start event handler. As a result, do not define an empty and useless Session_Start handler in your global.asax. If you do, your system will always experience per-request serialization/deserialization of empty data structures the best possible definition of useless code.
The sample code accompanying this article is available for download.
Dino Esposito is a Wintellect trainer and consultant who specializes in ASP.NET and ADO.NET. Author of Programming Microsoft ASP.NET and Introducing ASP.NET 2.0, both from Microsoft Press, Dino also helped several companies architect and build effective products for ASP.NET developers. Dino is the cofounder of http://www.DotNet2TheMax.com, a popular portal for .NET programmers. Write to him at mailto:[email protected] or join the blog at http://weblogs.asp.net/despos.