European Windows 2012 Hosting BLOG

BLOG about Windows 2012 Hosting and SQL 2012 Hosting - Dedicated to European Windows Hosting Customer

SQL Server Hosting - HostForLIFE.eu :: Zero-Downtime SQL Server Schema Modifications: A Safer.NET Deployment Method

clock September 16, 2026 11:32 by author Peter

Even if the application code is fine, a database schema change can bring down a.NET application. The ALTER TABLE statement is typically not the issue. Schema locks, ongoing transactions, current application code, indexes, constraints, and the sequence in which a new version of the application is released all interact with one another.

For instance, this appears innocuous:

ALTER TABLE dbo.Customers
ADD IsVerified bit NOT NULL DEFAULT 0;


On a busy production database, the same statement can have very different operational consequences depending on the table, SQL Server version, existing workload, and the exact definition of the change.

A safer approach is to treat database migrations as a deployment process rather than a collection of SQL statements.

Why Schema Changes Can Cause Downtime
SQL Server uses schema locks to protect database metadata while DDL operations are running.
An ALTER TABLE operation can require a schema modification lock (Sch-M). If another transaction is holding a conflicting lock, the schema change can wait.
The reverse can also happen. A schema change waiting for a lock can eventually block application requests.

The result can look like this:
Application requests
        |
        v
Long-running transaction
        |
        v
Schema migration waits
        |
        v
New requests begin waiting
        |
        v
Application latency increases


This is why a migration that executes successfully in a development environment can still create production problems.

The first question should therefore be:
Does this migration need to touch existing rows, rebuild an index, validate existing data, or acquire a long-lived schema lock?

The Expand-and-Contract Pattern

One of the safest patterns for .NET applications is expand and contract.
Instead of changing the database and application at the same time, introduce the new schema in stages.

Phase 1: Expand
Add the new database structure while keeping the existing application functional.
For example:
ALTER TABLE dbo.Customers
ADD DisplayName nvarchar(200) NULL;


The old application does not need this column, so it can continue working.

Phase 2: Deploy Compatible Application Code
Deploy application code that can work with both the old and new schema.

For example:
public sealed class Customer
{
    public int Id { get; set; }

    public string Name { get; set; } = string.Empty;

    public string? DisplayName { get; set; }
}

The application can initially continue using Name while the new column is populated.

Phase 3: Backfill
Populate existing rows in controlled batches instead of performing one enormous update.
UPDATE TOP (1000) dbo.Customers
SET DisplayName = Name
WHERE DisplayName IS NULL;

Repeat the operation until the required rows are populated.

Batching limits the amount of work performed by a single transaction and gives the application opportunities to continue processing between batches.

Phase 4: Switch Application Behavior
Once the new column contains the required data, deploy code that uses DisplayName.

Phase 5: Contract
Only after the application no longer depends on the old schema should you remove the old column or constraint.

ALTER TABLE dbo.Customers
DROP COLUMN Name;


The exact timing depends on whether older application instances can still be running.

Why Backward Compatibility Matters?

Consider a deployment where five application instances are running.

If the database migration removes a column immediately, an old application instance may still execute:
SELECT Id, Name
FROM dbo.Customers;

If Name has already been removed, that instance fails.

A safer deployment sequence is:
Old application
       |
       v
Add new schema
       |
       v
Deploy compatible application
       |
       v
Backfill data
       |
       v
Switch application
       |
       v
Remove old schema later


This approach also makes rollbacks easier because the previous application version can continue working with the expanded schema.

Not Every ALTER TABLE Is Equally Expensive

A common mistake is to classify every ALTER TABLE as either "safe" or "unsafe."
The actual behavior depends on the operation.

For example, SQL Server can add certain columns with metadata-only behavior when the default can be represented appropriately. In other cases, the operation needs to modify existing rows.

Consider:
ALTER TABLE dbo.Orders
ADD IsArchived bit NOT NULL
    CONSTRAINT DF_Orders_IsArchived DEFAULT 0;


This may be handled efficiently by SQL Server in supported scenarios, but you should not assume every ADD COLUMN operation is metadata-only.
The column type, default expression, row-size limits, existing objects, SQL Server version, and table design can affect the operation.
For production migrations, test the exact statement against a representative database.

Adding a NOT NULL Column Safely

Adding a nullable column is usually easier:
ALTER TABLE dbo.Customers
ADD PreferredLanguage nvarchar(20) NULL;


The application can begin writing the new value immediately.

If the final requirement is:
PreferredLanguage must never be NULL

do not necessarily enforce that constraint during the first deployment.

A safer sequence is:

  1. Add nullable column
  2. Deploy application support
  3. Populate existing rows
  4. Verify no NULL values remain
  5. Add NOT NULL constraint

Before enforcing the constraint, check the data:
SELECT COUNT(*) AS MissingValues
FROM dbo.Customers
WHERE PreferredLanguage IS NULL;


Only proceed when the result is zero.

Backfill Large Tables in Batches
Large data migrations can create their own availability problems.

Avoid:
UPDATE dbo.Customers
SET DisplayName = Name
WHERE DisplayName IS NULL;


on a very large production table without understanding the transaction size and workload.

A batch-oriented approach is easier to control:
WHILE 1 = 1
BEGIN
    UPDATE TOP (1000)
        dbo.Customers
    SET DisplayName = Name
    WHERE DisplayName IS NULL;

    IF @@ROWCOUNT = 0
        BREAK;

    WAITFOR DELAY '00:00:01';
END;


The batch size should be chosen based on the workload.
There is no universal value such as 1,000 or 10,000 that is correct for every production system.

Monitor:

  • transaction log growth
  • lock duration
  • CPU
  • I/O
  • query latency
  • replication or change capture lag
  • application error rates

The goal is controlled progress, not simply finishing the migration as quickly as possible.

Use Online Index Operations When Supported

Index creation and rebuilding can be expensive on large tables.

SQL Server supports online index operations in supported editions and scenarios:
CREATE INDEX IX_Customers_Email
ON dbo.Customers (Email)
WITH (ONLINE = ON);


An online operation allows normal queries and modifications to continue during most of the operation, but it does not mean the operation requires zero locking.

The beginning and final phases can still require locks, including a short Sch-M lock depending on the operation.

That final lock can matter on a busy system.

Control Blocking with WAIT_AT_LOW_PRIORITY

For supported online index operations, WAIT_AT_LOW_PRIORITY lets you control what happens when the operation cannot immediately obtain the required lock.

For example:
CREATE INDEX IX_Customers_Email
ON dbo.Customers (Email)
WITH
(
    ONLINE = ON
    (
        WAIT_AT_LOW_PRIORITY
        (
            MAX_DURATION = 5 MINUTES,
            ABORT_AFTER_WAIT = SELF
        )
    )
);


Here, the index operation waits at low priority for up to five minutes.
If the required lock still cannot be acquired, the operation aborts itself.
This is often safer than allowing a migration to wait indefinitely and unexpectedly affect application traffic.

Be careful with:
ABORT_AFTER_WAIT = BLOCKERS

That option can terminate user transactions blocking the operation and requires appropriate permissions.

Killing application transactions should be an explicit operational decision, not a default migration behavior.

Use Resumable Operations for Long Index Work
A large index operation may take longer than the maintenance window available to your application.

Supported SQL Server versions provide resumable index operations.

For example:
CREATE INDEX IX_Orders_CreatedAt
ON dbo.Orders (CreatedAt)
WITH
(
    ONLINE = ON,
    RESUMABLE = ON,
    MAX_DURATION = 60
);

A resumable operation can be paused and resumed:
ALTER INDEX IX_Orders_CreatedAt
ON dbo.Orders
PAUSE;

Then:

ALTER INDEX IX_Orders_CreatedAt
ON dbo.Orders
RESUME;


This can be useful when an operation needs to fit around controlled maintenance periods.

Resumable operations also provide recovery options for certain interruptions instead of requiring the entire operation to start again.

Adding Constraints Requires Planning

Constraints can expose bad data that already exists.

Suppose the application wants:
ALTER TABLE dbo.Customers
ADD CONSTRAINT UQ_Customers_Email
UNIQUE (Email);

Before running it, find duplicates:
SELECT Email, COUNT(*) AS DuplicateCount
FROM dbo.Customers
GROUP BY Email
HAVING COUNT(*) > 1;

If duplicates exist, the migration will not solve the data problem.

The application team needs a data-cleaning strategy first.

For supported SQL Server scenarios, adding primary key or unique constraints can also use resumable operations. This can be useful for large tables where building the supporting structure is expensive.

Handle Application and Database Deployment Together
A .NET application should not assume that a migration runs instantaneously.

For example, imagine introducing:
public bool IsArchived { get; set; }

The database migration must add the corresponding column before application instances start writing to it.

A deployment pipeline can use:
Build
  |
  v
Run compatibility migration
  |
  v
Deploy application
  |
  v
Backfill data
  |
  v
Validate
  |
  v
Enable new behavior
  |
  v
Remove legacy schema later

This is safer than:
Drop old column
  |
  v
Deploy new application


The second approach creates a period where the running application and database disagree about the schema.

EF Core Migrations Need the Same Discipline

Entity Framework Core makes schema changes easier to generate, but it does not automatically make every migration safe for production.

A generated migration might contain:
migrationBuilder.AddColumn<string>(
    name: "DisplayName",
    table: "Customers",
    type: "nvarchar(200)",
    nullable: true);

The code is valid, but production safety still depends on the actual database and workload.

For large tables, inspect the generated SQL before deployment.

You may need to split a logical change into multiple migrations:
Migration 1:
Add nullable column

Migration 2:
Application starts writing the column

Migration 3:
Backfill existing records

Migration 4:
Enforce NOT NULL or other constraints

Migration 5:
Remove legacy column


This takes more planning but gives the deployment process clear safety boundaries.

Test Migrations Against Production-Like Data
A migration test database containing 100 rows does not tell you how a migration behaves against a table containing hundreds of millions of rows.

A useful test environment should approximate:

  • table size
  • indexes
  • constraints
  • active connections
  • representative queries
  • transaction behavior
  • SQL Server configuration
  • database compatibility level

Measure the migration itself.

For example:
Migration duration
Lock wait duration
Transaction log growth
CPU usage
I/O usage
Application latency
Failed requests


Do not rely only on whether the migration completed successfully.

Common Mistakes
Changing and Removing in One Deployment
Dropping a column while deploying code that still references it creates an unnecessary failure window.

Assuming ONLINE Means No Blocking

Online operations reduce the duration of major blocking, but short lock acquisition phases can still affect production traffic.

Running a Huge Backfill in One Transaction

Large updates can increase log usage, locking, and recovery time.

Adding Constraints Without Checking Data

Existing duplicates or invalid values can cause the migration to fail.

Testing Only on Small Databases
Migration behavior can change significantly with production-sized data.

Automatically Killing Blockers

Using ABORT_AFTER_WAIT = BLOCKERS without understanding the workload can terminate legitimate transactions.
Treating EF Core Migrations as Risk-Free

EF Core generates migration code. It does not know your production traffic pattern or operational risk automatically.
Troubleshooting a Blocked Migration

When a migration is waiting, determine what it is waiting for before changing the SQL.

Check:
1. Which session is executing the migration?
2. Which session is blocking it?
3. Which object is locked?
4. How long has the blocking transaction been running?
5. Is the migration waiting for Sch-M?
6. Is application traffic increasing the blocking?

For index operations using low-priority waits, monitor the operation and its lock state rather than assuming it has failed.

If the migration repeatedly encounters blockers, the answer may be operational scheduling rather than a different SQL statement.

A Safer Migration Checklist
Before deploying a production schema change:

  • Identify the exact DDL operation.
  • Check whether existing rows must be modified.
  • Check the expected lock behavior.
  • Review indexes and constraints affected by the change.
  • Test against production-sized data.
  • Use expand-and-contract when application compatibility is required.
  • Batch large data backfills.
  • Use online index operations when supported and appropriate.
  • Consider WAIT_AT_LOW_PRIORITY for online index operations.
  • Consider resumable operations for long-running index or constraint work.
  • Monitor transaction log growth.
  • Define a rollback or forward-fix strategy.
  • Delay destructive cleanup until old application versions are no longer running.

Advantages and Disadvantages

Advantages

Disadvantages

Reduces the risk of application downtime

Requires multiple deployment stages

Supports rolling application deployments

Temporary schema objects may remain longer

Makes rollback easier

Backfills add operational work

Gives better control over locks

Large migrations still consume resources

Online and resumable operations can reduce maintenance impact

Feature availability depends on SQL Server version and operation

Works well with .NET and EF Core deployments

Requires testing against production-like data

A Practical Deployment Strategy

For a typical .NET application, the safest approach is to make schema changes compatible with more than one application version.

For example:

Release 1
  |
  +--> Add nullable column
  |
  v
Release 2
  |
  +--> Application writes new column
  |
  v
Backfill
  |
  +--> Existing records updated in batches
  |
  v
Release 3
  |
  +--> Application reads new column
  |
  v
Release 4
  |
  +--> Remove old column

The key idea is simple: do not make the database and application change their contract at the same moment when you can avoid it.

For .NET applications running against SQL Server, zero-downtime schema deployment is less about finding one magical ALTER TABLE statement and more about controlling compatibility, locks, data movement, and deployment order.

HostForLIFE.eu SQL Server 2022 Hosting
HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.



SQL Server Hosting - HostForLIFE.eu :: How to Use a Podman Container to Run SQL Server 2025 on Windows?

clock September 11, 2026 11:24 by author Peter

You may already have used Docker to run SQL Server within a container if you work with SQL Server.

Docker isn't the only choice, though. Another container program that can run Docker-compatible container images is Podman. This tutorial will demonstrate how to use Podman Desktop on Windows to run SQL Server 2025.

Additionally, we will use SQL Server Management Studio (SSMS) to connect to SQL Server and ensure that our database data is preserved between container restarts.

What is Podman?
Podman is an open-source tool for running and managing containers.
If you have used Docker before, Podman will feel very familiar.

For example, with Docker we use:
docker pull
docker run
docker ps
docker stop


With Podman, the commands are almost the same:
podman pull
podman run
podman ps
podman stop


One important difference is that Podman does not require a central daemon like Docker.
Podman also supports rootless containers, which means containers can run without giving them unnecessary root permissions.

For developers, the main point is simple:
We can run the official Microsoft SQL Server container image using Podman.

What Do We Need?

For this example, we need:

  • Windows 10 or Windows 11
  • WSL 2
  • Podman Desktop
  • SQL Server Management Studio (SSMS)
  • Enough RAM and disk space to run SQL Server

Step 1: Install WSL 2
Podman needs a Linux environment because the SQL Server container runs on Linux.
Open PowerShell as Administrator and check WSL:

wsl --status

If WSL is not installed, you can install it using:
wsl --install

If it is already installed, you can update it:
wsl --update

Restart Windows if required.

Step 2: Install Podman Desktop
Download and install Podman Desktop on your Windows machine.
During the setup, you can use WSL 2 as the machine provider.
Once the installation is complete, open PowerShell and run:
podman --version

You can also check the Podman machine:
podman machine list

If the machine is stopped, start it:
podman machine start

Now Podman is ready.

Step 3: Test Podman
Before installing SQL Server, let's make sure Podman is working.

Run:
podman run --rm hello-world
Podman will download the test image and run it.
If this works successfully, we can move to SQL Server.

Step 4: Download the SQL Server 2025 Image
Microsoft provides official SQL Server container images.

Run:
podman pull mcr.microsoft.com/mssql/server:2025-latest

The image is quite large, so downloading it may take some time depending on your internet connection.

After the download finishes, check it using:
podman images

You should see the SQL Server image in the list.

Step 5: Create Storage for SQL Server
Database data is important.
We don't want our database to disappear if we delete and recreate the container.

Let's create a Podman volume:
podman volume create sql2025data

Check the volume:
podman volume ls

We will use this volume to store SQL Server database files.

Step 6: Run SQL Server 2025
Now let's create our SQL Server container.

Open PowerShell and run:
podman run -d `
  --name sql2025 `
  --hostname sql2025 `
  -e ACCEPT_EULA=Y `
  -e MSSQL_SA_PASSWORD="YourStrongPasswordHere" `
  -p 1433:1433 `
  -v sql2025data:/var/opt/mssql `
  mcr.microsoft.com/mssql/server:2025-latest


Replace:
YourStrongPasswordHere

with a strong password.

Let's understand the command.
--name sql2025 gives our container the name sql2025.
ACCEPT_EULA=Y accepts the Microsoft SQL Server license agreement.
MSSQL_SA_PASSWORD sets the password for the SQL Server sa account.
-p 1433:1433 makes SQL Server available on port 1433.
-v sql2025data:/var/opt/mssql stores our SQL Server data in the Podman volume.

Step 7: Check SQL Server

Run:
podman ps

You should see the sql2025 container running.

You can also check SQL Server logs:
podman logs sql2025

SQL Server may take a few seconds to start.

If the container stops automatically, run:
podman ps -a

Then check the logs:
podman logs sql2025

The logs normally tell you why SQL Server failed to start.

Step 8: Connect from SSMS

Now open SQL Server Management Studio.
Enter:
Server Name: 127.0.0.1,1433

Authentication:
SQL Server Authentication

Login:
sa

Password:
Your SQL Server password


Click Connect.

You should now be connected to SQL Server 2025 running inside your Podman container.

Step 9: Check the SQL Server Version

Open a new query window in SSMS and run:
SELECT @@VERSION;

This will show the SQL Server version running inside the container.

You can also check the server name:
SELECT @@SERVERNAME;

Step 10: Create a Test Database
Let's create a simple database.

Run:
CREATE DATABASE PodmanDemo;
GO

Now use the database:
USE PodmanDemo;
GO


Create a table:
CREATE TABLE Employees
(
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Name NVARCHAR(100),
    Department NVARCHAR(100)
);
GO


Add some data:
INSERT INTO Employees (Name, Department)
VALUES
('John', 'Development'),
('Sarah', 'Database'),
('Mike', 'DevOps');
GO


Check the data:
SELECT *
FROM Employees;


Our SQL Server database is now running successfully inside Podman.

Step 11: Stop and Start SQL Server

You don't need to create the container every time.

To stop SQL Server:
podman stop sql2025

To start it again:
podman start sql2025

To restart it:
podman restart sql2025

After starting the container again, connect from SSMS and run:
USE PodmanDemo;

SELECT *
FROM Employees;

Your data should still be there.

Running Multiple SQL Server Containers
One useful feature of containers is that we can easily run multiple SQL Server environments.

Our first SQL Server uses port:
1433

We can run another SQL Server container using port 1434.

For example:
podman run -d `
  --name sql2025-test `
  -e ACCEPT_EULA=Y `
  -e MSSQL_SA_PASSWORD="YourStrongPasswordHere" `
  -p 1434:1433 `
  mcr.microsoft.com/mssql/server:2025-latest


Now we have two SQL Server instances.

The first one can be accessed using:
127.0.0.1,1433

And the second one using:
127.0.0.1,1434

This is very useful when you need separate development and testing databases.
Useful Podman Commands

Here are some commands you will probably use often.

Check running containers:
podman ps

Check all containers:
podman ps -a

Check SQL Server logs:
podman logs sql2025

Watch logs continuously:
podman logs -f sql2025

Stop SQL Server:
podman stop sql2025

Start SQL Server:
podman start sql2025

Restart SQL Server:
podman restart sql2025

Check CPU and memory usage:
podman stats

Open the Linux shell inside the SQL Server container:
podman exec -it sql2025 bash

Remove the container:
podman rm sql2025

Podman Desktop or Command Line?

Podman Desktop provides a graphical interface where you can see:

  • Containers
  • Images
  • Volumes
  • Pods
  • Logs
  • Podman machines

This is useful when you are getting started.

However, I prefer using commands for most development and DevOps work.

Commands are easy to save in documentation or scripts and can be reused on another machine.

Podman vs Docker
Podman and Docker solve a similar problem: they allow us to run applications inside containers. For basic SQL Server development, both work well.
Docker has a very large ecosystem and is supported by many development and CI/CD tools.

Podman has some interesting advantages. It is open source, supports rootless containers, does not require a central Docker-style daemon, and its commands are very similar to Docker.

If you already know Docker, learning Podman is quite easy.

For example:
docker ps

becomes:
podman ps

and:
docker run

becomes:
podman run

So you don't have to learn everything again.

Common Problems
SQL Server Container Stops Immediately

Check the logs:
podman logs sql2025

One common reason is that the sa password does not meet SQL Server password requirements.

SSMS Cannot Connect

First check that the container is running:
podman ps

Then try connecting using:
127.0.0.1,1433

Port 1433 Is Already in Use
Another SQL Server instance may already be using port 1433.

Use another port:
-p 1434:1433

Then connect using:
127.0.0.1,1434

Database Data Is Lost
Don't store important databases only inside the container.

Use a Podman volume:
-v sql2025data:/var/opt/mssql

This keeps the database files separate from the container.

When Should You Use Podman?
Podman is a good option if you want:

  • An alternative to Docker Desktop
  • Rootless containers
  • An open-source container tool
  • Local SQL Server development environments
  • Separate SQL Server environments for development and testing
  • A container tool that works well with Linux and Kubernetes environments

It is also useful for developers who want to learn more about container technologies without depending only on Docker.

Conclusion
Running SQL Server 2025 using Podman is quite simple. We installed Podman Desktop, downloaded Microsoft's official SQL Server image, created persistent storage, started SQL Server, and connected to it using SSMS. The best part is that developers who already know Docker will find Podman very familiar.  Commands such as pull, run, ps, stop, and start work almost the same way. For local development, testing, database migration testing, and DevOps environments, running SQL Server with Podman is a useful option. Docker is still a great container platform, but Podman gives us another good choice, especially if we want an open-source, daemonless, and rootless container solution.

HostForLIFE.eu SQL Server 2022 Hosting
HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.



SQL Server Hosting - HostForLIFE.eu :: Maintaining Consistency Across Multi-Team SQL Projects

clock September 8, 2026 12:25 by author Peter

Several teams frequently collaborate inside the same database in contemporary engineering firms. Over time, inconsistent SQL writing has become the largest performance bottleneck rather than wasteful queries. Subtle friction is introduced by disparate name standards, formatting styles, and query patterns. These variations appear innocuous on their own. When taken as a whole, they raise cognitive burden, impede growth, and make systems more difficult to sustain.

This article explains the importance of SQL consistency at scale and how teams can use automation and organized procedures to successfully enforce it.

The Hidden Cost of SQL Inconsistency
Inconsistent SQL does not break systems immediately. Instead, it introduces ongoing inefficiencies that affect daily engineering work.
Common issues include:

  • Naming inconsistencies: CustomerId, customer_id, and cust_id all refer to the same concept but require mental translation.
  • Formatting differences: Variations in indentation, casing, and query structure slow down code reviews.
  • Multiple solutions to the same problem: Different developers implement similar logic in different ways, increasing maintenance complexity.

Example:
-- Version 1
SELECT customer_id, MAX(order_date)
FROM orders
GROUP BY customer_id;

-- Version 2
SELECT o.customer_id, o.order_date
FROM orders o
WHERE o.order_date = (
    SELECT MAX(order_date)
    FROM orders
    WHERE customer_id = o.customer_id
);


Both queries produce the same result, but inconsistent approaches make code harder to standardize and maintain.

Why Documentation Alone Fails

Most teams maintain SQL style guides. However, documentation alone is not enough to enforce consistency.

Typical challenges include:

  • Different interpretations of the same rules
  • Reliance on senior developers for decisions
  • Variations introduced by different tools and editors

As a result, pull requests often turn into style discussions instead of focusing on correctness and performance.

Moving from Guidelines to Enforcement
To achieve consistency, rules must be embedded into the development workflow.

This includes:

  • Defining a shared formatting standard
  • Enforcing naming conventions automatically
  • Detecting risky query patterns early
  • Integrating validation into CI/CD pipelines

When enforcement is automated, consistency becomes the default rather than a manual effort.

Practical Implementation
1. Standardized Formatting

Use a consistent formatting style across all SQL queries.
SELECT
    customer_id,
    COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
ORDER BY order_count DESC;

This ensures readability and reduces review time.

2. Naming Conventions

Define and enforce rules such as:

  • Use snake_case for table and column names
  • Avoid abbreviations unless standardized
  • Use consistent prefixes for keys (e.g., customer_id)

3. Automated Validation in CI/CD
Integrate SQL linting and validation into your pipeline:

  • Reject queries that do not follow formatting rules
  • Flag missing WHERE clauses in UPDATE/DELETE
  • Prevent full table scans on large datasets

4. Safe Query Practices
Introduce safeguards for shared environments:
-- Risky
SELECT * FROM large_table;

-- Safer
SELECT * FROM large_table LIMIT 100;

This prevents accidental performance degradation.

Impact on Code Reviews and Onboarding

When SQL is standardized:

  • Reviews focus on logic, not formatting
  • Feedback cycles become faster
  • New developers ramp up quickly

Without consistency, developers spend time interpreting structure instead of solving problems.

Before vs After

AspectWithout ConsistencyWith Consistency

Code Reviews

Slow, style-focused

Fast, logic-focused

Onboarding

Steep learning curve

Faster ramp-up

Maintenance

High effort

Predictable and manageable

Risk

Higher

Reduced

Strategic Takeaways

  • Consistency is not cosmetic; it directly impacts scalability
  • Rules must be enforceable, not just documented
  • Automation reduces human error and review overhead
  • SQL should follow the same discipline as application code

Conclusion
Shared databases are more complicated as businesses expand. Small variations in SQL add up to major operational difficulties in the absence of uniform standards. Teams may increase cooperation, lower friction, and maintain high-quality database systems by establishing explicit rules and integrating them into automated procedures. Clean code is only one aspect of consistency. It involves creating mechanisms that effectively grow with your team.

HostForLIFE.eu SQL Server 2022 Hosting
HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.



SQL Server Hosting - HostForLIFE.eu :: Microsoft.Data.SqlClient Retry Logic: Building Resilient .NET SQL Server Applications

clock September 1, 2026 10:55 by author Peter

For essential functions like authentication, transactions, reporting, and business processes, modern.NET applications frequently rely on SQL Server. Network disruptions, connection pool pressure, database failovers, or transitory infrastructure problems can cause temporary failures even when an application and database are configured appropriately.

An application can recover from these transient errors without promptly delivering an error to the user by using a retry strategy. Microsoft.Data.The built-in retry features of the SqlClient library may be set up for SQL Server connections and procedures. This article describes the operation of retry logic, how to set it up in a.NET application, when to use it, and what production errors to prevent.

What Is Retry Logic in Microsoft.Data.SqlClient?
Retry logic allows Microsoft.Data.SqlClient to automatically retry certain operations when a transient SQL Server failure occurs.

Instead of following this pattern:
Application
    ↓
SQL Server
    ↓
Temporary failure
    ↓
Application returns error


a retry-enabled application can follow:
Application
    ↓
SQL Server
    ↓
Temporary failure
    ↓
Wait
    ↓
Retry
    ↓
SQL Server
    ↓
Success


This is particularly useful for cloud-hosted databases and distributed applications where temporary connectivity problems can occur. Retry logic is not intended to hide permanent failures. For example, an invalid SQL statement, authentication failure, or missing table generally cannot be fixed by repeatedly executing the same operation.

Why Transient SQL Failures Happen
Transient failures are temporary conditions where the same operation may succeed if attempted again after a short delay.

Common examples include:

Failure scenarioRetry potentially useful?Reason
Temporary network interruption Yes Connectivity may recover
SQL Server failover Yes New connection may become available
Temporary resource pressure Yes Database resources may become available
Connection timeout during transient infrastructure issue Sometimes A later attempt may succeed
Invalid SQL syntax No The command itself is incorrect
Invalid credentials No Retrying does not fix authentication
Missing database/table No Requires configuration or code changes
Constraint violation Usually no The application data needs correction

The important distinction is between transient and non-transient failures.

Configuring Retry Logic with Microsoft.Data.SqlClient
The retry functionality is available through Microsoft.Data.SqlClient.

A basic connection can be created like this:
using Microsoft.Data.SqlClient;

var connectionString =
    "Server=localhost;Database=SalesDb;Integrated Security=True;TrustServerCertificate=True;";

using var connection = new SqlConnection(connectionString);

await connection.OpenAsync();


To add retry behavior, configure a retry provider and assign it to the connection.

For example:
using Microsoft.Data.SqlClient;

var connectionString =
    "Server=localhost;Database=SalesDb;Integrated Security=True;TrustServerCertificate=True;";

var retryProvider = SqlConfigurableRetryFactory.CreateExponentialRetryProvider(
    retryCount: 5,
    maxTimeInterval: TimeSpan.FromSeconds(10),
    deltaTime: TimeSpan.FromSeconds(1));

using var connection = new SqlConnection(connectionString)
{
    RetryLogicProvider = retryProvider
};

await connection.OpenAsync();

Here, the retry provider controls how the client responds when a retryable transient error occurs.

Understanding the Retry Parameters

The following values are important:
SqlConfigurableRetryFactory.CreateExponentialRetryProvider(
    retryCount: 5,
    maxTimeInterval: TimeSpan.FromSeconds(10),
    deltaTime: TimeSpan.FromSeconds(1));

retryCount specifies how many retry attempts can be made.
maxTimeInterval limits the maximum delay between retry attempts.
deltaTime controls the delay growth used by the exponential retry strategy.
The goal is to avoid immediately sending the same failed request repeatedly.

Exponential Backoff and Why It Matters
A fixed retry strategy might look like this:
Attempt 1 → Fail
Wait 1 second
Attempt 2 → Fail
Wait 1 second
Attempt 3 → Fail


This can put unnecessary pressure on an already struggling database.

Exponential backoff gradually increases the waiting period:
Attempt 1 → Fail
Wait
Attempt 2 → Fail
Wait longer
Attempt 3 → Fail
Wait even longer
Attempt 4 → Success


This gives the underlying infrastructure time to recover.

In distributed systems, exponential backoff is generally more appropriate than aggressively retrying the same request without delay.
Using Retry Logic with ASP.NET Core

In an ASP.NET Core application, it is common to centralize database configuration instead of creating connections throughout the application.

For example:
using Microsoft.Data.SqlClient;

var builder = WebApplication.CreateBuilder(args);

var connectionString =
    builder.Configuration.GetConnectionString("DefaultConnection");

var retryProvider =
    SqlConfigurableRetryFactory.CreateExponentialRetryProvider(
        retryCount: 5,
        maxTimeInterval: TimeSpan.FromSeconds(10),
        deltaTime: TimeSpan.FromSeconds(1));

builder.Services.AddScoped<SqlConnection>(_ =>
{
    return new SqlConnection(connectionString)
    {
        RetryLogicProvider = retryProvider
    };
});

var app = builder.Build();

app.MapGet("/products", async (SqlConnection connection) =>
{
    await connection.OpenAsync();

    using var command = new SqlCommand(
        "SELECT Id, Name, Price FROM Products",
        connection);

    using var reader = await command.ExecuteReaderAsync();

    var products = new List<object>();

    while (await reader.ReadAsync())
    {
        products.Add(new
        {
            Id = reader.GetInt32(0),
            Name = reader.GetString(1),
            Price = reader.GetDecimal(2)
        });
    }

    return Results.Ok(products);
});

app.Run();


The main benefit of this approach is consistency. Database connections created through the configured dependency-injection registration receive the same retry configuration.
For larger applications, you would typically keep database access inside a repository or data-access service rather than placing SQL directly inside an endpoint.

Connection Retry vs Command Retry
One important aspect of Microsoft.Data.SqlClient retry logic is understanding what is actually being retried.

A database operation can involve:

  • Opening a connection
  • Executing a command
  • Reading results
  • Committing a transaction

Retry behavior must therefore be considered carefully, particularly for commands that modify data.

A read operation such as:
SELECT Id, Name
FROM Products
WHERE Id = @id

is generally easier to retry safely.

A write operation such as:
UPDATE Accounts
SET Balance = Balance - @amount
WHERE Id = @id

requires more consideration.

If the server processed the update but the client lost the connection before receiving the response, blindly retrying could potentially execute the business operation again.

The key issue is idempotency.

Designing Retry-Safe Database Operations

Before enabling aggressive retry behavior for write operations, determine whether repeating the operation produces the same intended result.

For example, this operation is not naturally idempotent:
UPDATE Accounts
SET Balance = Balance - 100
WHERE Id = 10;

Running it twice subtracts the amount twice.
A better design can use an operation identifier or another mechanism that allows the application to recognize an already-processed request.

For example:
INSERT INTO PaymentOperations
(
    OperationId,
    AccountId,
    Amount
)
VALUES
(
    @OperationId,
    @AccountId,
    @Amount
);


A unique constraint on OperationId can help prevent duplicate processing. The exact implementation depends on the application's business requirements, but the principle is important:
Retrying infrastructure operations is different from safely retrying business operations.

Retry Logic Compared with Polly

.NET applications have traditionally used libraries such as Polly for resilience strategies. 
Microsoft.Data.SqlClient retry logic and Polly can solve related but different problems.

CapabilityMicrosoft.Data.SqlClientPolly
SQL Server-specific retry Excellent Requires configuration
Database-aware transient handling Built in Application-defined
HTTP retry No Yes
Circuit breaker No Yes
Timeout policies Limited to client behavior Yes
Broader application resilience Limited Strong
SQL-specific configuration Simple More custom

If your requirement is specifically SQL Server client retry behavior, the built-in Microsoft.Data.SqlClient functionality can be a straightforward choice. If you need a broader resilience pipeline covering HTTP calls, caching, messaging, database operations, and other dependencies, an application-level resilience library may be more appropriate.

The two approaches should not be added blindly on top of each other. Layering multiple retry policies can result in unexpectedly large numbers of attempts.

Best Practices for Production

1. Keep Retry Counts Reasonable
More retries do not automatically mean better reliability.
A request that fails repeatedly can hold application resources and increase latency.
Start with conservative values and adjust them based on the application's requirements.

2. Use Exponential Backoff

Avoid immediate retry loops.
An exponential strategy gives SQL Server and the underlying infrastructure time to recover.

3. Do Not Retry Every Exception
Retry logic should only apply to failures that are actually transient.
An authentication problem will not normally be solved by five more login attempts.

4. Consider Operation Idempotency
Pay particular attention to:

  • Payments
  • Account balances
  • Inventory updates
  • Order creation
  • Message processing
  • Other state-changing operations

A retry can turn a temporary connection problem into a duplicate business operation if the design is not idempotent.

5. Monitor Retries
Retries can hide problems if they are not observable. 
Track useful information such as:

  1. Number of retry attempts
  2. Operation duration
  3. Final failure
  4. Database server
  5. Exception type
  6. Application endpoint or operation

A system that succeeds only after multiple retries may appear healthy to users while the underlying database infrastructure is experiencing problems.

Common Mistakes
Setting an Excessive Retry Count

A configuration such as:
retryCount: 50

may keep requests alive for an unnecessarily long time.

Retry policies should be based on the application's acceptable latency and failure-recovery requirements.

Retrying Permanent Errors
Retrying invalid SQL repeatedly wastes resources.
Fix the underlying problem instead of increasing the retry count.

Adding Multiple Retry Layers

For example:
HTTP retry
    ↓
Service retry
    ↓
Repository retry
    ↓
SqlClient retry

A single failed database operation can potentially trigger many actual database requests.
Define clear ownership of retry behavior.

Ignoring Transactions

Transactions require special attention because a connection failure does not always tell the client whether the server completed the transaction. Do not assume that an unsuccessful client response means that the database definitely rolled back the operation.

Troubleshooting Retry Behavior
When retry logic does not behave as expected, check the following:

  • Verify that the application is using Microsoft.Data.SqlClient, not an unrelated SQL client package.
  • Confirm that the retry provider is assigned to the connection or command configuration being used.
  • Check the actual exception and SQL error information.
  • Verify that the failure is transient and eligible for retry.
  • Check application logs for repeated attempts.
  • Review connection and command timeout settings.
  • Check SQL Server health, networking, resource utilization, and failover events.
  • Make sure another resilience layer is not already retrying the same operation.

Logging should make it possible to distinguish an operation that succeeded immediately from one that succeeded only after several retries.

Advantages and Disadvantages
Advantages

  • Built specifically for SQL Server client operations
  • Reduces failures caused by temporary connectivity problems
  • Supports configurable retry behavior
  • Exponential retry strategies reduce aggressive retry traffic
  • Can simplify SQL-specific resilience configuration
  • Works naturally with Microsoft.Data.SqlClient

Disadvantages

  • Does not solve permanent database failures
  • Excessive retries can increase application latency
  • Retrying write operations can introduce duplicate business operations
  • It does not replace broader application resilience patterns
  • Multiple retry layers can create unexpectedly high request counts

Conclusion
Transient database failures are a normal consideration for distributed .NET applications, particularly when applications depend on remote or cloud-hosted SQL Server environments. Microsoft.Data.SqlClient provides a practical way to introduce SQL-aware retry behavior without implementing every retry mechanism manually. Exponential backoff, sensible retry limits, correct transient-error handling, and good observability are the key pieces of a reliable implementation.

However, retry logic should not be treated as a universal solution. The most important production consideration is understanding what is being retried and whether repeating that operation is safe. For read-heavy workloads, carefully configured SqlClient retry logic can provide useful resilience against temporary database connectivity problems. For write-heavy or transactional workloads, combine retry configuration with idempotent application design, transaction awareness, monitoring, and an overall resilience strategy.

HostForLIFE.eu SQL Server 2022 Hosting
HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Month List

Tag cloud

Sign in