Web farms in .NET and IIS part 5: Session state management
July 1, 2013 24 Comments
Managing session state is vital in a web farm environment. Many websites simply cannot function without maintaining state throughout the user’s visit. Session variables are maintained by the web server and a cookie is saved to the client. This cookie will inform the server who the user is and the server can then extract the session information from it. You will need to give extra consideration to state management in a web farm scenario.
Imagine that the web request is routed to farm machine A and the code invoked by the request relies on some parameters stored in the session. If the same user is then routed to machine B in the next web request which also relies on the session state then the results may unpredictable: as the value is not available in the session then a null pointer exception may be thrown, or if you put in a guard clause then that variable may be assigned some default value thereby “forgetting” what the value was in the previous web request. And then the wrong default value may be stored in the database instead of the one the user has selected.
In this post we discussed session stickiness which makes sure that the same user is directed to the same machine on subsequent requests. We also said that we should avoid this solution so that the load balancer can pick the “right” machine in the farm based on the actual load. In case you need to deploy a Classic ASP site in a web farm then this is your only option: Classic ASP doesn’t have any built-in session state solution that works with a web farm.
You can define session state handling in IIS. Let’s see what the options are.
Session state management
Every web developer must have used the session state in some form. A classic example is when a user fills in a step-by-step signup questionnaire then the values are store in a session:
Session["FirstName"] = txtFirstName.Text; Session["LastName"] = txtLastName.Text;
The question is how and where these session states are stored.
In IIS you can open the session state manager by choosing the web site and then clicking the Session State icon:
This opens the following window:
The default choice is in-process. This means that the session state data is stored inside of the worker process called w3wp.exe. You can think of this as storing the session inside the application pool. This option provides the fastest retrieval of the session state as there’s no need to connect to another computer or database to read or store the session data. However, there are several disadvantages which are also related but not limited to web farms:
- Session management runs inside the worker process which means that it’s consuming memory in the worker process. This memory could be used by other functionality of your web site.
- The session state is lost as soon as the application pool of your website in IIS is recycled. It is gone forever.
- This option is particularly dangerous in a web farm environment. Take the example I mentioned above: one farm machine stores the session state but not the other. Subsequent web requests from the same user may not read the correct session state
This last point forces us to introduce sticky sessions which beats the purpose of load balancing so you should always try to avoid this path.
The points above will certainly lead to bad user experience in the form of inexplicable exceptions, unexpected values and irritating bugs. Each machine in the web farm will store its own little subset of the full session state:
Salvation comes in the exact opposite of in-process state management: out-of-process state management. This option simply means that instead of storing the session state within the application pool we store it somewhere else, in a place which is independent of the website:
- ASP.NET session state service: this provides a somewhat slower service than the in-process variant as we need to make calls to a remote server. All session data is stored in memory so shutting down the state machine will wipe out all session data as well
- SQL server, where we store the state in a database: this option provides the most reliable service but is also the slowest as all data must be read to and from a database. Session data is not lost even if the state machine is shut down
- A third party component, possibly commercial or self-built
So we move out the session state from each individual farm machine to one central place where the session can be read and stored:
ASP.NET state service
Open the Services management window and you should find the following service there:
So it’s a plain old Windows service. Every machine that has a .NET framework installed has this service. As you see by default it is turned off and is set to a manual start mode. Change this to automatic and start the service to activate it:
Normally you would do this on the designated state service machine. This will allow external machines to connect to the state service and put session data in and out of it and it’s all stored in memory. Restarting either the state server or just that Windows service will erase the session data.
Also, the service cannot be accessed remotely by default. To enable that option you need to set the value of the following registry key to 1: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\AllowRemoteConnection. Don’t forget to restart the asp.net state service after the registry key change.
So this Windows service is easy to use but there’s a significant disadvantage: the State Server does not provide any failover option. If the service is stopped or the machine running the state service is shut down then your session variables will not be available and cannot be saved. If you are willing to take that risk then you’ll be good to go in a couple of minutes.
As mentioned above this provides the most reliable session service but it also the slowest. If you rely on the session state a lot in your code then there will be a lot of read and write operations to and from your SQL server affecting its performance. However, the added redundancy may well compensate for the slight increase in the performance overhead.
You can have a cluster of SQL Servers configured with SQL AlwaysOn so that even if one state machine goes down then the other machine can take over. As state is persisted in database tables it is only lost if the table data is wiped out. Otherwise it is available even if the machine is restarted.
You must prepare your database to fit the session state schema. This can be done using the aspnet_regsql.exe tool which is located in the following folder:
You can only perform the necessary schema change from the command line. If you double-click the executable file then a GUI will open but that GUI is used for setting up ASP.NET membership, profiles and role management tables in the database. It is not meant for state management purposes.
Make sure that the user you provide in the command line arguments has admin rights to the database otherwise the command will fail. The command will need access to msdb system-stored procedures.
The command line arguments include a paramenter called -sstype which stands for the SQL Server session type. It has 3 options: ‘t’ for temporary, ‘p’ for permanent and ‘c’ for custom. T and p create a database called ASPState. T saves the session in the tempdb database so this data is only temporary: session state is lost if SQL Server is restarted. P on the other hand causes the session data to be saved in the ASPState database and allows the session state management to use the same SQL server.
Make sure to open port 1433 on the machine where you’re intending to save the session data. Note that the parameters are case-sensitive. Examples:
aspnet_regsql.exe -ssadd -sstype c -S sql.mycompany.local -d SQLState -U adminUserName -P adminPassword
-ssadd simply means to add SQL Server session state. The server is specified though -S, -d specifies the database, -U the username and -P the password.
aspnet_regsql.exe -ssadd -sstype c -S sql.mycompany.local -d SQLState -E
This is the same as above except that it uses Windows authentication.
aspnet_regsql.exe -ssadd -sstype p -S sql.mycompany.local -E
Here we ask the tool to create the ASPState database, which is the default name of the session state database, hence we don’t need the -d parameter.
You can view all parameters by typing aspnet_regsql.exe /?
Third party tools
The following tools are all reliable session managers that are built for highly available, highly scalable web farms. They either maintain session data in-process and replicate all changes immediately or store the session data out-of-process through a custom worker process that resides on each server.
- MS AppFabric Caching Services
- ScaleOut Software
- A free and open source alternative: Memcached
Preparing your web site
You need to declare in web.config in case you want to use an out of process state management option. Create a new MVC internet application or simply open the one we used in the previous blog post. Open web.config. You need to declare the session management options within the system.web node. Example:
<sessionState mode="StateServer" stateConnectionString="tcpip=machinename:42424"> </sessionState>
If you want to use the StateServer service on the local machine then you can ignore the stateConnectionString attribute.
Use intellisense to check the attribute options for the session state node. You will find the SQL variant there as well. You’ll probably recognise the purpose of the mode attribute and its value. The state connection string will be the address of the state machine and the port of the ASP.NET session service. By default it is listening on port 42424.
In case you’re opting for the SQL Server type of solution then this section may look like the following:
<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="the connection string name to the server"> </sessionState>
Where sqlConnectionString refers to the name of the connection string in the connectionStrings section of web.config which holds the details of the state table. The connection string itself may take up the following format:
<add name="ASPStateConnectionString" connectionString="Data Source=[DB machine name where state DB is stored];Initial Catalog=ASPState;User ID=[db admin user name];Password=[db admin password]" providerName="System.Data.SqlClient" />
We’re not done yet, we also need to add a machine and a validation key within the system.web node, that may look something like this:
<machineKey validationKey="some long hashed value" decryptionKey="another long hashed value" validation="SHA1"/>
The validation key is used to validate encrypted data such as the ViewState to ensure it has not been tampered with. The decryption key is used to encrypt and decrypt forms authentication data and viewstate when validation is set to TripleDES.
The default setting for the validation key is AutoGenerate which does exactly what the name applies: the key will be generated automatically by IIS. The default generation mode for the decryption key is IsolateApps. It generates a unique key for each application by using the application ID.
We need this common machine key as we want to run this on several machines so we don’t want the machine key to be automatically generated. The default IsolateApps mode for the decryption key is OK to use in a web farm scenario but the validation key must be common across all servers so we want to produce it ourselves. There are ASP.NET machine key generators out there on the net, you can search yourself, but here are some:
It’s important to emphasise that we want to have static machine key values. The opposite scenario would be that each web farm machine has its own machine key, which will have different values of course and then they access different versions of the session state service.
If you prepare web.config and then deploy your web site using the deployment profile we built in the previous post then the values in the Session State window in IIS will be populated with the values defined in web.config:
Check the machine key in IIS as well:
You’ll see that even the validation and decryption keys have also been filled out:
Now that we have set up the out of process state management we can recycle the application pool as much as we like the session state variables will be preserved. If for whatever reason you want to destroy the session state you have the restart the ASP.NET State Service in the Services window or restart the state machine itself.
If you recall from the previous post we set up a batch file that deploys the entire IIS configuration from the staging server to the web farm servers. That will ensure that even the machine keys will be “exported” so that we have one common machine key for the entire farm.
In the next post we’ll start looking at Web Farm Framework.