[erlang-questions] OpenPoker v2: The bestest and most scalable poker server ever
Joel Reymont
joelr1@REDACTED
Thu Oct 30 04:15:46 CET 2008
http://tinyurl.com/5exq4c
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
simpler.
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
whatsoever.
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!
--
wagerlabs.com
More information about the erlang-questions
mailing list