|Date||Commit message (Collapse)|
This release reduces CPU usage for Linux 4.5+ in most cases.
See "[PATCH 6/6] use EPOLLEXCLUSIVE on Linux 4.5+" for more details:
There's a couple of updates for Ruby 3.1, but we've finally
started relying on Ruby 2.0.0 features after 9 years :P
(so Ruby 1.9.3 users are stuck with older versions).
And the usual round of doc updates and some build speedups.
13 changes by the Bozo Doofus maintainer since v6.0.0:
test_util: less excessive encoding tests
drop Ruby 1.9.3 support, require 2.0+ for now
drop unnecessary IO#close_on_exec=true assignment
extconf.rb: get rid of unnecessary checks
makefile: reduce unnecessary rebuilds
HACKING: drop outdated information about pandoc
http_server: get rid of Process.ppid check
worker_loop: get rid of select() avoidance hack
use EPOLLEXCLUSIVE on Linux 4.5+
allow Ruby to deduplicate remaining globals
epollexclusive: remove rb_gc_force_recycle call
drop Ruby version warning, fix speling errer
doc: v3 .onion updates, nntp => nntps, minor wording changes
Tor v2 .onion support is deprecated, so we're stuck with longer
(but more secure :P) v3 .onion names. Use NNTPS where available
instead of NNTP to reduce the likelyhood of MiTM censorship and
The warning was probably lost in the noise of the build, anyways.
It's deprecated in Ruby 3.1+, and probably not relevant for
Most of these are bound to be used in Rack/Rails/apps/gems,
(though possibly with different encodings). Give Ruby
a chance to deduplicate them, at least.
While the capabilities of epoll cannot be fully exploited given
our primitive design; avoiding thundering herd wakeups on larger
SMP machines while below 100% utilization is possible with
With this change, only one worker wakes up per-connect(2)
(instead of all of them via select(2)), avoiding the thundering
herd effect when the system is mostly idle.
Saturated instances should not notice the difference if they
rarely had multiple workers sleeping in select(2). This change
benefits non-saturated instances.
With 2 parallel clients and 8 workers on a nominally (:P)
8-core CPU (AMD FX-8320), the uconnect.perl test script
invocation showed a reduction from ~3.4s to ~2.5s when
reading an 11-byte response body:
echo worker_processes 8 >u.conf.rb
bs=11 ruby -I lib -I test/ruby-2.5.5/ext/unicorn_http/ bin/unicorn \
test/benchmark/dd.ru -E none -l /tmp/u.sock -c u.conf.rb
time perl -I lib -w test/benchmark/uconnect.perl \
-n 100000 -c 2 /tmp/u.sock
Times improve less as "-c" increases for uconnect.perl (system
noise and timings are inconsistent). The benefit of this change
should be more noticeable on systems with more workers (and
I wanted to use EPOLLET (Edge-Triggered) to further reduce
syscalls, here, (similar to the old select()-avoidance bet) but
that would've either added too much complexity to deduplicate
wakeup sources, or run into the same starvation problem we
solved in April 2020.
Since the kernel already has the complexity and deduplication
built-in for Level-Triggered epoll support, we'll just let the
kernel deal with it.
Note: do NOT take this as an example of how epoll should be used
in a sophisticated server. unicorn is primitive by design and
cannot use threads nor handle multiple clients at once, thus it
it only uses epoll in this extremely limited manner.
Linux 4.5+ users will notice a regression of one extra epoll FD
per-worker and at least two epoll watches, so
/proc/sys/fs/epoll/max_user_watches may need to be changed along
This change has also been tested on Linux 3.10.x (CentOS 7.x)
and FreeBSD 11.x to ensure compatibility with systems without
Various EPOLLEXCLUSIVE discussions over the years:
It doesn't seem to do anything since commit 221340c4ebc15666
(prevent single listener from monopolizing a worker, 2020-04-16).
It's actually been unnecessary since commit 6f6e4115b4bb03e5
(rework master-to-worker signaling to use a pipe, 2013-12-09)
It's been outdated since d9b5943af26d5df5 (doc: replace
pandoc-"Markdown" with real manpages, 2019-12-15)
For the most part, header changes shouldn't trigger extconf.rb
SIZEOF_*, *2NUM and NUM2* should all be defined by ruby.h and
dependencies it pulls in since Ruby 2.0 and possibly earlier.
INT_MAX and LLONG_MAX are in limits.h which is POSIX.
HAVE_GMTIME_R is already defined by ruby/config.h, so we
shouldn't have to check for it, either.
Combined, these changes speed up extconf.rb by several seconds.
Ruby 2.0+ sets FD_CLOEXEC by default on all FDs.
Ruby 1.9.3 was released nearly a decade ago, so there's probably
few (if any) legacy users left, and they can continue using old
versions of unicorn. We'll be able to take advantage of some
Ruby 2.0+-only features down the road (and hopefully 2.3+).
Also, I no longer have a installation of Ruby 1.8 and getting it
working probably isn't worth the effort, so 4.x support is gone.
Ruby's handling of encodings hasn't changed much in over
a decade and these tests haven't failed for me since August 2013:
So lets take a small step in reducing energy consumption
and save potential developers over 10s of CPU time.
This release allocates a new Rack `env' hash for every request.
This is done for safety with internally-(thread|event)-using Rack
apps which expect to use `env' after the normal Rack response is
complete, but without relying on rack.hijack. Thanks to
Dirkjan Bussink <email@example.com> for the patch:
The major version is bumped since:
1) there are performance regressions for some simple Rack apps
2) unsupported 3rd-party monkey patches which previously
relied on this behavior may be broken (our version of
The test suite is also more reliable on multi-core systems
and Ruby 3.x.
 thread from 2017 around rack.hijack safety:
tests: force blocking I/O for Ruby 3.x
Otherwise we get test failures since we use sysread
and syswrite in many places
Since we allocate a new request object for each request,
the #clear call is now unnecessary
This removes the reuse of the parser between requests. Reusing these is
risky in the context of running any other threads within the unicorn
process, also for threads that run background tasks.
If any other thread accidentally grabs hold of the request it can modify
things for the next request in flight.
The downside here is that we allocate more for each request, but that is
worth the trade off here and the security risk we otherwise would carry
to leaking wrong and incorrect data.
While no incompatible changes to any supported configuration
files are planned, some deep internal data structures will
change and there are likely some 3rd-party monkey patches broken
We don't want at_exit firing in child processes and never wanted
it. This is apparently a long standing bug in the tests that
only started causing test_worker_dies_on_dead_master failures
for me. I assume it's only showing up now for me due to kernel
scheduler changes, since I've been using the same 4-core CPU for
~11 years, now.
This release supports env['rack.after_reply'] which allows
rack middleware to pass lambdas to be executed after the client
connection is closed, matching functionality in Puma.
Thanks to Blake Williams for this patch:
The top-level of our website is now simpler and no longer
redundant with the contents of https://yhbt.net/unicorn/README.html
(which contains the old content)
created.rid has no business being published anyways, and
index.html is redundant given README.html. Avoid confusing
search engines with identical documents at different URLs.
Our "homepage" is just a directory listing, now, which should
help discoverability of non-HTML docs. Old content is at
This adds `rack.after_reply` functionality which allows rack middleware
to pass lambdas that will be executed after the client connection has
This was driven by a need to perform actions in a request that shouldn't
block the request from completing but also don't make sense as
There is prior art of this being supported found in a few gems, as well
as this functionality existing in other rack based servers (e.g. Puma).
[ew: check if `env' is set in ensure statement]
Acked-by: Eric Wong <firstname.lastname@example.org>
Relaxed Ruby version requirements for Ruby 3.0.0dev.
Thanks to Jean Boussier for testing
Apparently, an IMAP server happened at some point and somehow
stays running. Maybe more users have IMAP clients than NNTP
Ruby just recently bump the master version to 3.0.
This requirement bump is necessary to test unicorn
against ruby master.
[ew: wrap at <80 columns for hackers with poor eyesight]
Acked-by: Eric Wong <email@example.com>
We can limit the amount of Ruby-version-specific code to
just the stuff in ext/* and bin/*, reducing I/O traffic
and FS + page cache footprint.
Furthermore, rely on GNU make behavior to copy all the necessary
files so we don't trigger unnecessary extconf.rb invocations
just by touching a .rb file in lib.
This can be useful for diagnosing failures, especially since
GNU tail supports inotify these days.
This release adds support for the early_hints configurator
directive for the 'rack.early_hints' API used by Rails 5.2+.
Thanks to Jean Boussier for the patch.
If a user removes "early_hints" entirely from the config file, a
SIGHUP needs to restore the default value. This is consistent
with the behavior of all the other configuration variables.
Cc: Jean Boussier <firstname.lastname@example.org>
IO#sysread may only capture the 103 response and return before
the server can send the 200. Since we don't support persistent
connections, we can just use IO#read to rely on the server
giving us an EOF after the 200 is sent.
Cc: Jean Boussier <email@example.com>
While not part of the rack spec, this API is exposed
by both puma and falcon, and Rails use it when available.
The 103 Early Hints response code is specified in RFC 8297.
This release fixes a bug for users of multiple listeners setups
where a busy listen socket could starve other listeners.
Thanks to Stan Hu for reporting and testing.
No need to upgrade if you're using a single listen socket.
In setups with multiple listeners, it's possible for our greedy
select(2)-avoidance optimization to get pinned on a single, busy
listener and starve the other listener(s).
Prevent starvation by retrying the select(2)-avoidance
optimization if and only if all listeners were active. This
should have no effect on the majority of deployments with only a
Thanks to Stan Hu for reporting and testing.
Reported-by: Stan Hu <firstname.lastname@example.org>
Tested-by: Stan Hu <email@example.com>
One change to improve RFC 7230 conformance in the HTTP parser:
We need to favor "Transfer-Encoding: chunked" over
"Content-Length" in the request header if they both exist.
Furthermore, we now reject redundant chunking and cases where
"chunked" is not the final encoding.
We currently do not and have no plans to decode "gzip",
"deflate", or "compress" encoding as described by RFC 7230.
That's a job more appropriate for middleware, anyways.
Documentation updates to switch bogomips.org to yhbt.net since
the .org TLD won't be affordable in the near future.
There's also a few minor test cleanups.
We can start using Ruby 1.9 APIs, nowadays
It was added nearly 11 years ago in commit 6945342a1f0a4caa
("Transfer-Encoding: chunked streaming input support") but
My hardware gets worse and worse every year :<
bogomips.org is due to expire, soon, and I'm not willing to pay
extortionist fees to Ethos Capital/PIR/ICANN to keep a .org. So
it's at yhbt.net, for now, but it will change again to
whatever's affordable... Identity is overrated.
Tor users can use .onions and kick ICANN to the curb:
torsocks w3m http://unicorn.ou63pmih66umazou.onion/
torsocks git clone http://ou63pmih66umazou.onion/unicorn.git/
torsocks w3m http://ou63pmih66umazou.onion/unicorn-public/
While we're at it, `s/news.gmane.org/news.gmane.io/g', too.
(but I suspect that'll need to be resynched since our mail
"List-Id:" header is changing).
Thanks to Terry Scheingeld, we now workaround a Ruby bug
and can now run with taint checks enabled:
There's also a few documentation updates and building packages
from source is easier since pandoc is no longer a dependency
(and I can no longer afford the bandwidth or space to install
Eric Wong (7):
test/benchmark/ddstream: demo for slowly reading clients
test/benchmark/readinput: demo for slowly uploading clients
test/benchmark/uconnect: test for accept loop speed
examples/unicorn@.service: note the NonBlocking flag
Merge remote-tracking branch 'origin/ts/tmpio'
test_util: get rid of some unused variables in tests
doc: replace pandoc-"Markdown" with real manpages
Terry Scheingeld (1):
tmpio: workaround File#path being tainted on unlink
Trying to install pandoc on an x86-64 Debian stable system says:
> Need to get 15.2 MB of archives.
> After this operation, 117 MB of additional disk space will be used.
My laptop is on metered Internet nowadays and already low on
disk space, so installing pandoc is not realistic. Maybe it
wasn't realistic to other hackers with limited resources in the
There's also dozens of subtly incompatible Markdown flavors out
there, most of which can't really handle manpages. Anyways,
roff isn't too bad and at least groff is well-documented.
Updating the website now requires olddoc 1.8.0 (which is much
smaller than pandoc), but I'm the only one with that burden. On
the flipside more users can update and read the manpages locally
without extra software, since nearly every developer's *nix
system has man(1) command, unlike pandoc.
Ruby 2.7.0dev warns on them
tmpio: workaround File#path being tainted on unlink
Ruby mistakenly taints the file path, causing File.unlink
to fail: https://bugs.ruby-lang.org/issues/14485
Workaround the Ruby bug by keeping the path as a local
variable and passing that to File.unlink, instead of the
return value of File#path.
It's racy otherwise when starting simultaneous instanced units.
Without specifying NonBlocking=true, systemd will clear the
O_NONBLOCK flag every time it starts a new service instance.
There's a small window where systemd can clear O_NONBLOCK
immediately after it's set by Ruby (or kgio):
unicorn@1 |systemd |unicorn@2
F_SETFL, O_NONBLOCK|O_RDWR | | (not running, yet)
|F_SETFL, O_RDWR |
| exec unicorn@2 |
accept4(...) # blocks! | | (now started by systemd)
| |accept4(...) non-blocking
In preparation for kgio removal, I want to ensure we can
maintain existing performance when swapping kgio_tryaccept
for accept_nonblock on Ruby 2.3+
There's plenty of TCP benchmarking tools, but TCP port reuse
delays hurt predictability since unicorn doesn't do persistent
So this is exclusively for Unix sockets and uses Perl instead
of Ruby since I don't want to be bothered with GC
unpredictability on the client side.
This is intended to demonstrate how badly we suck at dealing
with slow clients making uploads. It can help users evaluate
alternative fully-buffering reverse proxies, because nginx
should not be the only option.