HTTP Connection in.NET 11 Eviction: DNS Failover Testing for Extended APIs

Outbound HTTP calls are made by the majority of ASP.NET Core apps. They could make calls to third-party platforms, cloud services, internal microservices, payment services, and authentication APIs.

HttpClient often has a lengthy lifespan and depends on connection pooling for these applications. Reusing connections is a crucial component of effective HTTP communication as it eliminates the expense of creating a new TCP connection for each request.

However, enduring relationships create a new issue.

When a service behind a hostname modifies its IP address, what happens?

This can happen during:

  • Deployment
  • Failover
  • DNS-based load balancing
  • Infrastructure migration
  • Disaster recovery
  • Service scaling

If an application keeps using an existing connection for too long, changing DNS information does not necessarily mean that an already-established connection immediately moves to the new destination.

.NET 11 introduces configurable HTTP connection eviction capabilities that provide developers with more control over when pooled connections should be removed.

This article explains the problem, shows how to configure connection lifetime behavior, and demonstrates how to test DNS failover without claiming that a particular configuration is universally optimal.

Why DNS Changes Do Not Automatically Move Existing Connections

Consider an API client calling:

https://api.example.com

Initially, DNS resolves the hostname to:

api.example.com
       |
       v
10.0.0.10

Later, the service moves:

api.example.com
       |
       v
10.0.0.20

A new DNS lookup can discover the new address.

However, an application may already have an established HTTP connection to the previous destination.

Conceptually:

Existing connection
        |
        v
10.0.0.10

DNS now points to
        |
        v
10.0.0.20

The existing connection does not become a connection to the new IP simply because DNS changed.

That is where connection lifetime management becomes important.

How HttpClient Connection Pooling Works

A common pattern is to reuse a single HttpClient rather than creating a new instance for every request.

For example:

builder.Services.AddHttpClient<PaymentClient>(client =>
{
    client.BaseAddress =
        new Uri("https://payments.example.com");
});
C#

The underlying HTTP handler maintains connections and can reuse them for subsequent requests.

This is generally preferable to repeatedly doing:

using var client = new HttpClient();

await client.GetAsync("https://payments.example.com");
C#

Creating clients repeatedly can prevent effective connection reuse and can introduce unnecessary connection-management overhead.

The important point is that a long-lived client does not mean every request creates a new network connection.

That is normally a benefit, but it also means connection lifetime needs to be considered when infrastructure changes.

What Is Connection Eviction?

Connection eviction means removing a connection from the pool after a defined condition is reached.

One useful condition is age.

Conceptually:

Connection created
       |
       v
Connection reused
       |
       v
Connection reused
       |
       v
Maximum lifetime reached
       |
       v
Connection removed
       |
       v
New connection created
       |
       v
DNS can be resolved again

The exact behavior depends on the HTTP handler and configuration being used.

The purpose is not to force a new connection for every request.

It is to periodically give the application an opportunity to establish a fresh connection.

Why DNS Failover Testing Matters

Suppose an application calls a backend service continuously.

At 10:00:

api.internal
     |
     v
10.0.0.10

At 10:30, the backend fails and DNS is updated:

api.internal
     |
     v
10.0.0.20

If the application keeps using an existing connection to 10.0.0.10, requests can continue failing until that connection is closed or becomes unusable.

Connection eviction provides one mechanism for limiting how long such connections remain in the pool.

However, it is not a replacement for proper retry, timeout, health-check, and failover strategies.

Configuring HttpClient

A typical HttpClient registration looks like this:

builder.Services.AddHttpClient<BackendClient>(client =>
{
    client.BaseAddress =
        new Uri("https://api.example.com");
});
C#

Connection lifetime settings are configured on the underlying HTTP handler.

For example, with SocketsHttpHandler:

builder.Services.AddHttpClient<BackendClient>()
    .ConfigurePrimaryHttpMessageHandler(() =>
        new SocketsHttpHandler
        {
            PooledConnectionLifetime =
                TimeSpan.FromMinutes(5)
        });
C#

The lifetime shown here is an example configuration, not a universal recommendation.

The correct value depends on DNS TTLs, service behavior, infrastructure, traffic patterns, and the application’s tolerance for reconnecting.

Choosing a Connection Lifetime

A common mistake is choosing an arbitrary lifetime such as five minutes and assuming that it guarantees reliable failover.

It does not.

The configuration should be based on the environment.

For example:

DNS changes every 10 minutes
        |
        v
Connection lifetime should be evaluated
against that infrastructure behavior

The goal is to balance two competing concerns.

Longer Connection Lifetime

Advantages:

  • More connection reuse
  • Fewer connection establishments
  • Less connection-management overhead

Potential drawback:

  • Existing connections can remain around longer

Shorter Connection Lifetime

Advantages:

  • Connections are refreshed more frequently
  • DNS changes can be discovered sooner through new connections

Potential drawback:

  • More connection establishments
  • Potentially higher connection-management overhead

There is no single value that is correct for every application.

Building a DNS Failover Test

A useful test environment should contain two backend endpoints.

For example:

Service A
10.0.0.10

Service B
10.0.0.20

The client uses:

api.test.local

Initially, DNS points to Service A.

The client sends repeated requests:

Request 1 -> Service A
Request 2 -> Service A
Request 3 -> Service A

Then change the DNS record to Service B.

The test should continue sending requests and record which backend receives them.

This allows you to observe how the client behaves as connections age out and new connections are established.

Recording the Backend

Make each backend return an identifier.

For example:

app.MapGet("/health", () =>
{
    return Results.Ok(new
    {
        Server = Environment.GetEnvironmentVariable(
            "SERVER_ID") ?? "unknown"
    });
});
C#

Run the same application twice with different environment variables:

SERVER_ID=backend-a
SERVER_ID=backend-b

The client can then log the response:

var response = await client.GetFromJsonAsync<BackendResponse>(
    "/health");

Console.WriteLine(response?.Server);
C#

This gives you a simple way to observe where requests are going.

Simulating DNS Changes

For a controlled local test, you can use a test DNS environment or host-level name resolution.

For example, a hosts file entry can be used for a simple local experiment:

127.0.0.1 api.test.local

For more realistic DNS behavior, use an actual DNS service or test environment where the record can be changed while the client remains active.

The important requirement is that the hostname stays the same while its destination changes.

If you simply change the URL from one server to another in the application, you are not testing DNS failover.

Testing Connection Eviction

Start with a long connection lifetime.

For example:

PooledConnectionLifetime =
    TimeSpan.FromMinutes(30)
C#

Start continuous requests:

Request -> backend A
Request -> backend A
Request -> backend A

Change DNS to backend B.

Continue the test.

Then repeat with a shorter lifetime:

PooledConnectionLifetime =
    TimeSpan.FromMinutes(2)
C#

The exact timing and observed behavior should come from the test environment.

The purpose is to compare how quickly new connections begin using the new destination.

Measuring Failover Behavior

Record at least these values:

Metric What It Tells You
DNS change time When infrastructure changed
First new backend request When new destination was observed
Failed requests Impact during transition
Connection lifetime Client configuration
Request interval How frequently the client sends traffic
Recovery time Time until normal behavior returns

For example:

DNS changed
   |
   v
Requests continue
   |
   v
Existing connection reused
   |
   v
Connection evicted
   |
   v
New connection created
   |
   v
New DNS destination used

This makes the failover process easier to understand.

Connection Eviction Is Not a Failover Mechanism by Itself

This distinction is important.

Connection lifetime management can help an application discover infrastructure changes over time.

It does not guarantee that every request will succeed during a failure.

A production application should also consider:

  • Timeouts
  • Retries
  • Retry limits
  • Exponential backoff
  • Circuit breakers
  • Health checks
  • Load balancers
  • Service discovery
  • Idempotency

For example, a failed payment request should not simply be retried blindly.

Retry behavior depends on whether the operation is safe to repeat.

Adding Timeouts

An HTTP client should have an appropriate timeout.

For example:

builder.Services.AddHttpClient<BackendClient>(client =>
{
    client.BaseAddress =
        new Uri("https://api.example.com");

    client.Timeout =
        TimeSpan.FromSeconds(10);
});
C#

The value should reflect the actual service contract.

A timeout that is too short can generate unnecessary failures.

A timeout that is too long can keep resources occupied during a backend outage.

Adding Retry Logic Carefully

Transient failures may justify retry behavior.

However, retries should be bounded.

Conceptually:

Request
  |
  v
Failure
  |
  v
Wait
  |
  v
Retry
  |
  v
Failure
  |
  v
Stop

Do not create an unlimited retry loop.

For write operations, verify that the operation is safe to repeat before automatically retrying it.

Common Mistakes

Assuming DNS Changes Existing Connections

DNS resolution and established connections are different things.

Changing a DNS record does not rewrite an already-open TCP connection.

Setting an Extremely Short Lifetime

A very short lifetime can increase connection establishment overhead.

The goal is controlled refresh, not constant reconnection.

Ignoring DNS TTL

Connection lifetime and DNS behavior should be considered together.

A client setting cannot compensate for an infrastructure design that is fundamentally inconsistent.

Testing by Changing the URL

Changing:

api-a.example.com

to:

api-b.example.com

does not test DNS failover.

The hostname must remain the same while its destination changes.

Relying Only on Connection Eviction

Connection eviction is one part of connection management.

It should not replace timeout and failure-handling strategies.

Troubleshooting

If the client does not appear to move to the new backend, check:

  1. Whether the DNS record actually changed.
  2. Whether the client is still using an existing connection.
  3. Whether the configured lifetime has elapsed.
  4. Whether a proxy is involved.
  5. Whether a load balancer is maintaining its own connections.
  6. Whether DNS caching exists elsewhere in the path.
  7. Whether the backend is actually reachable.
  8. Whether requests are being retried.
  9. Whether the client is using HTTP/1.1 or HTTP/2.
  10. Whether the test environment accurately represents production.

Network infrastructure can make this behavior more complicated than a simple DNS lookup.

HTTP/2 Considerations

Modern applications may use HTTP/2, where multiple requests can share a single connection.

That makes connection lifetime behavior particularly relevant because one connection can carry many concurrent requests.

The conceptual model becomes:

HTTP/2 connection
       |
       +--> Request 1
       +--> Request 2
       +--> Request 3
       +--> Request 4

Closing or replacing that connection can affect multiple requests.

This is another reason not to choose connection lifetimes purely by guesswork.

Test the protocol and workload your application actually uses.

Production Considerations

Connection eviction should be considered together with the rest of the network architecture.

A typical production path might be:

Application
    |
    v
HttpClient
    |
    v
Proxy / Load Balancer
    |
    v
Service Discovery / DNS
    |
    v
Backend Instances

There may be multiple layers of connection pooling and caching.

Changing PooledConnectionLifetime in the application does not necessarily control every connection in the architecture.

Document where DNS resolution occurs and where connections are pooled before using connection lifetime as a failover strategy.

Best Practices

Reuse HttpClient

Use IHttpClientFactory or an appropriately managed long-lived HttpClient.

Configure Connection Lifetime Deliberately

Choose a value based on infrastructure requirements rather than an arbitrary number.

Understand DNS Behavior

Know the DNS TTL and where DNS resolution occurs.

Use Timeouts

Every external dependency should have a reasonable timeout.

Handle Transient Failures

Use bounded retry strategies where appropriate.

Test Actual Failover

Change the destination behind the same hostname and observe real request behavior.

Monitor Failover

Production systems should expose enough telemetry to identify connection failures, backend changes, and recovery times.

Advantages

  • Helps control how long pooled connections remain active.
  • Can allow clients to establish fresh connections periodically.
  • Useful when backend IP addresses can change.
  • Works naturally with long-lived HttpClient instances.
  • Provides more control over connection-pool behavior.

Disadvantages

  • Short lifetimes can increase connection-establishment overhead.
  • Connection eviction alone does not guarantee successful failover.
  • DNS, proxies, load balancers, and other infrastructure can affect the observed behavior.
  • The correct lifetime depends on the deployment architecture.
  • Poorly chosen settings can trade one problem for another.

Conclusion

Long-lived HTTP connections are an important performance feature, but they can become complicated when the destination behind a hostname changes.

.NET’s HTTP connection management provides ways to control how long pooled connections remain available. By configuring connection lifetime deliberately, an application can periodically establish fresh connections and give DNS changes an opportunity to take effect.

The right approach is not to choose the shortest possible lifetime.

Instead, understand the application’s DNS behavior, backend architecture, request frequency, HTTP protocol, and failure-handling requirements. Then test the actual failover scenario by changing the destination behind the same hostname.

Finally, treat connection eviction as one part of a broader reliability strategy. Timeouts, retries, health checks, load balancing, and service discovery still matter.

A controlled DNS failover test can reveal problems that normal functional testing will never expose, making it a useful addition to production-readiness testing for long-lived .NET APIs.

Best and Most Recommended ASP.NET Core 10.0 Hosting

Fortunately, there are a number of dependable and recommended web hosts available that can help you gain control of your website’s performance and improve your ASP.NET Core 10.0 web ranking. HostForLIFE.eu is highly recommended. In Europe, HostForLIFE.eu is the most popular option for first-time web hosts searching for an affordable plan. Their standard price begins at only €3.49 per month. Customers are permitted to choose quarterly and annual plans based on their preferences. HostForLIFE.eu guarantees “No Hidden Fees” and an industry-leading ’30 Days Cash Back’ policy. Customers who terminate their service within the first thirty days are eligible for a full refund.

By providing reseller hosting accounts, HostForLIFE.eu also gives its consumers the chance to generate income. You can purchase their reseller hosting account, host an unlimited number of websites on it, and even sell some of your hosting space to others. This is one of the most effective methods for making money online. They will take care of all your customers’ hosting needs, so you do not need to fret about hosting-related matters.