Performance of selective receive

Sean Hinde sean.hinde@REDACTED
Sun Nov 13 23:23:42 CET 2005


On 13 Nov 2005, at 18:18, Pascal Brisset wrote:

> Sean Hinde writes:
>> Alternative proposal:
>>
>> Never, but never use async messages into a blocking server from the
>> outside world. The way to avoid this is to make the socket process
>> (or whatever it is) make a synchronous call into the gen_server
>> process.
>
> Agreed, it's all about propagating flow-control end-to-end.
>
> Note that if the "socket process" uses {active,true}, it might
> itself be susceptible to the "snowball effect" - unless we are
> talking about a well-behaved network protocol with end-to-end
> flow control, in which case there is no need to worry at all.

Yes, of course. You should use {active, once}. I will update the  
tutorial one day.

>
> Also, consider a scenario with not one client (or socket process),
> but 1000.  Even if each client calls the server synchronously, the
> server can still have as much as 1000 requests in its message queue.
> That's enough to trigger a snowball effect.

No. This is only true if the server actually blocks. By the use of  
gen_server:reply/2 you can make every caller believe that it has made  
exclusive use of the server, but in fact the server can simply pass  
the messages straight through (maybe updating some local internal  
state on the way through).

If you use {active, once} in your sockets then they will all hold  
back more traffic from the network until they get a reply from the  
server. It is pretty unlikely that all 1000 servers sent their  
requests at exactly the same moment, and even if they did, the system  
would recover quickly, not spiral into meltdown as it would in the  
async case.

The limit of the system then becomes the number of clients and the  
real processing required from each one, not some unfortunate message  
storm.

>
>
>> You then have to make the gen_server itself not block. The
>> neatest way is to spawn a new process to make the blocking call to
>> the backend, and use the gen_server:reply/2 mechanism to reply later.
>> You could also use a pool of backend processes and reject if you hit
>> "congestion".
>
> Sometimes you just can't process requests asynchronously.
> In our case, the server was a session manager which *must*
> read and update a local mnesia table of active sessions
> before it can process the next message.

Yes, that's fine, that is most likely required in such scenarios.  
This is a different case to waiting for many seconds for some other  
system to respond. The erlang scheduler is fair given half a chance.  
In this case that means replacing the cast to your internal server  
with a call.

Under normal traffic the sync call sends twice as many messages. But  
you don't need to worry about that as you are not overloaded. When  
you are overloaded making this synchronous saves you 100% from these  
message storms.

I looked into a system once which suffered from this. Each client  
made 5 async sends to a central logging server before yielding. After  
a certain load the system bogged down. By simply making these  
requests syncronous the overall throughput of the system greatly  
increased and it remained consistent under sustained load.

>
> As Ulf highlighted, there are blocking calls hidden everywhere.
> There's nothing wrong with that, as long as your system is
> dimensioned correctly.  The problem is that a 10 % CPU load can
> suddenly turn into 100 % if you add a few thousand messages in
> the wrong message queue.

No. If that happens the problem is that the Erlang program is not  
correctly designed. Telephone systems have to handle 95% of maximum  
supported load while being attacked with 100 times that load.

Erlang is designed for telephone systems by the biggest supplier of  
telephone systems in the world. It works.

Sean




More information about the erlang-questions mailing list