unicorn_rails: give more info when aborting
[unicorn.git] / DESIGN
blob288502b3529d0175b5c61db28733aaeb71d4907a
1 == Design
3 * Simplicity: Unicorn is a traditional UNIX prefork web server.
4   No threads are used at all, this makes applications easier to debug
5   and fix.  When your application goes awry, a BOFH can just
6   "kill -9" the runaway worker process without worrying about tearing
7   all clients down, just one.  Only UNIX-like systems supporting
8   fork() and file descriptor inheritance are supported.
10 * The Ragel->C HTTP parser is taken from Mongrel.  This is the
11   only non-Ruby part and there are no plans to add any more
12   non-Ruby components.
14 * All HTTP protocol parsing and I/O is done just like Mongrel:
15     1. read/parse HTTP request in full
16     2. call Rack application
17     3. write HTTP response back to the client
19 * Like Mongrel, neither keepalive nor pipelining are supported.
20   These aren't needed since Unicorn is only designed to serve
21   fast, low-latency clients directly.  Do one thing, do it well;
22   let nginx handle slow clients.
24 * Configuration is purely in Ruby and eval().  Ruby is less
25   ambiguous than YAML and lets lambdas for
26   before_fork/after_fork/before_exec hooks be defined inline.  An
27   optional, separate config_file may be used to modify supported
28   configuration changes (and also gives you plenty of rope if you RTFS
29   :>)
31 * One master process spawns and reaps worker processes.  The
32   Rack application itself is called only within the worker process (but
33   can be loaded within the master).  A copy-on-write friendly garbage
34   collector like Ruby Enterprise Edition can be used to minimize memory
35   usage along with the "preload_app true" directive (see
36   Unicorn::Configurator).
38 * The number of worker processes should be scaled to the number of
39   CPUs, memory or even spindles you have.  If you have an existing
40   Mongrel cluster, using the same amount of processes should work.
41   Let a full-HTTP-request-buffering reverse proxy like nginx manage
42   concurrency to thousands of slow clients for you.  Unicorn scaling
43   should only be concerned about limits of your backend system(s).
45 * Load balancing between worker processes is done by the OS kernel.
46   All workers share a common set of listener sockets and does
47   non-blocking accept() on them.  The kernel will decide which worker
48   process to give a socket to and workers will sleep if there is
49   nothing to accept().
51 * Since non-blocking accept() is used, there can be a thundering
52   herd when an occasional client connects when application
53   *is not busy*.  The thundering herd problem should not affect
54   applications that are running all the time since worker processes
55   will only select()/accept() outside of the application dispatch.
57 * Blocking I/O is used for clients.  This allows a simpler code path
58   to be followed within the Ruby interpreter and fewer syscalls.
59   Applications that use threads should continue to work if Unicorn
60   is serving LAN or localhost clients.
62 * Timeout implementation is done via fchmod(2) in each worker
63   on a shared file descriptor to update st_ctime on the inode.
64   Master process wakeups for checking on timeouts is throttled
65   one a second to minimize the performance impact and simplify
66   the code path within the worker.  Neither futimes(2) nor
67   pwrite(2)/pread(2) are supported by base MRI, nor are they as
68   portable on UNIX systems as fchmod(2).
70 * SIGKILL is used to terminate the timed-out workers as reliably
71   as possible on a UNIX system.
73 * The poor performance of select() on large FD sets is avoided
74   as few file descriptors are used in each worker.
75   There should be no gain from moving to highly scalable but
76   unportable event notification solutions for watching few
77   file descriptors.
79 * If the master process dies unexpectedly for any reason,
80   workers will notice within :timeout/2 seconds and follow
81   the master to its death.