The ServiceThrottlingBehavior
ServiceThrottlingBehavior is a service-level behavior with three settings that cap resource consumption independently of how many clients are actually trying to connect: MaxConcurrentCalls limits how many messages can be actively executing across the entire service host at once; MaxConcurrentSessions limits how many session-based channels (relevant to InstanceContextMode.PerSession or netTcpBinding sessions) can be open simultaneously; and MaxConcurrentInstances limits how many service instances can exist concurrently, which matters most for PerSession and PerCall instancing where each call or session gets its own instance. These three numbers work together rather than independently — a request might be allowed by MaxConcurrentSessions but still queued because MaxConcurrentCalls is already at its ceiling, since a single session can issue many concurrent calls.
Cricket analogy: It's like a stadium having three separate capacity limits enforced together — total fans inside the ground (MaxConcurrentCalls), total active hospitality boxes occupied (MaxConcurrentSessions), and total staffed turnstiles operating (MaxConcurrentInstances) — and a fan can be blocked by any one of the three even if the other two have room.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="throttledBehavior">
<serviceThrottling maxConcurrentCalls="200"
maxConcurrentSessions="100"
maxConcurrentInstances="200" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="OrderService.OrderProcessor"
behaviorConfiguration="throttledBehavior">
<endpoint address="" binding="netTcpBinding"
contract="OrderService.IOrderProcessor" />
</service>
</services>
</system.serviceModel>Tuning Throttling Values
The default throttling values in WCF are conservative and were set with older hardware in mind (historically MaxConcurrentCalls defaulted to 16, MaxConcurrentSessions to 10, and MaxConcurrentInstances to 26 in early versions, with somewhat higher defaults tied to processor count in later releases) — meaning an out-of-the-box WCF service running on modern multi-core hardware is very likely leaving throughput on the table until these values are explicitly raised. The right values are workload-dependent: MaxConcurrentInstances should generally be at least as large as MaxConcurrentSessions for PerSession services since each session needs its own instance, and MaxConcurrentCalls should be tuned relative to how CPU- or I/O-bound each operation is — I/O-bound operations (waiting on a database or downstream API) can sustain a much higher MaxConcurrentCalls than CPU-bound operations without starving the thread pool, since threads mostly sit idle waiting rather than actively computing.
Cricket analogy: Raising these defaults is like a franchise that inherited a decades-old net-practice schedule designed for six players and never updated it, even though the academy now has fifty trainees and modern facilities that could easily support far more simultaneous practice sessions.
Since .NET 4.0, if you don't explicitly configure serviceThrottling, WCF computes higher defaults based on the number of processors on the machine (for example, MaxConcurrentCalls defaults to 16 * ProcessorCount), which is friendlier than the old fixed defaults, but many teams still explicitly set these values to make capacity planning intentional rather than implicit and hardware-dependent.
Throttling vs Queuing Behavior
When an incoming call would exceed MaxConcurrentCalls, WCF does not immediately reject it — the message is accepted at the transport level and queued, waiting for an executing call to finish and free up a slot. This means throttling limits don't produce errors under moderate overload; they produce increased latency as calls wait in the queue, which is often the desired behavior since a temporary burst just slows down rather than fails outright. However, if the queue backs up long enough that a client's own operation timeout (or the binding's sendTimeout) elapses before the call is dequeued and executed, the client receives a TimeoutException even though the service technically never actively rejected the call — a subtlety that makes throttling-related failures look like generic network timeouts unless you specifically know to check the throttling counters (exposed via WCF performance counters) during diagnosis.
Cricket analogy: It's like fans queueing outside a sold-out stadium gate — nobody's turned away outright the moment capacity is hit, they simply wait in line as seats free up, but if the queue takes so long that kickoff has already passed by the time they'd get in, the wait becomes effectively a rejection.
Throttling and Resource Protection
The real purpose of throttling isn't just capping WCF's own internal counters — it's protecting downstream resources that a flood of concurrent calls would otherwise overwhelm, such as a connection-pool-limited SQL Server database, a rate-limited third-party API, or the CLR thread pool itself. If MaxConcurrentCalls is set far higher than the database's connection pool size, WCF will happily accept far more concurrent calls than the database can actually service, and those calls will pile up waiting on database connections instead of waiting in WCF's own queue — moving the bottleneck downstream rather than eliminating it, and often making the failure mode worse because connection pool exhaustion can produce confusing errors deep in the data access layer rather than a clean, diagnosable WCF-level timeout. Effective throttling configuration treats MaxConcurrentCalls as roughly the minimum of what the service's CPU can handle and what every downstream dependency can sustain, not just an arbitrary large number chosen to 'allow more traffic.'
Cricket analogy: It's like a stadium letting in more fans than the concession stands can actually serve — the gate isn't the bottleneck anymore, but now a thousand people are jammed in a food-queue crush that's arguably a worse problem than a controlled, limited entry would have been.
Setting MaxConcurrentCalls very high without checking downstream capacity is a common way to turn a controllable WCF-level bottleneck into an uncontrollable, harder-to-diagnose downstream failure. Always size throttling limits against the weakest downstream dependency (database connection pool, third-party API rate limit, thread pool capacity), not just against what the service host itself could theoretically sustain.
- ServiceThrottlingBehavior exposes MaxConcurrentCalls, MaxConcurrentSessions, and MaxConcurrentInstances, which act together, not independently.
- Default throttling values are conservative; modern services almost always need these raised, though .NET 4.0+ scales defaults by processor count.
- MaxConcurrentInstances should generally be at least as large as MaxConcurrentSessions for PerSession services.
- Exceeding a throttling limit queues the call rather than rejecting it, trading increased latency for graceful degradation.
- A queued call that exceeds the client's own timeout produces a generic TimeoutException, masking the true throttling-related cause.
- Throttling's real purpose is protecting downstream resources like database connection pools and rate-limited APIs, not just capping WCF's internal counters.
- MaxConcurrentCalls should be sized against the weakest downstream dependency, not set arbitrarily high to 'allow more traffic.'
Practice what you learned
1. What does MaxConcurrentCalls control in ServiceThrottlingBehavior?
2. What happens when an incoming call would exceed MaxConcurrentCalls?
3. Why might a throttling-related failure appear to the client as a generic TimeoutException?
4. What is the real purpose of tuning MaxConcurrentCalls relative to downstream dependencies?
5. What is the relationship that should generally hold between MaxConcurrentInstances and MaxConcurrentSessions for a PerSession service?
Was this page helpful?
You May Also Like
WCF Instancing and Concurrency
Understand the InstanceContextMode and ConcurrencyMode settings that control how many service instances WCF creates and how many threads can touch each one at once.
WCF Reliable Messaging
Learn how WCF's WS-ReliableMessaging support guarantees ordered, exactly-once (or at-least-once) message delivery over unreliable transports like HTTP.
Faults and Exception Handling
Learn how WCF translates .NET exceptions into SOAP faults, how to design typed FaultContracts, and how to centralize fault handling with IErrorHandler.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics