Resolving ASP.NET Session State Inconsistency in Load-Balanced IIS 7.5 with SQL Server Session Storage


2 views

During a recent infrastructure audit, we encountered a particularly sneaky issue with ASP.NET session state in a load-balanced IIS 7.5 environment. Users reported session data mysteriously disappearing and reappearing - classic symptoms of session state problems in web farms.

The root cause became clear after examining our setup:

Server Architecture:
- 2 x IIS 7.5 web servers (Windows Server 2008 R2)
- Network Load Balancing (NLB) without client affinity
- SQL Server 2012 session state database (ASPState)
- ASP.NET 4.0 web application

Session state records showed duplicate entries like:

ASPStateTempSessions Table:
| SessionId                           | Created      |
|-------------------------------------|--------------|
| w2q4m55z5j0i2v55lqmlff45_9f8e7d     | 2023-01-01...|
| w2q4m55z5j0i2v55lqmlff45_a1b2c3     | 2023-01-01...|

Through packet analysis and SQL profiling, we determined that IIS was appending different application identifiers to session IDs:

Server A: /W3SVC/1/Root → AppID: 9f8e7d
Server B: /W3SVC/2/Root → AppID: a1b2c3

Here's the complete fix we implemented:

// PowerShell script to synchronize site IDs across servers
Import-Module WebAdministration

# Step 1: Get current site information
$site = Get-Item "IIS:\Sites\YourSiteName"
Write-Host "Current ID: $($site.Id)"

# Step 2: Set new consistent ID
Set-ItemProperty "IIS:\Sites\YourSiteName" -Name ID -Value 10

# Step 3: Verify change
$updatedSite = Get-Item "IIS:\Sites\YourSiteName"
Write-Host "Updated ID: $($updatedSite.Id)"

# Step 4: Restart IIS
iisreset /restart

After implementation, verify with:

SELECT 
    s.SessionId,
    a.AppName,
    a.AppId
FROM 
    ASPStateTempSessions s
JOIN 
    ASPStateTempApplications a ON s.AppId = a.AppId

For environments where changing site IDs isn't feasible:

<sessionState 
    mode="SQLServer" 
    sqlConnectionString="..." 
    allowCustomSqlDatabase="true"
    partitionResolverType="YourNamespace.DatabasePartitionResolver, YourAssembly" />

With a custom partition resolver:

public class DatabasePartitionResolver : IPartitionResolver
{
    public void Initialize()
    {
        // Implement custom partitioning logic
    }
    
    public string ResolvePartition(object key)
    {
        // Return consistent connection string
        return ConfigurationManager.ConnectionStrings["SessionDB"].ConnectionString;
    }
}
  • If using Windows Server 2012+/IIS 8+, consider using ARR (Application Request Routing) with sticky sessions
  • For Azure environments, use Redis Cache for session state instead of SQL Server
  • Always test session-dependent functionality after configuration changes

During a recent infrastructure audit, we noticed erratic session behavior in our ASP.NET 4.0 application running on IIS 7.5 with SQL Server session state storage. Users reported data appearing and disappearing randomly. Our setup consisted of:

- 2 load-balanced Windows Server 2008 R2 machines
- SQL Server 2012 for centralized session state
- Network Load Balancing (NLB) without sticky sessions

ASP.NET's SQL session state provider uses a composite key consisting of the session ID and application ID. The application ID is derived from the IIS site's metabase path. When site IDs differ across servers:

Server A: /W3SVC/1/Root → AppID: 1234abcd
Server B: /W3SVC/2/Root → AppID: 5678efgh

This causes ASP.NET to treat sessions from the same browser as distinct sessions when requests hit different servers.

Querying the ASPStateTempSessions table revealed duplicate entries for the same session cookie:

SELECT * FROM ASPStateTempSessions 
WHERE SessionId LIKE 'qwertyuiop%'

The results showed identical session data stored under different keys due to the varying AppID suffixes.

The solution involves synchronizing site IDs across all web servers:

Method 1: Through IIS Manager

1. Open IIS Manager → Sites → Select website → Advanced Settings
2. Change ID to consistent value (e.g., 10)
3. Execute in command prompt: iisreset /restart
4. Repeat on all servers in the farm

Method 2: Manual config edit

Edit applicationHost.config:

<site name="MySite" id="10">
    <application path="/" applicationPool="MyAppPool">
        <virtualDirectory path="/" physicalPath="C:\sites\mysite" />
    </application>
</site>

After making these changes:

  • Log file paths will change (W3SVC10 instead of W3SVC1)
  • Any hardcoded references to old paths need updating
  • Application pools may need recycling

For ASP.NET Core applications, consider using Redis distributed cache instead:

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "server:port";
    options.InstanceName = "MyApp_";
});

This PowerShell script helps validate consistency across servers:

Import-Module WebAdministration
$site = Get-Item 'IIS:\Sites\MySite'
Write-Output "Site ID: $($site.Id)"
Write-Output "Physical Path: $($site.PhysicalPath)"

Run this on each server to confirm identical configurations.