t/lib.perl: fix Perl integration tests w/o installation
[unicorn.git] / DESIGN
blob0bac24f5a97297a4a8bb53e05df867a812c685f9
1 == Design
3 Unicorn was designed to support poorly-written codebases back in 2008.
4 Its unfortunate popularity has only proliferated the existence of
5 poorly-written code ever since...
7 * Simplicity: Unicorn is a traditional UNIX prefork web server.
8   No threads are used at all, this makes applications easier to debug
9   and fix.  When your application goes awry, a BOFH can just
10   "kill -9" the runaway worker process without worrying about tearing
11   all clients down, just one.  Only UNIX-like systems supporting
12   fork() and file descriptor inheritance are supported.
14 * The Ragel+C HTTP parser is taken from Mongrel.
16 * All HTTP parsing and I/O is done much like Mongrel:
17     1. read/parse HTTP request headers in full
18     2. call Rack application
19     3. write HTTP response back to the client
21 * Like Mongrel, neither keepalive nor pipelining are supported.
22   These aren't needed since Unicorn is only designed to serve
23   fast, low-latency clients directly.  Do one thing, do it well;
24   let nginx handle slow clients.
26 * Configuration is purely in Ruby and eval().  Ruby is less
27   ambiguous than YAML and lets lambdas for
28   before_fork/after_fork/before_exec hooks be defined inline.  An
29   optional, separate config_file may be used to modify supported
30   configuration changes (and also gives you plenty of rope if you RTFS
31   :>)
33 * One master process spawns and reaps worker processes.  The
34   Rack application itself is called only within the worker process (but
35   can be loaded within the master).  A copy-on-write friendly garbage
36   collector like the one found in mainline Ruby 2.0.0 and later
37   can be used to minimize memory usage along with the "preload_app true"
38   directive (see Unicorn::Configurator).
40 * The number of worker processes should be scaled to the number of
41   CPUs, memory or even spindles you have.  If you have an existing
42   Mongrel cluster on a single-threaded app, using the same amount of
43   processes should work.  Let a full-HTTP-request-buffering reverse
44   proxy like nginx manage concurrency to thousands of slow clients for
45   you.  Unicorn scaling should only be concerned about limits of your
46   backend system(s).
48 * Load balancing between worker processes is done by the OS kernel.
49   All workers share a common set of listener sockets and does
50   non-blocking accept() on them.  The kernel will decide which worker
51   process to give a socket to and workers will sleep if there is
52   nothing to accept().
54 * Since non-blocking accept() is used, there can be a thundering
55   herd when an occasional client connects when application
56   *is not busy*.  The thundering herd problem should not affect
57   applications that are running all the time since worker processes
58   will only select()/accept() outside of the application dispatch.
60 * Additionally, thundering herds are much smaller than with
61   configurations using existing prefork servers.  Process counts should
62   only be scaled to backend resources, _never_ to the number of expected
63   clients like is typical with blocking prefork servers.  So while we've
64   seen instances of popular prefork servers configured to run many
65   hundreds of worker processes, Unicorn deployments are typically only
66   2-4 processes per-core.
68 * On-demand scaling of worker processes never happens automatically.
69   Again, Unicorn is concerned about scaling to backend limits and should
70   never configured in a fashion where it could be waiting on slow
71   clients.  For extremely rare circumstances, we provide TTIN and TTOU
72   signal handlers to increment/decrement your process counts without
73   reloading.  Think of it as driving a car with manual transmission:
74   you have a lot more control if you know what you're doing.
76 * Blocking I/O is used for clients.  This allows a simpler code path
77   to be followed within the Ruby interpreter and fewer syscalls.
78   Applications that use threads continue to work if Unicorn
79   is only serving LAN or localhost clients.
81 * SIGKILL is used to terminate the timed-out workers from misbehaving apps
82   as reliably as possible on a UNIX system.  The default timeout is a
83   generous 60 seconds (same default as in Mongrel).
85 * The poor performance of select() on large FD sets is avoided
86   as few file descriptors are used in each worker.
87   There should be no gain from moving to highly scalable but
88   unportable event notification solutions for watching few
89   file descriptors.
91 * If the master process dies unexpectedly for any reason,
92   workers will notice within :timeout/2 seconds and follow
93   the master to its death.
95 * There is never any explicit real-time dependency or communication
96   between the worker processes nor to the master process.
97   Synchronization is handled entirely by the OS kernel and shared
98   resources are never accessed by the worker when it is servicing
99   a client.