Consistency for Timeouts and Quotas

A few weeks ago we spent some time thinking about the right way to think about quotas. This metathinking was triggered by a fix for a bug in the service host. The WCF service model layer, including the service host, sits on top of the messaging layer and provides a set of quotas that are similar but not at all the same as our quotas. The bug was that one of the service host quotas for a maximum number of sessions was not being enforced. Fixing that bug caused an ugly interaction with one of the transport quotas for a maximum number of connections. Sessions and connections are not the same thing but the two are linked in such a way that changing the quota for the number of sessions doesn't make a lot of sense unless you also pull along the quota for the number of connections.

The interaction occurred when people hit the quota for the maximum number of sessions. Under the covers, the service host is running a loop to handle as many connections as there are bubbling up from the messaging layer. When the session quota is exceeded, the loop stops and both the number of sessions and the number of handled connections stops growing. People with lots of processing power want to handle more sessions than our default quota values permit. If you were to increase the session quota though, you'd find that it didn't really help. That was because the number of connections also had a quota and even though the service host was trying to run its loop, there simply were no more connections coming up from the transport to create sessions from. This meant that you also had to change the quota for the number of connections to make all of this works, which kind of sucks. You have to fiddle with two knobs to solve one problem.

This lead to a slight tweak in the philosophy for providing quotas. There shouldn't be a quota when the movement of data is purely voluntary. The service host needs a quota because it would otherwise be running a continuous loop that moves more and more data without any intervention in your code. The transport doesn't need a quota because handling a connection is an active process. You have to call a method each time you want another connection. If you want to stop getting new connections, then you can just stop calling the method.

We took a look at other places with these loops and tried to make other cases that were similar have the same user experience.

  • We removed the MaxInboundConnections quota for connection-oriented transports. The number of handled connections is something you control through your actions.
  • We added a MaxPendingConnections quota for these transports. Connections pend without you taking any action so the number of pending connections needs a quota.
  • We renamed the MaxConnectionsPendingDispatch quota on SMSvcHost to MaxPendingConnections for consistency.
  • We added a MaxAcceptedChannels quota to the one-way channel I talked about the other day. This channel has a loop similar to the service host starting sessions so we need some way to decide when the loop should stop running.

Next time: Controlling Connection Pooling for TCP and Named Pipes