SlideShare a Scribd company logo
1 of 49
Download to read offline
locker: distributed locking
         Knut Nesheim
           @knutin
The need

Real-time multiplayer game at Wooga
Stateful
One process per user
One process per world
The need
Only one process per user & world
Cannot reconcile game worlds
Strong consistency
Fine-grained distributed coordination
Lookup table when sending game updates
Client                           Client

    visit world #123                 visit world #123

World #123                       World #123



    lock      ok          lock     already_locked



                   Lock
Client     Client




World #123
Next generation

Lock implemented once already
Central serialization with atomic ops (Redis)
SPoF
Next gen want higher availability, because..
Living with failure

Hardware sometimes fails
The network is mostly ok
Software is almost bug-free
Downtime is often short
Living with a SPoF sucks..
Development easy

Operations suck

Change become scary

Operator error becomes costly

Must fix problems immediately
Requirements

~10k conditional writes per second
~3M eventually consistent reads per second
~150k reads on local replica
Lock expires if not kept alive
Dynamic cluster membership
ZooKeeper looks like
  the best option
“What would the dream
  solution look like?”
LB         LB




App   App   App        App        App   App   App

App   App   App        App        App   App   App




                  S3         DB
App   App   App   App   App   App   App

App   App   App   App   App   App   App
App   App   App   App   App

                              App   App

App   App   App   App   App   App   App
N=5, W=3




App   App     App      App   App

                                   App   App

App   App     App      App   App   App   App
N=5, W=3




App   App     App      App   App

                                   App   App

App   App     App      App   App   App   App
N=5, W=3




App   App     App      App   App

                                   App   App

App   App     App      App   App   App   App
N=5, W=3




App   App     App      App   App

                                   App   App

App   App     App      App   App   App   App
N=5, W=3




App   App     App      App   App   App

                                         App

App   App     App      App   App   App   App
Run inside our app servers

Easy to debug, instrument, monitor, deploy

Simple operations
“How hard can it be?”
Distributed systems are hard

Many books, papers on distributed systems

Riak a good example of mindset

Idea: pick the algorithms that fits best
Simplest thing that
could possibly work
“good enough”
   Problem                Solution
  Consistency,           Serialization,
conditional writes    “2 Phase Commit”
                      Multiple serializers,
   Availability
                         quorum (CP)
  Local replica      Replay transaction log

Dynamic cluster      Manual configuration

  Anti-entropy          Lock keep alive
Implementation
Proof of concept in Friday afternoon
Looked promising, spent ~3 weeks
Turned into production quality
Test race conditions
PropEr
330 lines (!)
locker
http://github.com/wooga/locker
Beware tradeoffs!

Keep consistency, sacrifice availability during failure
No persistency, assumes enough masters stays up
No group membership, views
Manually configure masters, replicas, quorum value
Assumes perfect network during reconfiguration
Not based on a described algorithm
Usage
start_link(W)
set_nodes(AllNodes,	 Masters,	 Replicas)
lock(Key,	 Value,	 LeaseLength)
extend_lease(Key,	 Value,	 LeaseLength)
release(Key,	 Value)
dirty_read(Key)
lock(Key,	 Value,	 LeaseLength)



  Two phases:
       Prepare: Ask masters for votes
       Commit: If majority, write on masters
  Timeout counted as negative vote, CP
  Asynchronous replication, wait_for/2
Client A                     Client B




   locker:lock(foo, pid(0,123,0))




Master #1     Master #2      Master #3
Client A    locker:lock(foo, pid(0,123,0))   Client B
         Send: {get_write_lock, foo, not_found}




  Master #1            Master #2               Master #3

Write locks:[]             []                     []
Client A      locker:lock(foo, pid(0,123,0))   Client B
          Send: {get_write_lock, foo, not_found}




  Master #1              Master #2               Master #3

[{lock, foo}]           [{lock, foo}]               []
Client A      locker:lock(foo, pid(0,123,0))   Client B
          Send: {get_write_lock, foo, not_found}




  Master #1              Master #2               Master #3

[{lock, foo}]           [{lock, foo}]               []
Client A      locker:lock(foo, pid(0,123,0))   Client B
          Send: {get_write_lock, foo, not_found}




  Master #1              Master #2               Master #3

[{lock, foo}]           [{lock, foo}]        [{lock, foo}]
Client A      locker:lock(foo, pid(0,123,0))   Client B
          Send: {get_write_lock, foo, not_found}




                                                       Already
                                                       locked!
  Master #1              Master #2               Master #3

[{lock, foo}]           [{lock, foo}]        [{lock, foo}]
Client A      locker:lock(foo, pid(0,123,0))   Client B
          Send: {get_write_lock, foo, not_found}



                [ok, ok, error]                      [error,error, ok]




  Master #1               Master #2              Master #3

[{lock, foo}]           [{lock, foo}]        [{lock, foo}]
Client A      locker:lock(foo, pid(0,123,0))   Client B

[ok, ok, error]                             [error, error, ok]




  Master #1              Master #2               Master #3

[{lock, foo}]           [{lock, foo}]        [{lock, foo}]
Client A      locker:lock(foo, pid(0,123,0))   Client B

[ok, ok, error]                             [error, error, ok]

Send: {write, foo, 123}                   Send: release_write_lock




  Master #1               Master #2              Master #3

[{lock, foo}]           [{lock, foo}]        [{lock, foo}]
Client A     locker:lock(foo, pid(0,123,0))   Client B

[ok, ok, error]                           [error, error, ok]

Send: {write, foo, 123}                 Send: release_write_lock




  Master #1               Master #2            Master #3

     []                      []                   []
Use locker when

Strong consistency is sometimes needed
Protect resources
Leader election
Service discovery
Is it any good?
Some experts say yes,
 some experts say no
Conclusion
Distributed systems are hard
We could maybe have
made ZooKeeper work..
..but we now have our
     dream system
Ambitious, naive project
          led to
big operational advantage
Q&A
http://github.com/wooga/locker
            @knutin

More Related Content

What's hot

Doing It Wrong with Puppet -
Doing It Wrong with Puppet - Doing It Wrong with Puppet -
Doing It Wrong with Puppet - Puppet
 
Writing and using php streams and sockets
Writing and using php streams and socketsWriting and using php streams and sockets
Writing and using php streams and socketsElizabeth Smith
 
Ruby Concurrency and EventMachine
Ruby Concurrency and EventMachineRuby Concurrency and EventMachine
Ruby Concurrency and EventMachineChristopher Spring
 
Natural Language Processing and Python
Natural Language Processing and PythonNatural Language Processing and Python
Natural Language Processing and Pythonanntp
 
Udp socket programming(Florian)
Udp socket programming(Florian)Udp socket programming(Florian)
Udp socket programming(Florian)Flor Ian
 
Profiling with Devel::NYTProf
Profiling with Devel::NYTProfProfiling with Devel::NYTProf
Profiling with Devel::NYTProfbobcatfish
 
基于 Google protobuf 的 webgame 网络协议设计
基于 Google protobuf 的 webgame 网络协议设计基于 Google protobuf 的 webgame 网络协议设计
基于 Google protobuf 的 webgame 网络协议设计勇浩 赖
 
Creating an Arduino Web Server from scratch hardware and software
Creating an Arduino Web Server from scratch hardware and softwareCreating an Arduino Web Server from scratch hardware and software
Creating an Arduino Web Server from scratch hardware and softwareJustin Mclean
 
SymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performancesSymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performancesjulien pauli
 

What's hot (19)

Doing It Wrong with Puppet -
Doing It Wrong with Puppet - Doing It Wrong with Puppet -
Doing It Wrong with Puppet -
 
Writing and using php streams and sockets
Writing and using php streams and socketsWriting and using php streams and sockets
Writing and using php streams and sockets
 
Ruby Concurrency and EventMachine
Ruby Concurrency and EventMachineRuby Concurrency and EventMachine
Ruby Concurrency and EventMachine
 
Natural Language Processing and Python
Natural Language Processing and PythonNatural Language Processing and Python
Natural Language Processing and Python
 
Python Programming Essentials - M13 - Tuples
Python Programming Essentials - M13 - TuplesPython Programming Essentials - M13 - Tuples
Python Programming Essentials - M13 - Tuples
 
Udp socket programming(Florian)
Udp socket programming(Florian)Udp socket programming(Florian)
Udp socket programming(Florian)
 
Profiling with Devel::NYTProf
Profiling with Devel::NYTProfProfiling with Devel::NYTProf
Profiling with Devel::NYTProf
 
基于 Google protobuf 的 webgame 网络协议设计
基于 Google protobuf 的 webgame 网络协议设计基于 Google protobuf 的 webgame 网络协议设计
基于 Google protobuf 的 webgame 网络协议设计
 
Npc08
Npc08Npc08
Npc08
 
Creating an Arduino Web Server from scratch hardware and software
Creating an Arduino Web Server from scratch hardware and softwareCreating an Arduino Web Server from scratch hardware and software
Creating an Arduino Web Server from scratch hardware and software
 
Sockets intro
Sockets introSockets intro
Sockets intro
 
Python for Penetration testers
Python for Penetration testersPython for Penetration testers
Python for Penetration testers
 
Python Programming Essentials - M7 - Strings
Python Programming Essentials - M7 - StringsPython Programming Essentials - M7 - Strings
Python Programming Essentials - M7 - Strings
 
Lecture8
Lecture8Lecture8
Lecture8
 
Java Full Throttle
Java Full ThrottleJava Full Throttle
Java Full Throttle
 
effective_r27
effective_r27effective_r27
effective_r27
 
Building Custom PHP Extensions
Building Custom PHP ExtensionsBuilding Custom PHP Extensions
Building Custom PHP Extensions
 
SymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performancesSymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performances
 
Internet of dusty things
Internet of dusty thingsInternet of dusty things
Internet of dusty things
 

Similar to Locker: distributed consistent locking

Douglas Crockford: Serversideness
Douglas Crockford: ServersidenessDouglas Crockford: Serversideness
Douglas Crockford: ServersidenessWebExpo
 
Servers with Event Machine - David Troy - RailsConf 2011
Servers with Event Machine - David Troy - RailsConf 2011Servers with Event Machine - David Troy - RailsConf 2011
Servers with Event Machine - David Troy - RailsConf 2011David Troy
 
Message Queueing - by an MQ noob
Message Queueing - by an MQ noobMessage Queueing - by an MQ noob
Message Queueing - by an MQ noobRichard Jones
 
Raymond Kuiper - Working the API like a Unix Pro
Raymond Kuiper - Working the API like a Unix ProRaymond Kuiper - Working the API like a Unix Pro
Raymond Kuiper - Working the API like a Unix ProZabbix
 
LCA2014 - Introduction to Go
LCA2014 - Introduction to GoLCA2014 - Introduction to Go
LCA2014 - Introduction to Godreamwidth
 
Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)
Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)
Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)James Titcumb
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)James Titcumb
 
Zimbra scripting with python
Zimbra scripting with pythonZimbra scripting with python
Zimbra scripting with pythonImam Omar Mochtar
 
Practical non blocking microservices in java 8
Practical non blocking microservices in java 8Practical non blocking microservices in java 8
Practical non blocking microservices in java 8Michal Balinski
 
An opinionated intro to Node.js - devrupt hospitality hackathon
An opinionated intro to Node.js - devrupt hospitality hackathonAn opinionated intro to Node.js - devrupt hospitality hackathon
An opinionated intro to Node.js - devrupt hospitality hackathonLuciano Mammino
 
amani_rwc_password
amani_rwc_passwordamani_rwc_password
amani_rwc_passwordArvind Mani
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud CastlesBen Scofield
 
Scaling PostgreSQL with Skytools
Scaling PostgreSQL with SkytoolsScaling PostgreSQL with Skytools
Scaling PostgreSQL with SkytoolsGavin Roy
 
Zope component architechture
Zope component architechtureZope component architechture
Zope component architechtureAnatoly Bubenkov
 
Post Metasploitation
Post MetasploitationPost Metasploitation
Post Metasploitationegypt
 
Node.js - Advanced Basics
Node.js - Advanced BasicsNode.js - Advanced Basics
Node.js - Advanced BasicsDoug Jones
 

Similar to Locker: distributed consistent locking (20)

Douglas Crockford: Serversideness
Douglas Crockford: ServersidenessDouglas Crockford: Serversideness
Douglas Crockford: Serversideness
 
Servers with Event Machine - David Troy - RailsConf 2011
Servers with Event Machine - David Troy - RailsConf 2011Servers with Event Machine - David Troy - RailsConf 2011
Servers with Event Machine - David Troy - RailsConf 2011
 
Message queueing
Message queueingMessage queueing
Message queueing
 
Message Queueing - by an MQ noob
Message Queueing - by an MQ noobMessage Queueing - by an MQ noob
Message Queueing - by an MQ noob
 
Raymond Kuiper - Working the API like a Unix Pro
Raymond Kuiper - Working the API like a Unix ProRaymond Kuiper - Working the API like a Unix Pro
Raymond Kuiper - Working the API like a Unix Pro
 
LCA2014 - Introduction to Go
LCA2014 - Introduction to GoLCA2014 - Introduction to Go
LCA2014 - Introduction to Go
 
Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)
Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)
Practical Message Queuing Using RabbitMQ (PHPem, 3rd July 2014)
 
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
Adding 1.21 Gigawatts to Applications with RabbitMQ (PHPNW Dec 2014 Meetup)
 
Queue your work
Queue your workQueue your work
Queue your work
 
Java sockets
Java socketsJava sockets
Java sockets
 
Zimbra scripting with python
Zimbra scripting with pythonZimbra scripting with python
Zimbra scripting with python
 
Practical non blocking microservices in java 8
Practical non blocking microservices in java 8Practical non blocking microservices in java 8
Practical non blocking microservices in java 8
 
An opinionated intro to Node.js - devrupt hospitality hackathon
An opinionated intro to Node.js - devrupt hospitality hackathonAn opinionated intro to Node.js - devrupt hospitality hackathon
An opinionated intro to Node.js - devrupt hospitality hackathon
 
amani_rwc_password
amani_rwc_passwordamani_rwc_password
amani_rwc_password
 
Python3 (boto3) for aws
Python3 (boto3) for awsPython3 (boto3) for aws
Python3 (boto3) for aws
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud Castles
 
Scaling PostgreSQL with Skytools
Scaling PostgreSQL with SkytoolsScaling PostgreSQL with Skytools
Scaling PostgreSQL with Skytools
 
Zope component architechture
Zope component architechtureZope component architechture
Zope component architechture
 
Post Metasploitation
Post MetasploitationPost Metasploitation
Post Metasploitation
 
Node.js - Advanced Basics
Node.js - Advanced BasicsNode.js - Advanced Basics
Node.js - Advanced Basics
 

Locker: distributed consistent locking

  • 1. locker: distributed locking Knut Nesheim @knutin
  • 2. The need Real-time multiplayer game at Wooga Stateful One process per user One process per world
  • 3. The need Only one process per user & world Cannot reconcile game worlds Strong consistency Fine-grained distributed coordination Lookup table when sending game updates
  • 4. Client Client visit world #123 visit world #123 World #123 World #123 lock ok lock already_locked Lock
  • 5. Client Client World #123
  • 6. Next generation Lock implemented once already Central serialization with atomic ops (Redis) SPoF Next gen want higher availability, because..
  • 7. Living with failure Hardware sometimes fails The network is mostly ok Software is almost bug-free Downtime is often short
  • 8. Living with a SPoF sucks..
  • 9. Development easy Operations suck Change become scary Operator error becomes costly Must fix problems immediately
  • 10. Requirements ~10k conditional writes per second ~3M eventually consistent reads per second ~150k reads on local replica Lock expires if not kept alive Dynamic cluster membership
  • 11. ZooKeeper looks like the best option
  • 12. “What would the dream solution look like?”
  • 13. LB LB App App App App App App App App App App App App App App S3 DB
  • 14. App App App App App App App App App App App App App App
  • 15. App App App App App App App App App App App App App App
  • 16. N=5, W=3 App App App App App App App App App App App App App App
  • 17. N=5, W=3 App App App App App App App App App App App App App App
  • 18. N=5, W=3 App App App App App App App App App App App App App App
  • 19. N=5, W=3 App App App App App App App App App App App App App App
  • 20. N=5, W=3 App App App App App App App App App App App App App App
  • 21. Run inside our app servers Easy to debug, instrument, monitor, deploy Simple operations
  • 22. “How hard can it be?”
  • 23. Distributed systems are hard Many books, papers on distributed systems Riak a good example of mindset Idea: pick the algorithms that fits best
  • 24. Simplest thing that could possibly work
  • 25. “good enough” Problem Solution Consistency, Serialization, conditional writes “2 Phase Commit” Multiple serializers, Availability quorum (CP) Local replica Replay transaction log Dynamic cluster Manual configuration Anti-entropy Lock keep alive
  • 26. Implementation Proof of concept in Friday afternoon Looked promising, spent ~3 weeks Turned into production quality Test race conditions PropEr 330 lines (!)
  • 28. Beware tradeoffs! Keep consistency, sacrifice availability during failure No persistency, assumes enough masters stays up No group membership, views Manually configure masters, replicas, quorum value Assumes perfect network during reconfiguration Not based on a described algorithm
  • 29. Usage start_link(W) set_nodes(AllNodes, Masters, Replicas) lock(Key, Value, LeaseLength) extend_lease(Key, Value, LeaseLength) release(Key, Value) dirty_read(Key)
  • 30. lock(Key, Value, LeaseLength) Two phases: Prepare: Ask masters for votes Commit: If majority, write on masters Timeout counted as negative vote, CP Asynchronous replication, wait_for/2
  • 31. Client A Client B locker:lock(foo, pid(0,123,0)) Master #1 Master #2 Master #3
  • 32. Client A locker:lock(foo, pid(0,123,0)) Client B Send: {get_write_lock, foo, not_found} Master #1 Master #2 Master #3 Write locks:[] [] []
  • 33. Client A locker:lock(foo, pid(0,123,0)) Client B Send: {get_write_lock, foo, not_found} Master #1 Master #2 Master #3 [{lock, foo}] [{lock, foo}] []
  • 34. Client A locker:lock(foo, pid(0,123,0)) Client B Send: {get_write_lock, foo, not_found} Master #1 Master #2 Master #3 [{lock, foo}] [{lock, foo}] []
  • 35. Client A locker:lock(foo, pid(0,123,0)) Client B Send: {get_write_lock, foo, not_found} Master #1 Master #2 Master #3 [{lock, foo}] [{lock, foo}] [{lock, foo}]
  • 36. Client A locker:lock(foo, pid(0,123,0)) Client B Send: {get_write_lock, foo, not_found} Already locked! Master #1 Master #2 Master #3 [{lock, foo}] [{lock, foo}] [{lock, foo}]
  • 37. Client A locker:lock(foo, pid(0,123,0)) Client B Send: {get_write_lock, foo, not_found} [ok, ok, error] [error,error, ok] Master #1 Master #2 Master #3 [{lock, foo}] [{lock, foo}] [{lock, foo}]
  • 38. Client A locker:lock(foo, pid(0,123,0)) Client B [ok, ok, error] [error, error, ok] Master #1 Master #2 Master #3 [{lock, foo}] [{lock, foo}] [{lock, foo}]
  • 39. Client A locker:lock(foo, pid(0,123,0)) Client B [ok, ok, error] [error, error, ok] Send: {write, foo, 123} Send: release_write_lock Master #1 Master #2 Master #3 [{lock, foo}] [{lock, foo}] [{lock, foo}]
  • 40. Client A locker:lock(foo, pid(0,123,0)) Client B [ok, ok, error] [error, error, ok] Send: {write, foo, 123} Send: release_write_lock Master #1 Master #2 Master #3 [] [] []
  • 41. Use locker when Strong consistency is sometimes needed Protect resources Leader election Service discovery
  • 42. Is it any good?
  • 43. Some experts say yes, some experts say no
  • 46. We could maybe have made ZooKeeper work..
  • 47. ..but we now have our dream system
  • 48. Ambitious, naive project led to big operational advantage