SQL Server 2008 R2 High Availability Setup: Load Balancing Strategies for ASP.NET Applications with 99.9% Uptime


4 views

When your ASP.NET application starts experiencing heavy database traffic, SQL Server 2008 R2 can become a bottleneck. Traditional web server load balancing won't solve database contention issues. Let's examine practical approaches that won't break the bank for Micro-ISVs.

For 99.9% uptime (8.76 hours annual downtime allowance), we need solutions addressing:

  • Seamless Windows Update application
  • Hardware failure resilience
  • Query load distribution
  • Minimal code changes to existing ASP.NET apps

This provides automatic redirection during primary server failures. Configuration example:

-- On principal server
ALTER DATABASE YourDB 
SET PARTNER = 'TCP://MirrorServer.domain.com:5022'
GO

-- On mirror server
ALTER DATABASE YourDB 
SET PARTNER = 'TCP://PrincipalServer.domain.com:5022'
GO

Connection string modification in ASP.NET:

Data Source=PrincipalServer;Failover Partner=MirrorServer;
Initial Catalog=YourDB;Integrated Security=True;

Offload reporting queries to snapshots:

CREATE DATABASE YourDB_Snapshot ON
(NAME = YourDB_Data, FILENAME = 'D:\Snapshots\YourDB.ss')
AS SNAPSHOT OF YourDB;

Implement a read/write router in your DAL:

public class DbRouter
{
    public SqlConnection GetWriteConnection()
    {
        return new SqlConnection(ConfigurationManager
           .ConnectionStrings["PrimaryWrite"].ConnectionString);
    }
    
    public SqlConnection GetReadConnection()
    {
        if(DateTime.Now.Second % 2 == 0) // Simple load balancing
            return new SqlConnection(ConfigurationManager
               .ConnectionStrings["Snapshot1"].ConnectionString);
        else
            return new SqlConnection(ConfigurationManager
               .ConnectionStrings["Snapshot2"].ConnectionString);
    }
}

For Windows Updates without downtime:

  1. Configure Windows Server Update Services (WSUS) to delay updates
  2. Use Database Mirroring Monitor to verify synchronization
  3. Failover manually before patching primary
  4. Apply updates to original primary
  5. Failback during low-traffic periods

Essential settings in connection strings:

Max Pool Size=200; 
Connection Timeout=30;
Connect Retry Count=3; 
Connect Retry Interval=10;

Monitor with this query:

SELECT 
    DB_NAME(database_id) AS DatabaseName,
    COUNT(*) AS ConnectionCount
FROM sys.dm_exec_connections
GROUP BY database_id;

For storage redundancy:

  • Configure RAID 10 for transaction logs
  • Use SAN storage with multipath I/O
  • Implement battery-backed write cache

While we're focusing on 2008 R2, consider this migration path:

-- Availability group creation (2012+ syntax)
CREATE AVAILABILITY GROUP [AG_YourDB]
WITH (
    AUTOMATED_BACKUP_PREFERENCE = SECONDARY,
    FAILURE_CONDITION_LEVEL = 3
)
FOR DATABASE [YourDB]
REPLICA ON 
    'PrimaryServer' WITH (
        ENDPOINT_URL = 'TCP://PrimaryServer:5022',
        AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
        FAILOVER_MODE = AUTOMATIC
    ),
    'SecondaryServer' WITH (
        ENDPOINT_URL = 'TCP://SecondaryServer:5022',
        AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
        FAILOVER_MODE = AUTOMATIC
    );

When your ASP.NET application starts hitting serious traffic, the database layer often becomes the critical bottleneck. SQL Server 2008 R2 presents unique challenges for high availability setups because it predates Always On Availability Groups (introduced in SQL Server 2012). Here's how we solved it for our SaaS product handling 50,000+ transactions per minute.

For SQL Server 2008 R2, database mirroring was our go-to solution. The setup requires:

-- On principal server
ALTER DATABASE YourDatabase 
SET PARTNER = 'TCP://MirrorServerName:5022'
GO

-- On mirror server
ALTER DATABASE YourDatabase 
SET PARTNER = 'TCP://PrincipalServerName:5022'
GO

Key configuration notes:

  • Use high-safety mode with automatic failover (requires witness server)
  • Configure connection strings with Failover Partner parameter
  • Monitor mirroring status with SQL Agent jobs

Actual read load balancing requires additional techniques:

// ASP.NET connection string with load balancing
Server=PrincipalDB;Failover Partner=MirrorDB;
Load Balance Timeout=30;MultiSubnetFailover=True;

We implemented these strategies:

  • Read-only routing using database snapshots
  • Manual read/write splitting in application code
  • Caching layer with Redis for frequent queries

For Windows updates without downtime:

  1. Failover to mirror server manually
  2. Patch primary server
  3. Failback after verification
  4. Repeat for mirror server

Automation script example:

USE master
GO
ALTER DATABASE YourDatabase SET PARTNER FAILOVER
GO

Beyond mirroring, we implemented:

  • Storage-level SAN replication
  • Automatic alerts on mirroring state changes
  • Connection retry logic in application
// C# retry pattern
int retries = 3;
while (retries > 0)
{
    try {
        using (var conn = new SqlConnection(connString)) {
            conn.Open();
            // Execute commands
            break;
        }
    }
    catch (SqlException) {
        retries--;
        Thread.Sleep(1000);
    }
}

Essential performance counters to monitor:

  • SQLServer:Database Mirroring - Redo Queue
  • SQLServer:Database Mirroring - Log Send Queue
  • SQLServer:Buffer Manager - Page life expectancy

After running this setup for 3 years:

  • Average failover time: 23 seconds
  • Maximum data loss during failures: 1 transaction
  • Uptime achieved: 99.94%