A component of ASP.NET Core for creating RESTful web services that support HTTP-based communication between clients and servers.
If the site still hangs with Rapid-Fail Protection disabled, the fastest way to find the cause is to capture a memory dump while it's hung, before you recycle, and look at what every thread is waiting on. A recycle throws that evidence away.
1. Get dotnet-dump
On a machine with the .NET SDK: dotnet tool install --global dotnet-dump. On a server without the SDK, download the single-file executable from https://aka.ms/dotnet-dump/win-x64.
2. Capture a dump while the site is unresponsive
Run an elevated command prompt on the server:
dotnet-dump ps
dotnet-dump collect -p <PID> -o C:\dumps\hang.dmp
Pick the right process: with in-process hosting (the default for ASP.NET Core on IIS) the app runs inside w3wp.exe, and with out-of-process hosting it runs in dotnet.exe or your app's .exe. IIS Manager → Worker Processes shows the PID for each application pool.
3. Look at what the threads are waiting on
dotnet-dump analyze C:\dumps\hang.dmp
> parallelstacks
> syncblk
> threadpool
-
parallelstacksgroups threads with the same call stack, so you'll see something like "40 threads all waiting inYourService.GetOrders".
syncblk shows which thread owns a lock other threads are waiting for.
threadpool shows whether work is piling up in the queue (thread pool starvation).
You can also open the .dmp file in Visual Studio and use the Parallel Stacks window.
What to look for with this "only when quiet" pattern
A background job (a timer or IHostedService) that takes a lock and then blocks on a call without a timeout.
Blocking on async code (.Result, .Wait()) that ties up thread pool threads.
An outbound call (database, HTTP, Redis) whose connection was dropped while idle, for example by a firewall, and that has no timeout, so the first request after a quiet period waits forever.
I reproduced the first pattern in a .NET 9 app: a timer took a lock and then blocked, and every request needing that lock hung while other endpoints still responded. In the dump, syncblk showed the one thread owning the lock, and parallelstacks showed that thread inside the timer callback with four request threads waiting on the same lock. That points straight at the line to fix.
If you capture a dump, feel free to post the parallelstacks output here and I'll help read it.