Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Game Servers in OTP
Search
Martin Schürrer
July 26, 2014
Programming
9
700
Game Servers in OTP
Given at ElixirConf 2014
Martin Schürrer
July 26, 2014
Tweet
Share
More Decks by Martin Schürrer
See All by Martin Schürrer
Deploying Elixir
msch
3
300
RailsGirls Linz Lightning Talk
msch
0
140
Erlang for Rubyists
msch
4
700
Whirlwind Tour of Erlang
msch
1
270
Other Decks in Programming
See All in Programming
AI Agent Dojo #4: watsonx Orchestrate ADK体験
oniak3ibm
PRO
0
130
Oxlintはいいぞ
yug1224
5
1.1k
ELYZA_Findy AI Engineering Summit登壇資料_AIコーディング時代に「ちゃんと」やること_toB LLMプロダクト開発舞台裏_20251216
elyza
2
1.2k
ゆくKotlin くるRust
exoego
1
210
DevFest Android in Korea 2025 - 개발자 커뮤니티를 통해 얻는 가치
wisemuji
0
190
高速開発のためのコード整理術
sutetotanuki
1
330
.NET Conf 2025 の興味のあるセッ ションを復習した / dotnet conf 2025 quick recap for backend engineer
tomohisa
0
120
AI Schema Enrichment for your Oracle AI Database
thatjeffsmith
0
130
CSC307 Lecture 05
javiergs
PRO
0
490
AtCoder Conference 2025
shindannin
0
990
Fragmented Architectures
denyspoltorak
0
140
メルカリのリーダビリティチームが取り組む、AI時代のスケーラブルな品質文化
cloverrose
2
500
Featured
See All Featured
Exploring anti-patterns in Rails
aemeredith
2
230
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
150
We Are The Robots
honzajavorek
0
140
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
84
The Cult of Friendly URLs
andyhume
79
6.8k
How to build a perfect <img>
jonoalderson
1
4.9k
Paper Plane
katiecoart
PRO
0
46k
Claude Code のすすめ
schroneko
67
210k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
220
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
85
Designing Experiences People Love
moore
143
24k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.1k
Transcript
Game Servers in OTP
@msch featurebranch.com
None
None
None
None
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
HA keepalived haproxy active Erlang active Erlang standby Erlang standby
PostgreSQL haproxy standby
Erlang active
OTP Apps :cardgame :storage :network :gameserver
:cardgame OTP APP {Table,9} {Table,8} {Table,7} {Table,6}
:network OTP APP WebSocket :cowboy Flash (TCP) :ranch
:storage OTP APP PSQLDBWorker TableIdSequence
:gameserver OTP APP {Player,1} {Player,2} {Player,3}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
None
DealCards Knocking DealCards Play Result idle playing
[ {DealCards, [cards_to_deal, :private]}, {Knocking, [5000]}, {DealCards, [cards_to_deal, :private]}, {Negotiation,
[]}, {Play, [contra_enabled: true]}, {Play, []}, {Play, []}, … {Play, []}, {Play, []}, {Result, []} ]
gen_fsm:send_event/2 -----> Module:StateName/2 gen_fsm:send_event/2 -----> Table.playing/2 {:next_state, next_state_name, new_state_data} !
{:stop, reason, new_state_data}
DealCards Knocking DealCards Play Result idle playing
{:next_stage, new_stage_data, new_state_data} ! {:end_game, new_stage_data, new_state_data} ! Table.broadcast_event {:next_state,
next_state_name, new_state_data} ! {:stop, reason, new_state_data} ! GenServer.reply Table.playing/2 -----> DealCards.playing/3
DealCards Knocking DealCards Play Result idle playing
Knocking DealCards Play Result idle playing
DealCards Play Result idle playing
Play Result idle playing {:game_event, …}
Play Result idle playing {:game_event, …}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
P1 P2 P3 P4 {Table,9} {Player,1}
P1 P2 P3 P4 {Table,9} {Player,1} Ports
P1 P2 P3 P4 {Table,9} {Player,1}
P1 P2 P3 P4 {Table,9} {Player,1} {:game_event, :must_play_card}
P1 {Table,9} {Player,1} {:game_event, …}
P1 {Table,9} {Player,1} { "type": "mustPlayCard" } {:game_event, :must_play_card} ProtocolJSON.write/1
P1 {Table,9} {Player,1}
P1 {Table,9} {Player,1} { "type": "playACard", "card": "XH" } {:game_event,
{:play_card,{:ten,:hearts}} ProtocolJSON.parse/1
P1 {Table,9} {Player,1} {:game_event, …}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
P1 P2 P3 P4 {Table,9} {Player,1}
haproxy active Erlang active Erlang standby Erlang standby
haproxy active Erlang standby Erlang active Erlang standby
:rpc.multicall(GameServer, switch_active, [node()])
P1 P2 P3 P4 {Table,9} P5 P6 P7 old
P1 P2 P3 P4 {Table,9} P5 P6 P7 old
P1 P2 P3 P4 {Table,9} old
P1 P2 P3 P4 {Table,9} old new {TableMigrator,9}
P1 P2 P3 P4 {Table,9} old new {TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
ConnectionProxy {TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} old new {TableMigrator,9}
None
DealCards Knocking DealCards Play Result idle playing
P1 P2 P3 P4 old new {TableMigrator,9} portable_state
P1 2 3 4 old new { portable_state portable_state %{table_id:
9, player_ids: [1,2,3,4], positions: %{1 => 3, 2 => 0, 3 => 1, 4 => 2}, modifications: [:short_hand], last_game_id: 123 }
P1 P2 P3 P4 old new {TableMigrator,9}
old new {TableMigrator,9}
old new {TableMigrator,9}
None
None
old new {TableMigrator,9}
old new {TableMigrator,9} P1
old new {TableMigrator,9} P2 P3 P4 P1
old new {TableMigrator,9} P2 P3 P4 P1 portable_state
old new P2 P3 P4 P1 {Table,9}
None
P2 P3 P4 P1 {Table,9}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
All-in! Elixir Rewrite
Why? Extensibility goals stdlib mix ExUnit
:gen_fsm :ets GenServer Supervisor alias :ets, as: ETS
proper Tests send_events(ClientDeviceIDs, ClientIP) -> ?FORALL(Event, event(), ?FORALL(Data, event_data(Event), ...
parse_transform ?
Architecture big-picture The Table-FSM Life of an in-game event Moving
tables across servers BoldPoker <3 Elixir Lessons Learned & QA
Perform en/decoding at the system border P1 { "type": "playACard",
"card": "XH" } {:game_event, {:play_card,{:ten,:hearts}} ProtocolJSON.parse/1 WS
Create your own behaviours for fun better reusability and profit
easier testing {:next_stage, new_stage_data, new_state_data} {:end_game, new_stage_data, new_state_data}
Distributed Erlang makes hard things easy ConnectionProxy
DB failure? We got all data already, so finish the
game, then just store result on disk
Thank you! @msch featurebranch.com