Slide 1

Slide 1 text

Taming the Unicorn Demystify your Unix system jesse@shopify.com @jstorimer jstorimer.com workingwithunixprocesses.com 1

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

Find me at http://unicorn.bogomips.org 3

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

You have to know what’s possible 5

Slide 6

Slide 6 text

Join the Lineage 6

Slide 7

Slide 7 text

“Thirty years from now, there will still be [system calls] and smart people will still be using them to solve hard problems reliably and predictably, just like they were thirty years ago.” ~ Ryan Tomayko 7

Slide 8

Slide 8 text

It’s all about system calls 8

Slide 9

Slide 9 text

Documentation Code 9

Slide 10

Slide 10 text

Documentation •intro(2) -> $ man 2 intro Code 9

Slide 11

Slide 11 text

Documentation •intro(2) -> $ man 2 intro •malloc(3) -> $ man 3 malloc Code 9

Slide 12

Slide 12 text

Documentation •intro(2) -> $ man 2 intro •malloc(3) -> $ man 3 malloc •crontab(5) -> $ man 5 crontab Code 9

Slide 13

Slide 13 text

Documentation •intro(2) -> $ man 2 intro •malloc(3) -> $ man 3 malloc •crontab(5) -> $ man 5 crontab •[Linux only] socket(7) -> $ man 7 socket Code 9

Slide 14

Slide 14 text

Documentation •intro(2) -> $ man 2 intro •malloc(3) -> $ man 3 malloc •crontab(5) -> $ man 5 crontab •[Linux only] socket(7) -> $ man 7 socket •$ apropos thing; Searches the manpages Code 9

Slide 15

Slide 15 text

UNIX POSIX Linux FreeBSD Darwin you-nix? Pick ‘the’ Unix 10

Slide 16

Slide 16 text

11

Slide 17

Slide 17 text

12

Slide 18

Slide 18 text

Preforking 12

Slide 19

Slide 19 text

Preforking Signal handling 12

Slide 20

Slide 20 text

Preforking Signal handling kgio 12

Slide 21

Slide 21 text

Preforking Signal handling kgio Bets and Memory Tricks 12

Slide 22

Slide 22 text

Preforking Signal handling kgio Bets and Memory Tricks Zero downtime restarts? 12

Slide 23

Slide 23 text

Preforking Signal handling kgio Bets and Memory Tricks Zero downtime restarts? Code spelunking? 12

Slide 24

Slide 24 text

Preforking 1.First, Forking 2.Then, Pre 13

Slide 25

Slide 25 text

fork(2) system call Code 14

Slide 26

Slide 26 text

Processes are the fundamental units 15

Slide 27

Slide 27 text

fork(2) creates an *exact* copy of the current process 16

Slide 28

Slide 28 text

17

Slide 29

Slide 29 text

18

Slide 30

Slide 30 text

19

Slide 31

Slide 31 text

20

Slide 32

Slide 32 text

Parent and child execute independently Parent can exit and child lives on Code 21

Slide 33

Slide 33 text

Now to add the pre Code 22

Slide 34

Slide 34 text

require 'socket' concurrency = 3 child_pids = [] signal_queue = [] socket = TCPServer.open('127.0.0.1', 4481) concurrency.times do child_pids << fork do trap(:INT) { exit } loop do connection = socket.accept connection.write(connection.read) connection.close end end end [:INT, :CHLD].each do |sig| trap(sig) do signal_queue << sig end end 23

Slide 35

Slide 35 text

require 'socket' concurrency = 3 child_pids = [] signal_queue = [] socket = TCPServer.open('127.0.0.1', 4481) concurrency.times do child_pids << fork do trap(:INT) { exit } loop do connection = socket.accept connection.write(connection.read) connection.close end end end [:INT, :CHLD].each do |sig| trap(sig) do signal_queue << sig end end Parent Process Child Processes 24

Slide 36

Slide 36 text

Further Reading •fork(2) •wait(2) •https://github.com/defunkt/unicorn/ blob/master/lib/unicorn/ http_server.rb#L483-494 25

Slide 37

Slide 37 text

Signal Handling 26

Slide 38

Slide 38 text

Sending a signal •kill(1) or Process.kill •Each signal can be represented with a name or number •Anyone ever use? $kill -9 Code 27

Slide 39

Slide 39 text

Caveats of signal handling •Handlers may run at *any* time •Your handlers must be re-entrant Code 28

Slide 40

Slide 40 text

Further Reading •Unicorn’s signal queue: https:// github.com/defunkt/unicorn/blob/ master/lib/unicorn/ http_server.rb#L262 •https://github.com/defunkt/unicorn/ blob/master/lib/unicorn/ http_server.rb#L131 •signal(7) 29

Slide 41

Slide 41 text

kgio Kindler, gentler, IO for ruby. 30

Slide 42

Slide 42 text

Why? Code 31

Slide 43

Slide 43 text

Why? •Unicorn uses nonblocking IO methods Code 31

Slide 44

Slide 44 text

Why? •Unicorn uses nonblocking IO methods •They raise Errno::EAGAIN when they would block Code 31

Slide 45

Slide 45 text

Why? •Unicorn uses nonblocking IO methods •They raise Errno::EAGAIN when they would block •Socket#accept will block until it gets a connection Code 31

Slide 46

Slide 46 text

Why? •Unicorn uses nonblocking IO methods •They raise Errno::EAGAIN when they would block •Socket#accept will block until it gets a connection •Socket#accept_nonblock will get a connection or raise Errno::EAGAIN Code 31

Slide 47

Slide 47 text

Why? Code 32

Slide 48

Slide 48 text

Why? Exceptions in Ruby (and other langs) are expensive Code 32

Slide 49

Slide 49 text

Bets and Memory Tricks But first, a note on micro-optimization 33

Slide 50

Slide 50 text

(A made-up definition) mi·cro·op·ti·mi·za·tion n. 1. Making the code more efficient by making it more obscure. 34

Slide 51

Slide 51 text

Don’t Micro-optimize Applications 35

Slide 52

Slide 52 text

It’s about tradeoffs •Would you rather get another 1 ms of performance or get Feature X implemented? •Applications set their own standards 36

Slide 53

Slide 53 text

System software has to micro-optimize •It’s at the mercy of the standards of the application •What if an application wants really fast responses and the server holds them back? 37

Slide 54

Slide 54 text

Let’s pretend for a minute What if Unicorn could save 0.1ms per request? 38

Slide 55

Slide 55 text

# make the following bet: if we accepted clients this round, # we're probably reasonably busy, so avoid calling select() # and do a speculative non-blocking accept() on ready listeners # before we sleep again in select(). unless nr == 0 # (nr < 0) => reopen logs (unlikely) ready = l.dup redo end 39

Slide 56

Slide 56 text

... # A frozen format for this is about 15% faster REMOTE_ADDR = 'REMOTE_ADDR'.freeze ... e[REMOTE_ADDR] = socket.kgio_addr ... # This is the typical way to accomplish the above: e['REMOTE_ADDR'] = socket.kgio_addr 40

Slide 57

Slide 57 text

# Does the majority of the IO processing. It has been written in # Ruby using about 8 different IO processing strategies. # # It is currently carefully constructed to make sure that it gets # the best possible performance for the common case: GET requests # that are fully complete after a single read(2) # # Anyone who thinks they can make it faster is more than welcome to # take a crack at it. # # returns an environment hash suitable for Rack if successful # This does minimal exception trapping and it is up to the caller # to handle any socket errors (e.g. user aborted upload). def read(socket) 41

Slide 58

Slide 58 text

Go read my source code! http://unicorn.bogomips.org/ 42

Slide 59

Slide 59 text

43

Slide 60

Slide 60 text

Resources •Unicorn source code (http:// unicorn.bogomips.org/) •usp.ruby list (http://librelist.com/browser/ usp.ruby/) •http://tomayko.com/writings/unicorn-is-unix •https://github.com/blog/517-unicorn2010/ everything-you-need-to-know-about-unicorn/ •http://www.engineyard.com/blog/2010/ everything-you-need-to-know-about-unicorn/ 44

Slide 61

Slide 61 text

Credits • Unicorn 502 page: http://chuckjhardy.com/post/3329075019/github-502-unicorn-server- error • The thinker: http://www.flickr.com/photos/22087304@N07/5074344673/ • Ritchie and Thompson: http://catb.org/~esr/writings/taoup/html/ch02s01.html • Blocks: http://www.flickr.com/photos/kmtucker/3355551036/ • Signal: http://www.flickr.com/photos/philipstorry/4924064517/ • Color scheme: http://www.colourlovers.com/palette/1111659/Ninja_Rainbow 45