[erlang-questions] OpenPoker v2: The bestest and most scalable poker server ever

Joel Reymont joelr1@REDACTED
Thu Oct 30 04:15:46 CET 2008


This is the final architecture as I'm sick of rewriting the thing and  
I don't think I can simplify it further!

The first version I wrote back in 2005 used processes for everything.  
Seats, games, players, bots, games, pots, hands, all were processes.  
Moreover, they were all gen_servers.

My cardgame "uber gen_fsm" was of particular interest and gave me the  
ability to put a stack of gen_fsm modules together. The cardgame  
driver would pop modules off the stack as they finished and modules  
could also tell the driver to re-run them, restart from the top of the  
full stack or go to the last module.

The implementation was fugly but gave me the ability to put card game  
logic together from a set of betting, card dealing, etc. modules.  
Texas Hold'em, for example, uses a stack that looks like this:  
wait_for_players, blinds, deal_cards, betting, deal_cards, etc.

Another feature was mapping process ids to integers by using  
gen_server:call on the pid at serialization time and looking up the  
integer in Mnesia when interpreting the packets.

Last but not least, games freely gen_server:called players and vise  
versa, a recipe for disaster as I discovered under very high load.  
Yes, Virginia you can deadlock in Erlang!

A great example of deadlock waiting to happen is firing off a timer  
from a game process and asking the player process for its id while the  
player is waiting for something from the game after a gen_server:call  
of its own.

There are only two process types left in this latest version of  
OpenPoker: games and players. State machine logic is integrated into  
the game gen_server and I'm happy to say that the implementation is  
beautiful and still uses a stack of modules, albeit much smaller and  

Game gen_servers never ever call players and all processes are  
globally registered as {game, N} and {player, N} respectively. All  
packets go out with the integer ids in them since they are known at  
the source. Incoming messages are simply addressed to {player,  
<integer id}. This works, scales and is darn fast! It does rely on the  
"global" infrastructure but it hasn't let me down yet.

Everything else is just record "mutation", no message passing  

I'm throwing down the gauntlet here. I'll bloody switch to Lisp and  
libevent-based single-threaded servers if this OpenPoker cannot handle  
mover than 10k games and 50K players on a single server!


More information about the erlang-questions mailing list