Production Engineering · Reliability · Sitecore

Root Cause Over Restarts: Resolving Sitecore Thread Exhaustion Across 65 Production Websites

How I traced recurring SSL failures to unnecessary xConnect retries, isolated a configuration issue across two production nodes, and rolled out a targeted fix without platform-wide downtime.

When 65 websites share a reliability problem

I investigated recurring stability issues in a production Sitecore environment supporting approximately 65 brand websites. Two load-balanced content delivery servers, which I'll call Server A and Server B, were experiencing resource pressure and partial degradation. Because so many websites shared those application nodes, a configuration problem could affect the entire portfolio.

The goal was not to make the logs look cleaner or temporarily recover by restarting IIS. I needed to understand which application behavior was consuming resources, identify the underlying cause, and implement a controlled fix without taking the websites offline.

Quantifying the failure before changing anything

I started with evidence from the Sitecore application logs. On one day, Server A recorded 1,829 SSL-related errors and Server B recorded 1,706—a combined 3,535 errors. Similar failures on both nodes suggested a shared dependency or configuration problem rather than an isolated server interruption.

An online server is not necessarily a healthy server. Repeated failed outbound requests can consume the resources needed to serve legitimate inbound traffic, even while health checks continue to respond.

The PowerShell snippet below illustrates the counting approach, not a verbatim copy of my production command. In practice, I would use the specific exception text and same measurement window on both nodes.

# Illustrative log check; adapt the path and pattern to the environment.
Get-ChildItem "C:\Sitecore\Data\logs" -Filter "log.*.txt" |
  Select-String -Pattern "SSL|TLS|secure channel" |
  Measure-Object

Following the dependency rather than chasing symptoms

The recurring exceptions pointed to Sitecore's xConnect/xDB services. I examined endpoint reachability and the TLS/certificate-related failure path, but the bigger architectural question was whether the content delivery nodes needed to make those calls at all.

xConnect supplies access to Sitecore experience data; xDB enables analytics and tracking capabilities. In this environment, the delivery tier was intended to run without active xDB analytics collection. Nevertheless, the CD nodes still had xDB-related behavior enabled.

When connection attempts failed, retry behavior associated with Polly caused more outbound attempts. Repeated retries against a dependency that the application does not need can amplify the initial failure by occupying execution resources. In this case, the activity contributed to IIS/.NET thread-pool exhaustion and degraded request handling.

The root cause was an unnecessary runtime dependency

The diagnosis was a configuration mismatch: the delivery nodes were trying to use xConnect functionality despite not requiring analytics collection in the intended deployment mode.

That shifted the fix from treating SSL symptoms to removing unnecessary application behavior. I checked the vendor's guidance for running without analytics collection before changing anything. Rather than increase capacity or rely on recurring IIS restarts, I chose a narrow configuration patch.

A small, targeted patch

I wrote a small, dedicated configuration patch that turned off the analytics behavior the delivery servers didn't need. Keeping it as its own patch made the intent obvious and the change easy to review, instead of editing core configuration directly.

Before rolling it out, I checked the final merged configuration to make sure the patch actually did what I expected. A config file that looks right isn't the same as a config the application actually loads.

Rolling out the fix without a platform-wide outage

I validated the patch on Server B first, using direct node testing through hosts-file routing to check the updated application independently. After the application loaded the revised settings, I checked whether the recurring connection failures continued.

Once Server B was validated, I rotated Server A out of active service through the load balancer, applied the same correction, and restored it after verification. Updating one node at a time preserved an available path for traffic and avoided a simultaneous restart of both production nodes.

The important operational decision was the sequence: establish a baseline, change one node, validate, and only then repeat. A fix is not production-ready merely because the configuration file is syntactically valid.

Baseline: Server A 1,829 errors | Server B 1,706 errors
          ↓
Apply configuration patch to Server B
          ↓
Validate Server B application and error pattern
          ↓
Rotate Server A through the load balancer
          ↓
Apply the same patch and validate Server A
          ↓
Restore normal traffic distribution

Measured outcome and the limits of the comparison

Before the correction, the sampled daily Sitecore logs contained 1,829 SSL errors on Server A and 1,706 on Server B. Post-patch validation found zero occurrences of the recurring error pattern. The rollout was completed without platform-wide downtime.

Those figures should be read as an initial daily baseline versus a post-change validation observation, not necessarily identical-duration monitoring windows. The reliable conclusion is that the previously recurring failures stopped after the targeted configuration change.

Removing the failed xConnect retry activity also removed a source of avoidable thread-pool pressure. It addressed the cause of that pressure rather than simply recycling the application pools to temporarily free resources.

                         Before patch      After patch*
Server A                 1,829              0
Server B                 1,706              0
Combined                 3,535              0

* Post-patch validation, not asserted to be an identical
  time window to the initial one-day baseline.

What this incident reinforced

This is the part of production engineering I enjoy most: following evidence across application, configuration, and infrastructure boundaries until the fix is smaller—and more durable—than the symptoms initially suggested.

  • Correlate symptoms across nodes before assuming one server is the problem.
  • Distinguish an unreachable dependency from an unnecessary dependency.
  • Understand retry behavior: resilience mechanisms can increase load when a failure persists.
  • Prefer a small, reviewable configuration correction over recurring recovery actions.
  • Deploy progressively, keeping a healthy node available throughout the change.
  • Measure the outcome and describe the measurement window accurately.