unicorn Ruby/Rack server user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download mbox.gz: |
* [PATCH 0/4] a small pile of patches
@ 2024-03-23 19:45  1% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2024-03-23 19:45 UTC (permalink / raw)
  To: unicorn-public

[-- Attachment #1: Type: text/plain, Size: 4446 bytes --]

Some stuff to future-proof against future Ruby incompatibilities.
More coming....

I've also pushed out preliminary work (started in 2021) to the
`pico' branch to switch the HTTP parser from Ragel to
picohttpparser.  It will simplify the build + maintenance,
especially when distros carry different Ragel versions (or don't
package it all, as some hackers can't afford bandwidth and disk
for a C++ toolchain).

Other notes: New releases will probably be hosted on yhbt.net if
the Rubygems.org MFA threshold is reached.  Caring about the
identity of hackers is totally misguided when we already show
our code (and even document it!).  If you can't audit the code
yourself, get an actual professional to do it and don't bother
amateurs like me.

Eric Wong (4):
  t/integration: disable proxies when running curl(1)
  tests: port back-out-of-upgrade to Perl 5
  doc: various updates and disclaimers
  treewide: future-proof frozen_string_literal changes

 HACKING                             |  13 +++-
 README                              |   9 +++
 Rakefile                            |   1 +
 TODO                                |   4 +-
 bin/unicorn                         |   1 +
 bin/unicorn_rails                   |   1 +
 examples/big_app_gc.rb              |   1 +
 examples/echo.ru                    |   1 +
 examples/logger_mp_safe.rb          |   1 +
 examples/unicorn.conf.minimal.rb    |   1 +
 examples/unicorn.conf.rb            |   1 +
 ext/unicorn_http/extconf.rb         |   1 +
 lib/unicorn.rb                      |   1 +
 lib/unicorn/app/old_rails.rb        |   1 +
 lib/unicorn/app/old_rails/static.rb |   1 +
 lib/unicorn/cgi_wrapper.rb          |   1 +
 lib/unicorn/configurator.rb         |   1 +
 lib/unicorn/const.rb                |   1 +
 lib/unicorn/http_request.rb         |   1 +
 lib/unicorn/http_response.rb        |   1 +
 lib/unicorn/http_server.rb          |   1 +
 lib/unicorn/launcher.rb             |   1 +
 lib/unicorn/oob_gc.rb               |   1 +
 lib/unicorn/preread_input.rb        |   1 +
 lib/unicorn/select_waiter.rb        |   1 +
 lib/unicorn/socket_helper.rb        |   1 +
 lib/unicorn/stream_input.rb         |   1 +
 lib/unicorn/tee_input.rb            |   1 +
 lib/unicorn/tmpio.rb                |   1 +
 lib/unicorn/util.rb                 |   1 +
 lib/unicorn/worker.rb               |   1 +
 setup.rb                            |   1 +
 t/back-out-of-upgrade.t             |  44 +++++++++++
 t/broken-app.ru                     |   1 +
 t/client_body_buffer_size.ru        |   1 +
 t/detach.ru                         |   1 +
 t/env.ru                            |   1 +
 t/fails-rack-lint.ru                |   1 +
 t/heartbeat-timeout.ru              |   1 +
 t/integration.ru                    |   1 +
 t/integration.t                     |   1 +
 t/lib.perl                          |  67 ++++++++++++++---
 t/listener_names.ru                 |   1 +
 t/oob_gc.ru                         |   1 +
 t/oob_gc_path.ru                    |   1 +
 t/pid.ru                            |   1 +
 t/preread_input.ru                  |   1 +
 t/reopen-logs.ru                    |   1 +
 t/t0008-back_out_of_upgrade.sh      | 110 ----------------------------
 t/t0013.ru                          |   1 +
 t/t0014.ru                          |   1 +
 t/t0301.ru                          |   1 +
 test/aggregate.rb                   |   1 +
 test/benchmark/dd.ru                |   1 +
 test/benchmark/ddstream.ru          |   1 +
 test/benchmark/readinput.ru         |   1 +
 test/benchmark/stack.ru             |   1 +
 test/exec/test_exec.rb              |   1 +
 test/test_helper.rb                 |   1 +
 test/unit/test_ccc.rb               |   1 +
 test/unit/test_configurator.rb      |   1 +
 test/unit/test_droplet.rb           |   1 +
 test/unit/test_http_parser.rb       |   1 +
 test/unit/test_http_parser_ng.rb    |   1 +
 test/unit/test_request.rb           |   1 +
 test/unit/test_server.rb            |   1 +
 test/unit/test_signals.rb           |   1 +
 test/unit/test_socket_helper.rb     |   1 +
 test/unit/test_stream_input.rb      |   1 +
 test/unit/test_tee_input.rb         |   1 +
 test/unit/test_util.rb              |   1 +
 test/unit/test_waiter.rb            |   1 +
 unicorn.gemspec                     |   1 +
 73 files changed, 188 insertions(+), 126 deletions(-)
 create mode 100644 t/back-out-of-upgrade.t
 delete mode 100755 t/t0008-back_out_of_upgrade.sh

[-- Attachment #2: 0001-t-integration-disable-proxies-when-running-curl-1.patch --]
[-- Type: text/x-diff, Size: 737 bytes --]

From f3acce5dce62ac4b0288d3c0ddf0a6db2cbd9e7f Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Tue, 9 Jan 2024 21:35:08 +0000
Subject: [PATCH 1/4] t/integration: disable proxies when running curl(1)

This was also done in t/test-lib.sh, but using '*' is more
encompassing.
---
 t/integration.t | 1 +
 1 file changed, 1 insertion(+)

diff --git a/t/integration.t b/t/integration.t
index 7310ff2..d17ace0 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -27,6 +27,7 @@ listen "$u1"
 EOM
 my $ar = unicorn(qw(-E none t/integration.ru -c), $u_conf, { 3 => $srv });
 my $curl = which('curl');
+local $ENV{NO_PROXY} = '*'; # for curl
 my $fifo = "$tmpdir/fifo";
 POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
 my %PUT = (

[-- Attachment #3: 0002-tests-port-back-out-of-upgrade-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 8889 bytes --]

From 724fb631c76f09964ec289ee8e144886ba15d380 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 6 Nov 2023 05:45:29 +0000
Subject: [PATCH 2/4] tests: port back-out-of-upgrade to Perl 5

Another place where we can be faster without adding more
dependencies on Ruby maintaining stable behavior.
---
 t/back-out-of-upgrade.t        |  44 +++++++++++++
 t/lib.perl                     |  67 +++++++++++++++++---
 t/t0008-back_out_of_upgrade.sh | 110 ---------------------------------
 3 files changed, 102 insertions(+), 119 deletions(-)
 create mode 100644 t/back-out-of-upgrade.t
 delete mode 100755 t/t0008-back_out_of_upgrade.sh

diff --git a/t/back-out-of-upgrade.t b/t/back-out-of-upgrade.t
new file mode 100644
index 0000000..cf3b09f
--- /dev/null
+++ b/t/back-out-of-upgrade.t
@@ -0,0 +1,44 @@
+#!perl -w
+# Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+# test backing out of USR2 upgrade
+use v5.14; BEGIN { require './t/lib.perl' };
+use autodie;
+my $srv = tcp_server();
+mkfifo_die $fifo;
+write_file '>', $u_conf, <<EOM;
+preload_app true
+stderr_path "$err_log"
+pid "$pid_file"
+after_fork { |s,w| File.open('$fifo', 'w') { |fp| fp.write "pid=#\$\$" } }
+EOM
+my $ar = unicorn(qw(-E none t/pid.ru -c), $u_conf, { 3 => $srv });
+
+like(my $wpid_orig_1 = slurp($fifo), qr/\Apid=\d+\z/a, 'got worker pid');
+
+ok $ar->do_kill('USR2'), 'USR2 to start upgrade';
+ok $ar->do_kill('WINCH'), 'drop old worker';
+
+like(my $wpid_new = slurp($fifo), qr/\Apid=\d+\z/a, 'got pid from new master');
+chomp(my $new_pid = slurp($pid_file));
+isnt $new_pid, $ar->{pid}, 'PID file changed';
+chomp(my $pid_oldbin = slurp("$pid_file.oldbin"));
+is $pid_oldbin, $ar->{pid}, '.oldbin PID valid';
+
+ok $ar->do_kill('HUP'), 'HUP old master';
+like(my $wpid_orig_2 = slurp($fifo), qr/\Apid=\d+\z/a, 'got worker new pid');
+ok kill('QUIT', $new_pid), 'abort old master';
+kill_until_dead $new_pid;
+
+my ($st, $hdr, $req_pid) = do_req $srv, 'GET /';
+chomp $req_pid;
+is $wpid_orig_2, "pid=$req_pid", 'new worker on old worker serves';
+
+ok !-f "$pid_file.oldbin", '.oldbin PID file gone';
+chomp(my $old_pid = slurp($pid_file));
+is $old_pid, $ar->{pid}, 'PID file restored';
+
+my @log = grep !/ERROR -- : reaped .*? exec\(\)-ed/, slurp($err_log);
+check_stderr @log;
+undef $tmpdir;
+done_testing;
diff --git a/t/lib.perl b/t/lib.perl
index 9254b23..b20a2c6 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -6,30 +6,58 @@ use v5.14;
 use parent qw(Exporter);
 use autodie;
 use Test::More;
+use Socket qw(SOMAXCONN);
 use Time::HiRes qw(sleep time);
 use IO::Socket::INET;
+use IO::Socket::UNIX;
+use Carp qw(croak);
 use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh, $err_log, $u_sock, $u_conf, $daemon_pid,
-	$pid_file);
+	$pid_file, $wtest_sock, $fifo);
 our @EXPORT = qw(unicorn slurp tcp_server tcp_start unicorn
 	$tmpdir $errfh $err_log $u_sock $u_conf $daemon_pid $pid_file
+	$wtest_sock $fifo
 	SEEK_SET tcp_host_port which spawn check_stderr unix_start slurp_hdr
-	do_req stop_daemon sleep time);
+	do_req stop_daemon sleep time mkfifo_die kill_until_dead write_file);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
+
+$wtest_sock = "$tmpdir/wtest.sock";
 $err_log = "$tmpdir/err.log";
 $pid_file = "$tmpdir/pid";
+$fifo = "$tmpdir/fifo";
 $u_sock = "$tmpdir/u.sock";
 $u_conf = "$tmpdir/u.conf.rb";
 open($errfh, '>>', $err_log);
 
+if (my $t = $ENV{TAIL}) {
+	my @tail = $t =~ /tail/ ? split(/\s+/, $t) : (qw(tail -F));
+	push @tail, $err_log;
+	my $pid = fork;
+	if ($pid == 0) {
+		open STDOUT, '>&', \*STDERR;
+		exec @tail;
+		die "exec(@tail): $!";
+	}
+	say "# @tail";
+	sleep 0.2;
+	UnicornTest::AutoReap->new($pid);
+}
+
+sub kill_until_dead ($;%) {
+	my ($pid, %opt) = @_;
+	my $tries = $opt{tries} // 1000;
+	my $sig = $opt{sig} // 0;
+	while (CORE::kill($sig, $pid) && --$tries) { sleep(0.01) }
+	$tries or croak "PID: $pid died after signal ($sig)";
+}
+
 sub stop_daemon (;$) {
 	my ($is_END) = @_;
 	kill('TERM', $daemon_pid);
-	my $tries = 1000;
-	while (CORE::kill(0, $daemon_pid) && --$tries) { sleep(0.01) }
+	kill_until_dead $daemon_pid;
 	if ($is_END && CORE::kill(0, $daemon_pid)) { # after done_testing
 		CORE::kill('KILL', $daemon_pid);
 		die "daemon_pid=$daemon_pid did not die";
@@ -44,8 +72,9 @@ END {
 	stop_daemon(1) if defined $daemon_pid;
 };
 
-sub check_stderr () {
-	my @log = slurp($err_log);
+sub check_stderr (@) {
+	my @log = @_;
+	slurp($err_log) if !@log;
 	diag("@log") if $ENV{V};
 	my @err = grep(!/NameError.*Unicorn::Waiter/, grep(/error/i, @log));
 	@err = grep(!/failed to set accept_filter=/, @err);
@@ -63,6 +92,16 @@ sub slurp_hdr {
 	($status, \@hdr);
 }
 
+sub unix_server (;$@) {
+	my $l = shift // $u_sock;
+	IO::Socket::UNIX->new(Listen => SOMAXCONN, Local => $l, Blocking => 0,
+				Type => SOCK_STREAM, @_);
+}
+
+sub unix_connect ($) {
+	IO::Socket::UNIX->new(Peer => $_[0], Type => SOCK_STREAM);
+}
+
 sub tcp_server {
 	my %opt = (
 		ReuseAddr => 1,
@@ -95,8 +134,7 @@ sub tcp_host_port {
 
 sub unix_start ($@) {
 	my ($dst, @req) = @_;
-	my $s = IO::Socket::UNIX->new(Peer => $dst, Type => SOCK_STREAM) or
-		BAIL_OUT "unix connect $dst: $!";
+	my $s = unix_connect($dst) or BAIL_OUT "unix connect $dst: $!";
 	$s->autoflush(1);
 	print $s @req, "\r\n\r\n" if @req;
 	$s;
@@ -201,7 +239,7 @@ sub unicorn {
 	state $ver = $ENV{TEST_RUBY_VERSION} // `$ruby -e 'print RUBY_VERSION'`;
 	state $eng = $ENV{TEST_RUBY_ENGINE} // `$ruby -e 'print RUBY_ENGINE'`;
 	state $ext = File::Spec->rel2abs("test/$eng-$ver/ext/unicorn_http");
-	state $exe = File::Spec->rel2abs('bin/unicorn');
+	state $exe = File::Spec->rel2abs("test/$eng-$ver/bin/unicorn");
 	my $pid = spawn(\%env, $ruby, '-I', $lib, '-I', $ext, $exe, @args);
 	UnicornTest::AutoReap->new($pid);
 }
@@ -219,6 +257,17 @@ sub do_req ($@) {
 	($status, $hdr, $bdy);
 }
 
+sub mkfifo_die ($;$) {
+	POSIX::mkfifo($_[0], $_[1] // 0600) or croak "mkfifo: $!";
+}
+
+sub write_file ($$@) { # mode, filename, LIST (for print)
+	open(my $fh, shift, shift);
+	print $fh @_;
+	# return $fh for futher writes if user wants it:
+	defined(wantarray) && !wantarray ? $fh : close $fh;
+}
+
 # automatically kill + reap children when this goes out-of-scope
 package UnicornTest::AutoReap;
 use v5.14;
diff --git a/t/t0008-back_out_of_upgrade.sh b/t/t0008-back_out_of_upgrade.sh
deleted file mode 100755
index 96d4057..0000000
--- a/t/t0008-back_out_of_upgrade.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 13 "backout of USR2 upgrade"
-
-worker_wait_start () {
-	test xSTART = x"$(cat $fifo)"
-	unicorn_pid=$(cat $pid)
-}
-
-t_begin "setup and start" && {
-	unicorn_setup
-	rm -f $pid.oldbin
-
-cat >> $unicorn_config <<EOF
-after_fork do |server, worker|
-  # test script will block while reading from $fifo,
-  # so notify the script on the first worker we spawn
-  # by opening the FIFO
-  if worker.nr == 0
-    File.open("$fifo", "wb") { |fp| fp.syswrite "START" }
-  end
-end
-EOF
-	unicorn -D -c $unicorn_config pid.ru
-	worker_wait_start
-	orig_master_pid=$unicorn_pid
-}
-
-t_begin "read original worker pid" && {
-	orig_worker_pid=$(curl -sSf http://$listen/)
-	test -n "$orig_worker_pid" && kill -0 $orig_worker_pid
-}
-
-t_begin "upgrade to new master" && {
-	kill -USR2 $orig_master_pid
-}
-
-t_begin "kill old worker" && {
-	kill -WINCH $orig_master_pid
-}
-
-t_begin "wait for new worker to start" && {
-	worker_wait_start
-	test $unicorn_pid -ne $orig_master_pid
-	new_master_pid=$unicorn_pid
-}
-
-t_begin "old master pid is stashed in $pid.oldbin" && {
-	test -s "$pid.oldbin"
-	test $orig_master_pid -eq $(cat $pid.oldbin)
-}
-
-t_begin "ensure old worker is no longer running" && {
-	i=0
-	while kill -0 $orig_worker_pid 2>/dev/null
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-}
-
-t_begin "capture pid of new worker" && {
-	new_worker_pid=$(curl -sSf http://$listen/)
-}
-
-t_begin "reload old master process" && {
-	kill -HUP $orig_master_pid
-	worker_wait_start
-}
-
-t_begin "gracefully kill new master and ensure it dies" && {
-	kill -QUIT $new_master_pid
-	i=0
-	while kill -0 $new_worker_pid 2>/dev/null
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-}
-
-t_begin "ensure $pid.oldbin does not exist" && {
-	i=0
-	while test -s $pid.oldbin
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-	while ! test -s $pid
-	do
-		i=$(( $i + 1 ))
-		test $i -lt 600 || die "timed out"
-		sleep 1
-	done
-}
-
-t_begin "ensure $pid is correct" && {
-	cur_master_pid=$(cat $pid)
-	test $orig_master_pid -eq $cur_master_pid
-}
-
-t_begin "killing succeeds" && {
-	kill $orig_master_pid
-}
-
-dbgcat r_err
-
-t_done

[-- Attachment #4: 0003-doc-various-updates-and-disclaimers.patch --]
[-- Type: text/x-diff, Size: 3343 bytes --]

From 69d15a7a51a096b6acf00ccf23e1b988076d3b5f Mon Sep 17 00:00:00 2001
From: Eric Wong <bofh@yhbt.net>
Date: Mon, 1 Jan 2024 10:43:13 +0000
Subject: [PATCH 3/4] doc: various updates and disclaimers

Covering my ass from draconian legislation.
---
 HACKING | 13 +++++++++----
 README  |  9 +++++++++
 TODO    |  4 +---
 3 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/HACKING b/HACKING
index 5aca83e..777e75e 100644
--- a/HACKING
+++ b/HACKING
@@ -6,6 +6,8 @@ Like Mongrel, we use Ruby where it makes sense, and Ragel with C where
 it helps performance.  All of the code that actually runs your Rack
 application is written Ruby, Ragel or C.
 
+Ragel may be dropped in favor of a picohttpparser-based one in the future.
+
 As far as tests and documentation goes, we're not afraid to embrace Unix
 and use traditional Unix tools where they make sense and get the job
 done.
@@ -16,6 +18,9 @@ Tests are good, but slow tests make development slow, so we make tests
 faster (in parallel) with GNU make (instead of Rake) and avoiding
 RubyGems.
 
+New tests are written in Perl 5 and use TAP <https://testanything.org/>
+to ensure stability and immunity from Ruby incompatibilities.
+
 Users of GNU-based systems (such as GNU/Linux) usually have GNU make
 installed as "make" instead of "gmake".
 
@@ -69,10 +74,10 @@ supported by the versions of Ruby we target.
 
 === Ragel Compatibility
 
-We target the latest released version of Ragel and will update our code
-to keep up with new releases.  Packaged tarballs and gems include the
-generated source code so they will remain usable if compatibility is
-broken.
+We target the latest released version of Ragel in Debian and will update
+our code to keep up with new releases.  Packaged tarballs and gems
+include the generated source code so they will remain usable if
+compatibility is broken.
 
 == Contributing
 
diff --git a/README b/README
index 84c0fdf..b60ed00 100644
--- a/README
+++ b/README
@@ -122,6 +122,7 @@ supported.  Run `unicorn -h` to see command-line options.
 
 There is NO WARRANTY whatsoever if anything goes wrong, but
 {let us know}[link:ISSUES.html] and maybe someone can fix it.
+No commercial support will ever be provided by the amateur maintainer.
 
 unicorn is designed to only serve fast clients either on the local host
 or a fast LAN.  See the PHILOSOPHY and DESIGN documents for more details
@@ -132,6 +133,14 @@ damage done to the entire Ruby ecosystem.  Its unintentional popularity
 set Ruby back decades in parallelism, concurrency and robustness since
 it prolongs and proliferates the existence of poorly-written code.
 
+unicorn hackers are NOT responsible for your supply chain security:
+read and understand it yourself or get someone you trust to audit it.
+Malicious commits and releases will be made if under duress.  The only
+defense you'll ever have is from reviewing the source code.
+
+No user or contributor will ever be expected to sacrifice their own
+security by running JavaScript or revealing any personal information.
+
 == Contact
 
 All feedback (bug reports, user/development dicussion, patches, pull
diff --git a/TODO b/TODO
index ebbccdc..a3b18fd 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1 @@
-* Documentation improvements
-
-* improve test suite
+* improve test suite (port to Perl 5 for stability and maintainability)

[-- Attachment #5: 0004-treewide-future-proof-frozen_string_literal-changes.patch --]
[-- Type: text/x-diff, Size: 24100 bytes --]

From ccf2443901c18ffb26b2785f52d921005e862167 Mon Sep 17 00:00:00 2001
From: Eric Wong <bofh@yhbt.net>
Date: Thu, 8 Feb 2024 12:16:31 +0000
Subject: [PATCH 4/4] treewide: future-proof frozen_string_literal changes

Once again Ruby seems ready to introduce more incompatibilities
and force busywork upon maintainers[1].  In order to avoid
incompatibilities in the future, I used a Perl script[2] to
prepend `frozen_string_literal: false' to every Ruby file.

Somebody interested will have to go through every Ruby source
file and enable frozen_string_literal once they've thoroughly
verified it's safe to do so.

[1] https://bugs.ruby-lang.org/issues/20205
[2] https://yhbt.net/add-fsl.git/74d7689/s/?b=add-fsl.perl
---
 Rakefile                            | 1 +
 bin/unicorn                         | 1 +
 bin/unicorn_rails                   | 1 +
 examples/big_app_gc.rb              | 1 +
 examples/echo.ru                    | 1 +
 examples/logger_mp_safe.rb          | 1 +
 examples/unicorn.conf.minimal.rb    | 1 +
 examples/unicorn.conf.rb            | 1 +
 ext/unicorn_http/extconf.rb         | 1 +
 lib/unicorn.rb                      | 1 +
 lib/unicorn/app/old_rails.rb        | 1 +
 lib/unicorn/app/old_rails/static.rb | 1 +
 lib/unicorn/cgi_wrapper.rb          | 1 +
 lib/unicorn/configurator.rb         | 1 +
 lib/unicorn/const.rb                | 1 +
 lib/unicorn/http_request.rb         | 1 +
 lib/unicorn/http_response.rb        | 1 +
 lib/unicorn/http_server.rb          | 1 +
 lib/unicorn/launcher.rb             | 1 +
 lib/unicorn/oob_gc.rb               | 1 +
 lib/unicorn/preread_input.rb        | 1 +
 lib/unicorn/select_waiter.rb        | 1 +
 lib/unicorn/socket_helper.rb        | 1 +
 lib/unicorn/stream_input.rb         | 1 +
 lib/unicorn/tee_input.rb            | 1 +
 lib/unicorn/tmpio.rb                | 1 +
 lib/unicorn/util.rb                 | 1 +
 lib/unicorn/worker.rb               | 1 +
 setup.rb                            | 1 +
 t/broken-app.ru                     | 1 +
 t/client_body_buffer_size.ru        | 1 +
 t/detach.ru                         | 1 +
 t/env.ru                            | 1 +
 t/fails-rack-lint.ru                | 1 +
 t/heartbeat-timeout.ru              | 1 +
 t/integration.ru                    | 1 +
 t/listener_names.ru                 | 1 +
 t/oob_gc.ru                         | 1 +
 t/oob_gc_path.ru                    | 1 +
 t/pid.ru                            | 1 +
 t/preread_input.ru                  | 1 +
 t/reopen-logs.ru                    | 1 +
 t/t0013.ru                          | 1 +
 t/t0014.ru                          | 1 +
 t/t0301.ru                          | 1 +
 test/aggregate.rb                   | 1 +
 test/benchmark/dd.ru                | 1 +
 test/benchmark/ddstream.ru          | 1 +
 test/benchmark/readinput.ru         | 1 +
 test/benchmark/stack.ru             | 1 +
 test/exec/test_exec.rb              | 1 +
 test/test_helper.rb                 | 1 +
 test/unit/test_ccc.rb               | 1 +
 test/unit/test_configurator.rb      | 1 +
 test/unit/test_droplet.rb           | 1 +
 test/unit/test_http_parser.rb       | 1 +
 test/unit/test_http_parser_ng.rb    | 1 +
 test/unit/test_request.rb           | 1 +
 test/unit/test_server.rb            | 1 +
 test/unit/test_signals.rb           | 1 +
 test/unit/test_socket_helper.rb     | 1 +
 test/unit/test_stream_input.rb      | 1 +
 test/unit/test_tee_input.rb         | 1 +
 test/unit/test_util.rb              | 1 +
 test/unit/test_waiter.rb            | 1 +
 unicorn.gemspec                     | 1 +
 66 files changed, 66 insertions(+)

diff --git a/Rakefile b/Rakefile
index 37569ce..fe1588b 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # optional rake-compiler support in case somebody needs to cross compile
 begin
   mk = "ext/unicorn_http/Makefile"
diff --git a/bin/unicorn b/bin/unicorn
index 00c8464..af8353c 100755
--- a/bin/unicorn
+++ b/bin/unicorn
@@ -1,5 +1,6 @@
 #!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'unicorn/launcher'
 require 'optparse'
 
diff --git a/bin/unicorn_rails b/bin/unicorn_rails
index 354c1df..374fd8e 100755
--- a/bin/unicorn_rails
+++ b/bin/unicorn_rails
@@ -1,5 +1,6 @@
 #!/this/will/be/overwritten/or/wrapped/anyways/do/not/worry/ruby
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'unicorn/launcher'
 require 'optparse'
 require 'fileutils'
diff --git a/examples/big_app_gc.rb b/examples/big_app_gc.rb
index c1bae10..0baea26 100644
--- a/examples/big_app_gc.rb
+++ b/examples/big_app_gc.rb
@@ -1,2 +1,3 @@
+# frozen_string_literal: false
 # see {Unicorn::OobGC}[https://yhbt.net/unicorn/Unicorn/OobGC.html]
 # Unicorn::OobGC was broken in Unicorn v3.3.1 - v3.6.1 and fixed in v3.6.2
diff --git a/examples/echo.ru b/examples/echo.ru
index e982180..453a5e6 100644
--- a/examples/echo.ru
+++ b/examples/echo.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 #
 # Example application that echoes read data back to the HTTP client.
 # This emulates the old echo protocol people used to run.
diff --git a/examples/logger_mp_safe.rb b/examples/logger_mp_safe.rb
index 05ad3fa..f2c0500 100644
--- a/examples/logger_mp_safe.rb
+++ b/examples/logger_mp_safe.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # Multi-Processing-safe monkey patch for Logger
 #
 # This monkey patch fixes the case where "preload_app true" is used and
diff --git a/examples/unicorn.conf.minimal.rb b/examples/unicorn.conf.minimal.rb
index 46fd634..4f96ede 100644
--- a/examples/unicorn.conf.minimal.rb
+++ b/examples/unicorn.conf.minimal.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # Minimal sample configuration file for Unicorn (not Rack) when used
 # with daemonization (unicorn -D) started in your working directory.
 #
diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index d90bdc4..5bae830 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # Sample verbose configuration file for Unicorn (not Rack)
 #
 # This configuration file documents many features of Unicorn
diff --git a/ext/unicorn_http/extconf.rb b/ext/unicorn_http/extconf.rb
index 11099cd..de896fe 100644
--- a/ext/unicorn_http/extconf.rb
+++ b/ext/unicorn_http/extconf.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'mkmf'
 
 have_func("rb_hash_clear", "ruby.h") or abort 'Ruby 2.0+ required'
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index 564cb30..fb91679 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'etc'
 require 'stringio'
 require 'raindrops'
diff --git a/lib/unicorn/app/old_rails.rb b/lib/unicorn/app/old_rails.rb
index 1e8c41a..54b3e69 100644
--- a/lib/unicorn/app/old_rails.rb
+++ b/lib/unicorn/app/old_rails.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # :enddoc:
 # This code is based on the original Rails handler in Mongrel
diff --git a/lib/unicorn/app/old_rails/static.rb b/lib/unicorn/app/old_rails/static.rb
index 2257270..cf34e02 100644
--- a/lib/unicorn/app/old_rails/static.rb
+++ b/lib/unicorn/app/old_rails/static.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 # This code is based on the original Rails handler in Mongrel
 # Copyright (c) 2005 Zed A. Shaw
diff --git a/lib/unicorn/cgi_wrapper.rb b/lib/unicorn/cgi_wrapper.rb
index d9b7fe5..fb43605 100644
--- a/lib/unicorn/cgi_wrapper.rb
+++ b/lib/unicorn/cgi_wrapper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # :enddoc:
 # This code is based on the original CGIWrapper from Mongrel
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index b21a01d..3c81596 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require 'logger'
 
 # Implements a simple DSL for configuring a unicorn server.
diff --git a/lib/unicorn/const.rb b/lib/unicorn/const.rb
index 33ab4ac..8032863 100644
--- a/lib/unicorn/const.rb
+++ b/lib/unicorn/const.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 module Unicorn::Const # :nodoc:
   # default TCP listen host address (0.0.0.0, all interfaces)
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index ab3bd6e..a48dab7 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 # no stable API here
 require 'unicorn_http'
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index 0ed0ae3..3634165 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 # Writes a Rack response to your client using the HTTP/1.1 specification.
 # You use it by simply doing:
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index ed5bbf1..08fbe40 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # This is the process manager of Unicorn. This manages worker
 # processes which in turn handle the I/O and application process.
diff --git a/lib/unicorn/launcher.rb b/lib/unicorn/launcher.rb
index 78e8f39..bd3324e 100644
--- a/lib/unicorn/launcher.rb
+++ b/lib/unicorn/launcher.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # :enddoc:
 $stdout.sync = $stderr.sync = true
diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb
index db9f2cb..efd9177 100644
--- a/lib/unicorn/oob_gc.rb
+++ b/lib/unicorn/oob_gc.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Strongly consider https://github.com/tmm1/gctools if using Ruby 2.1+
 # It is built on new APIs in Ruby 2.1, so it is more intelligent than
diff --git a/lib/unicorn/preread_input.rb b/lib/unicorn/preread_input.rb
index 12eb3e8..c62cc09 100644
--- a/lib/unicorn/preread_input.rb
+++ b/lib/unicorn/preread_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 module Unicorn
 # This middleware is used to ensure input is buffered to memory
diff --git a/lib/unicorn/select_waiter.rb b/lib/unicorn/select_waiter.rb
index cb84aab..d11ea57 100644
--- a/lib/unicorn/select_waiter.rb
+++ b/lib/unicorn/select_waiter.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # fallback for non-Linux and Linux <4.5 systems w/o EPOLLEXCLUSIVE
 class Unicorn::SelectWaiter # :nodoc:
   def get_readers(ready, readers, timeout) # :nodoc:
diff --git a/lib/unicorn/socket_helper.rb b/lib/unicorn/socket_helper.rb
index 06ec2b2..986932f 100644
--- a/lib/unicorn/socket_helper.rb
+++ b/lib/unicorn/socket_helper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :enddoc:
 require 'socket'
 
diff --git a/lib/unicorn/stream_input.rb b/lib/unicorn/stream_input.rb
index 9246f73..23a9976 100644
--- a/lib/unicorn/stream_input.rb
+++ b/lib/unicorn/stream_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # When processing uploads, unicorn may expose a StreamInput object under
 # "rack.input" of the Rack environment when
diff --git a/lib/unicorn/tee_input.rb b/lib/unicorn/tee_input.rb
index 2ccc2d9..b3c6535 100644
--- a/lib/unicorn/tee_input.rb
+++ b/lib/unicorn/tee_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Acts like tee(1) on an input input to provide a input-like stream
 # while providing rewindable semantics through a File/StringIO backing
diff --git a/lib/unicorn/tmpio.rb b/lib/unicorn/tmpio.rb
index 0bbf6ec..deecd80 100644
--- a/lib/unicorn/tmpio.rb
+++ b/lib/unicorn/tmpio.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # :stopdoc:
 require 'tmpdir'
 
diff --git a/lib/unicorn/util.rb b/lib/unicorn/util.rb
index b826de4..f28d929 100644
--- a/lib/unicorn/util.rb
+++ b/lib/unicorn/util.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'fcntl'
 module Unicorn::Util # :nodoc:
diff --git a/lib/unicorn/worker.rb b/lib/unicorn/worker.rb
index 4af31be..d2445d5 100644
--- a/lib/unicorn/worker.rb
+++ b/lib/unicorn/worker.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 require "raindrops"
 
 # This class and its members can be considered a stable interface
diff --git a/setup.rb b/setup.rb
index cf1abd9..96cf75a 100644
--- a/setup.rb
+++ b/setup.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 #
 # setup.rb
 #
diff --git a/t/broken-app.ru b/t/broken-app.ru
index d05d7ab..5966bff 100644
--- a/t/broken-app.ru
+++ b/t/broken-app.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # we do not want Rack::Lint or anything to protect us
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
diff --git a/t/client_body_buffer_size.ru b/t/client_body_buffer_size.ru
index 44161a5..1a0fb16 100644
--- a/t/client_body_buffer_size.ru
+++ b/t/client_body_buffer_size.ru
@@ -1,4 +1,5 @@
 #\ -E none
+# frozen_string_literal: false
 app = lambda do |env|
   input = env['rack.input']
   case env["PATH_INFO"]
diff --git a/t/detach.ru b/t/detach.ru
index bbd998e..8d35951 100644
--- a/t/detach.ru
+++ b/t/detach.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentType, "text/plain"
 fifo_path = ENV["TEST_FIFO"] or abort "TEST_FIFO not set"
 run lambda { |env|
diff --git a/t/env.ru b/t/env.ru
index 388412e..86c3cfa 100644
--- a/t/env.ru
+++ b/t/env.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] }
diff --git a/t/fails-rack-lint.ru b/t/fails-rack-lint.ru
index 82bfb5f..8b8b5ec 100644
--- a/t/fails-rack-lint.ru
+++ b/t/fails-rack-lint.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This rack app returns an invalid status code, which will cause
 # Rack::Lint to throw an exception if it is present.  This
 # is used to check whether Rack::Lint is in the stack or not.
diff --git a/t/heartbeat-timeout.ru b/t/heartbeat-timeout.ru
index 3eeb5d6..ccc6a8e 100644
--- a/t/heartbeat-timeout.ru
+++ b/t/heartbeat-timeout.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 headers = { 'content-type' => 'text/plain' }
 run lambda { |env|
diff --git a/t/integration.ru b/t/integration.ru
index 888833a..6df481c 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -1,4 +1,5 @@
 #!ruby
+# frozen_string_literal: false
 # Copyright (C) unicorn hackers <unicorn-public@80x24.org>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 
diff --git a/t/listener_names.ru b/t/listener_names.ru
index edb4e6a..f52c59b 100644
--- a/t/listener_names.ru
+++ b/t/listener_names.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 names = Unicorn.listener_names.inspect # rely on preload_app=true
diff --git a/t/oob_gc.ru b/t/oob_gc.ru
index 224cb06..2ae58a8 100644
--- a/t/oob_gc.ru
+++ b/t/oob_gc.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 require 'unicorn/oob_gc'
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
diff --git a/t/oob_gc_path.ru b/t/oob_gc_path.ru
index 7f40601..5358222 100644
--- a/t/oob_gc_path.ru
+++ b/t/oob_gc_path.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 require 'unicorn/oob_gc'
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
diff --git a/t/pid.ru b/t/pid.ru
index f5fd31f..b49b137 100644
--- a/t/pid.ru
+++ b/t/pid.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 run lambda { |env| [ 200, {}, [ "#$$\n" ] ] }
diff --git a/t/preread_input.ru b/t/preread_input.ru
index 18af221..5f68fe9 100644
--- a/t/preread_input.ru
+++ b/t/preread_input.ru
@@ -1,4 +1,5 @@
 #\-E none
+# frozen_string_literal: false
 require 'digest/md5'
 require 'unicorn/preread_input'
 use Unicorn::PrereadInput
diff --git a/t/reopen-logs.ru b/t/reopen-logs.ru
index c39e8f6..488da85 100644
--- a/t/reopen-logs.ru
+++ b/t/reopen-logs.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, "text/plain"
 run lambda { |env|
diff --git a/t/t0013.ru b/t/t0013.ru
index 48a3a34..e425093 100644
--- a/t/t0013.ru
+++ b/t/t0013.ru
@@ -1,4 +1,5 @@
 #\ -E none
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, 'text/plain'
 app = lambda do |env|
diff --git a/t/t0014.ru b/t/t0014.ru
index b0bd2b7..686d214 100644
--- a/t/t0014.ru
+++ b/t/t0014.ru
@@ -1,4 +1,5 @@
 #\ -E none
+# frozen_string_literal: false
 use Rack::ContentLength
 use Rack::ContentType, 'text/plain'
 app = lambda do |env|
diff --git a/t/t0301.ru b/t/t0301.ru
index ce68213..54929b1 100644
--- a/t/t0301.ru
+++ b/t/t0301.ru
@@ -1,4 +1,5 @@
 #\-N --debug
+# frozen_string_literal: false
 run(lambda do |env|
   case env['PATH_INFO']
   when '/vars'
diff --git a/test/aggregate.rb b/test/aggregate.rb
index 5eebbe5..0f32b2f 100755
--- a/test/aggregate.rb
+++ b/test/aggregate.rb
@@ -1,5 +1,6 @@
 #!/usr/bin/ruby -n
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 BEGIN { $tests = $assertions = $failures = $errors = 0 }
 
diff --git a/test/benchmark/dd.ru b/test/benchmark/dd.ru
index 111fa2e..5bd2739 100644
--- a/test/benchmark/dd.ru
+++ b/test/benchmark/dd.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This benchmark is the simplest test of the I/O facilities in
 # unicorn.  It is meant to return a fixed-sized blob to test
 # the performance of things in Unicorn, _NOT_ the app.
diff --git a/test/benchmark/ddstream.ru b/test/benchmark/ddstream.ru
index b14c973..fd40ced 100644
--- a/test/benchmark/ddstream.ru
+++ b/test/benchmark/ddstream.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This app is intended to test large HTTP responses with or without
 # a fully-buffering reverse proxy such as nginx. Without a fully-buffering
 # reverse proxy, unicorn will be unresponsive when client count exceeds
diff --git a/test/benchmark/readinput.ru b/test/benchmark/readinput.ru
index c91bec3..95c0226 100644
--- a/test/benchmark/readinput.ru
+++ b/test/benchmark/readinput.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 # This app is intended to test large HTTP requests with or without
 # a fully-buffering reverse proxy such as nginx. Without a fully-buffering
 # reverse proxy, unicorn will be unresponsive when client count exceeds
diff --git a/test/benchmark/stack.ru b/test/benchmark/stack.ru
index fc9193f..17a565b 100644
--- a/test/benchmark/stack.ru
+++ b/test/benchmark/stack.ru
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 run(lambda { |env|
   body = "#{caller.size}\n"
   h = {
diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb
index 8494452..807f724 100644
--- a/test/exec/test_exec.rb
+++ b/test/exec/test_exec.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 # Don't add to this file, new tests are in Perl 5. See t/README
 FLOCK_PATH = File.expand_path(__FILE__)
 require './test/test_helper'
diff --git a/test/test_helper.rb b/test/test_helper.rb
index d86f83b..0bf3c90 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2005 Zed A. Shaw
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_ccc.rb b/test/unit/test_ccc.rb
index f518230..a0a2bff 100644
--- a/test/unit/test_ccc.rb
+++ b/test/unit/test_ccc.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 require 'socket'
 require 'unicorn'
 require 'io/wait'
diff --git a/test/unit/test_configurator.rb b/test/unit/test_configurator.rb
index 1298f0e..1a89aca 100644
--- a/test/unit/test_configurator.rb
+++ b/test/unit/test_configurator.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'test/unit'
 require 'tempfile'
diff --git a/test/unit/test_droplet.rb b/test/unit/test_droplet.rb
index 81ad82b..4b2d2d0 100644
--- a/test/unit/test_droplet.rb
+++ b/test/unit/test_droplet.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 require 'test/unit'
 require 'unicorn'
 
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index 697af44..adcc84f 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2005 Zed A. Shaw
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_http_parser_ng.rb b/test/unit/test_http_parser_ng.rb
index 425d5ad..fd47246 100644
--- a/test/unit/test_http_parser_ng.rb
+++ b/test/unit/test_http_parser_ng.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require './test/test_helper'
 require 'digest/md5'
diff --git a/test/unit/test_request.rb b/test/unit/test_request.rb
index 53ae944..9d1b350 100644
--- a/test/unit/test_request.rb
+++ b/test/unit/test_request.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2009 Eric Wong
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_server.rb b/test/unit/test_server.rb
index 7ffa48f..5a2252f 100644
--- a/test/unit/test_server.rb
+++ b/test/unit/test_server.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2005 Zed A. Shaw
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_signals.rb b/test/unit/test_signals.rb
index 6c48754..49ff3c7 100644
--- a/test/unit/test_signals.rb
+++ b/test/unit/test_signals.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 # Copyright (c) 2009 Eric Wong
 # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
diff --git a/test/unit/test_socket_helper.rb b/test/unit/test_socket_helper.rb
index a446f06..4363474 100644
--- a/test/unit/test_socket_helper.rb
+++ b/test/unit/test_socket_helper.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require './test/test_helper'
 require 'tempfile'
diff --git a/test/unit/test_stream_input.rb b/test/unit/test_stream_input.rb
index 7986ca7..7ee98e4 100644
--- a/test/unit/test_stream_input.rb
+++ b/test/unit/test_stream_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'test/unit'
 require 'digest/sha1'
diff --git a/test/unit/test_tee_input.rb b/test/unit/test_tee_input.rb
index 607ce87..8f05c77 100644
--- a/test/unit/test_tee_input.rb
+++ b/test/unit/test_tee_input.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require 'test/unit'
 require 'digest/sha1'
diff --git a/test/unit/test_util.rb b/test/unit/test_util.rb
index bc7b233..ce53b86 100644
--- a/test/unit/test_util.rb
+++ b/test/unit/test_util.rb
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 
 require './test/test_helper'
 require 'tempfile'
diff --git a/test/unit/test_waiter.rb b/test/unit/test_waiter.rb
index 0995de2..a20994b 100644
--- a/test/unit/test_waiter.rb
+++ b/test/unit/test_waiter.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
 require 'test/unit'
 require 'unicorn'
 require 'unicorn/select_waiter'
diff --git a/unicorn.gemspec b/unicorn.gemspec
index e7e3ef7..36700a8 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -1,4 +1,5 @@
 # -*- encoding: binary -*-
+# frozen_string_literal: false
 manifest = File.exist?('.manifest') ?
   IO.readlines('.manifest').map!(&:chomp!) : `git ls-files`.split("\n")
 

^ permalink raw reply related	[relevance 1%]

* [PATCH] README: fix wording
  2023-09-16 20:46  0% ` ideal.water4095
@ 2023-09-30 23:54  2%   ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2023-09-30 23:54 UTC (permalink / raw)
  To: ideal.water4095; +Cc: unicorn-public

ideal.water4095@fastmail.com wrote:
> > +to tolerate (and thus encourage) bad code.  It is only designed
> > +to only handle fast clients on low-latency, high-bandwidth connections

<snip>

> Double "only" here.

Thanks, my brain often doubles words :x
Will push the patch below out.  In the future, please trim out
irrelevant parts since it still took me extra time to spot.
Thanks again

------8<-----
Subject: [PATCH] README: fix wording

Reported-by: <ideal.water4095@fastmail.com>
---
 README | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README b/README
index c5c5222..ff14c03 100644
--- a/README
+++ b/README
@@ -3,7 +3,7 @@
 unicorn is an HTTP server for Rack applications that has done
 decades of damage to the entire Ruby ecosystem due to its ability
 to tolerate (and thus encourage) bad code.  It is only designed
-to only handle fast clients on low-latency, high-bandwidth connections
+to handle fast clients on low-latency, high-bandwidth connections
 and take advantage of features in Unix/Unix-like kernels.
 Slow clients must only be served by placing a reverse proxy capable of
 fully buffering both the the request and response in between unicorn

^ permalink raw reply related	[relevance 2%]

* Re: [PATCH] doc: various updates ahead of the release
  2023-09-10 20:14  2% [PATCH] doc: various updates ahead of the release Eric Wong
@ 2023-09-16 20:46  0% ` ideal.water4095
  2023-09-30 23:54  2%   ` [PATCH] README: fix wording Eric Wong
  0 siblings, 1 reply; 200+ results
From: ideal.water4095 @ 2023-09-16 20:46 UTC (permalink / raw)
  To: Eric Wong, unicorn-public

> The damage unicorn has done to the entire Ruby, Rack and Rails
> ecosystems with its ability to tolerate buggy code is
> unforgivable.  Update the documentation to further discourage
> its use and clarify a few wordings noticed along the way.
> ---
>  DESIGN                      |  4 ++++
>  ISSUES                      |  6 +++++-
>  README                      | 38 ++++++++++++++++++++-----------------
>  lib/unicorn/configurator.rb |  7 ++++++-
>  4 files changed, 36 insertions(+), 19 deletions(-)
>
> diff --git a/DESIGN b/DESIGN
> index 46d7923..0bac24f 100644
> --- a/DESIGN
> +++ b/DESIGN
> @@ -1,5 +1,9 @@
>  == Design
> 
> +Unicorn was designed to support poorly-written codebases back in 2008.
> +Its unfortunate popularity has only proliferated the existence of
> +poorly-written code ever since...
> +
>  * Simplicity: Unicorn is a traditional UNIX prefork web server.
>    No threads are used at all, this makes applications easier to debug
>    and fix.  When your application goes awry, a BOFH can just
> diff --git a/ISSUES b/ISSUES
> index 083b1c8..d6c2a7a 100644
> --- a/ISSUES
> +++ b/ISSUES
> @@ -32,6 +32,10 @@ and such.
>  If you don't get a response within a few days, we may have forgotten
>  about it so feel free to ask again.
> 
> +The project does not and will never endorse nor promote commercial
> +services (including support).  The author of unicorn must never be
> +allowed to profit off the damage it's done to the entire Ruby world.
> +
>  == Bugs in related projects
> 
>  unicorn is sometimes affected by bugs in its dependencies.  Bugs
> @@ -65,7 +69,7 @@ There is a kernel.org Bugzilla instance, but it is 
> ignored by most.
> 
>  Likewise for any rare glibc bugs we might encounter, we should Cc:
>  mailto:libc-alpha@sourceware.org
> -Unofficial archives are available at: https://public-inbox.org/libc-alpha/
> +Archives are available at: https://inbox.sourceware.org/libc-alpha/
>  Keep in mind glibc upstream does use Bugzilla for tracking bugs:
>  https://sourceware.org/bugzilla/
> 
> diff --git a/README b/README
> index 5411003..c5c5222 100644
> --- a/README
> +++ b/README
> @@ -1,10 +1,13 @@
>  = unicorn: Rack HTTP server for fast clients and Unix
> 
> -unicorn is an HTTP server for Rack applications designed to only serve
> -fast clients on low-latency, high-bandwidth connections and take
> -advantage of features in Unix/Unix-like kernels.  Slow clients should
> -only be served by placing a reverse proxy capable of fully buffering
> -both the the request and response in between unicorn and slow clients.
> +unicorn is an HTTP server for Rack applications that has done
> +decades of damage to the entire Ruby ecosystem due to its ability
> +to tolerate (and thus encourage) bad code.  It is only designed
> +to only handle fast clients on low-latency, high-bandwidth connections
> +and take advantage of features in Unix/Unix-like kernels.
> +Slow clients must only be served by placing a reverse proxy capable of
> +fully buffering both the the request and response in between unicorn
> +and slow clients.

Double "only" here.

^ permalink raw reply	[relevance 0%]

* [PATCH] doc: various updates ahead of the release
@ 2023-09-10 20:14  2% Eric Wong
  2023-09-16 20:46  0% ` ideal.water4095
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2023-09-10 20:14 UTC (permalink / raw)
  To: unicorn-public

The damage unicorn has done to the entire Ruby, Rack and Rails
ecosystems with its ability to tolerate buggy code is
unforgivable.  Update the documentation to further discourage
its use and clarify a few wordings noticed along the way.
---
 DESIGN                      |  4 ++++
 ISSUES                      |  6 +++++-
 README                      | 38 ++++++++++++++++++++-----------------
 lib/unicorn/configurator.rb |  7 ++++++-
 4 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/DESIGN b/DESIGN
index 46d7923..0bac24f 100644
--- a/DESIGN
+++ b/DESIGN
@@ -1,5 +1,9 @@
 == Design
 
+Unicorn was designed to support poorly-written codebases back in 2008.
+Its unfortunate popularity has only proliferated the existence of
+poorly-written code ever since...
+
 * Simplicity: Unicorn is a traditional UNIX prefork web server.
   No threads are used at all, this makes applications easier to debug
   and fix.  When your application goes awry, a BOFH can just
diff --git a/ISSUES b/ISSUES
index 083b1c8..d6c2a7a 100644
--- a/ISSUES
+++ b/ISSUES
@@ -32,6 +32,10 @@ and such.
 If you don't get a response within a few days, we may have forgotten
 about it so feel free to ask again.
 
+The project does not and will never endorse nor promote commercial
+services (including support).  The author of unicorn must never be
+allowed to profit off the damage it's done to the entire Ruby world.
+
 == Bugs in related projects
 
 unicorn is sometimes affected by bugs in its dependencies.  Bugs
@@ -65,7 +69,7 @@ There is a kernel.org Bugzilla instance, but it is ignored by most.
 
 Likewise for any rare glibc bugs we might encounter, we should Cc:
 mailto:libc-alpha@sourceware.org
-Unofficial archives are available at: https://public-inbox.org/libc-alpha/
+Archives are available at: https://inbox.sourceware.org/libc-alpha/
 Keep in mind glibc upstream does use Bugzilla for tracking bugs:
 https://sourceware.org/bugzilla/
 
diff --git a/README b/README
index 5411003..c5c5222 100644
--- a/README
+++ b/README
@@ -1,10 +1,13 @@
 = unicorn: Rack HTTP server for fast clients and Unix
 
-unicorn is an HTTP server for Rack applications designed to only serve
-fast clients on low-latency, high-bandwidth connections and take
-advantage of features in Unix/Unix-like kernels.  Slow clients should
-only be served by placing a reverse proxy capable of fully buffering
-both the the request and response in between unicorn and slow clients.
+unicorn is an HTTP server for Rack applications that has done
+decades of damage to the entire Ruby ecosystem due to its ability
+to tolerate (and thus encourage) bad code.  It is only designed
+to only handle fast clients on low-latency, high-bandwidth connections
+and take advantage of features in Unix/Unix-like kernels.
+Slow clients must only be served by placing a reverse proxy capable of
+fully buffering both the the request and response in between unicorn
+and slow clients.
 
 == Features
 
@@ -14,8 +17,8 @@ both the the request and response in between unicorn and slow clients.
 
 * Compatible with Ruby 2.0.0 and later.
 
-* Process management: unicorn will reap and restart workers that
-  die from broken apps.  There is no need to manage multiple processes
+* Process management: unicorn reaps and restarts workers that die
+  from broken code.  There is no need to manage multiple processes
   or ports yourself.  unicorn can spawn and manage any number of
   worker processes you choose to scale to your backend.
 
@@ -57,7 +60,7 @@ both the the request and response in between unicorn and slow clients.
 
 == License
 
-unicorn is copyright 2009-2018 by all contributors (see logs in git).
+unicorn is copyright all contributors (see logs in git).
 It is based on Mongrel 1.1.5.
 Mongrel is copyright 2007 Zed A. Shaw and contributors.
 
@@ -79,8 +82,8 @@ You may install it via RubyGems on RubyGems.org:
 You can get the latest source via git from the following locations
 (these versions may not be stable):
 
-  https://yhbt.net/unicorn.git
-  https://repo.or.cz/unicorn.git (mirror)
+  git clone https://yhbt.net/unicorn.git
+  git clone https://repo.or.cz/unicorn.git # mirror
 
 You may browse the code from the web:
 
@@ -118,23 +121,24 @@ supported.  Run `unicorn -h` to see command-line options.
 == Disclaimer
 
 There is NO WARRANTY whatsoever if anything goes wrong, but
-{let us know}[link:ISSUES.html] and we'll try our best to fix it.
+{let us know}[link:ISSUES.html] and maybe someone can fix it.
 
 unicorn is designed to only serve fast clients either on the local host
 or a fast LAN.  See the PHILOSOPHY and DESIGN documents for more details
 regarding this.
 
-Due to its ability to tolerate crashes and isolate clients, unicorn
-is unfortunately known to prolong the existence of bugs in applications
-and libraries which run on top of it.
+The use of unicorn in new deployments is STRONGLY DISCOURAGED due to the
+damage done to the entire Ruby ecosystem.  Its unintentional popularity
+set Ruby back decades in parallelism, concurrency and robustness since
+it prolongs and proliferates the existence of poorly-written code.
 
 == Contact
 
 All feedback (bug reports, user/development dicussion, patches, pull
-requests) go to the mailing list/newsgroup.  See the ISSUES document for
-information on the {mailing list}[mailto:unicorn-public@yhbt.net].
+requests) go to the public mailbox.  See the ISSUES document for
+information on posting to mailto:unicorn-public@yhbt.net
 
-The mailing list is archived at https://yhbt.net/unicorn-public/
+Mirror-able mail archives are at https://yhbt.net/unicorn-public/
 
 Read-only NNTP access is available at:
 nntps://news.public-inbox.org/inbox.comp.lang.ruby.unicorn and
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index ecdf03e..b21a01d 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -216,7 +216,12 @@ def before_exec(*args, &block)
     set_hook(:before_exec, block_given? ? block : args[0], 1)
   end
 
-  # sets the timeout of worker processes to +seconds+.  Workers
+  # Strongly consider using link:/Application_Timeouts.html instead
+  # of this misfeature.  This misfeature has done decades of damage
+  # to Ruby since it demotivates the use of fine-grained timeout
+  # mechanisms.
+  #
+  # Sets the timeout of worker processes to +seconds+.  Workers
   # handling the request/app.call/response cycle taking longer than
   # this time period will be forcibly killed (via SIGKILL).  This
   # timeout is enforced by the master process itself and not subject

^ permalink raw reply related	[relevance 2%]

* [PATCH 00-23/23] start porting tests to Perl5
@ 2023-06-05 10:32  1% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2023-06-05 10:32 UTC (permalink / raw)
  To: unicorn-public

[-- Attachment #1: Type: text/plain, Size: 4179 bytes --]

Still a lot more work to do, but at least socat is no longer a
test dependency.  Perl5 is installed on far more systems than
socat.

Ruby introduces breaking changes every year and I can't trust
tests to work as they were originally intended, anymore.
Perl 5 doesn't have perfect backwards compatibility, either; but
it's the least bad of any widely-installed scripting language.

Note: that 23/23 introduces a subtle bugfix which changes
behavior for systemd users

Patches are attached to reduce load on SMTP servers.
Some more patches to come as I deal with Ruby 3.x deprecation
warnings :<

Eric Wong (23):
  switch unit/test_response.rb to Perl 5 integration test
  support rack 3 multi-value headers
  port t0018-write-on-close.sh to Perl 5
  port t0000-http-basic.sh to Perl 5
  port t0002-parser-error.sh to Perl 5
  t/integration.t: use start_req to simplify test slighly
  port t0011-active-unix-socket.sh to Perl 5
  port t0100-rack-input-tests.sh to Perl 5
  tests: use autodie to simplify error checking
  port t0019-max_header_len.sh to Perl 5
  test_exec: drop sd_listen_fds emulation test
  test_exec: drop test_basic and test_config_ru_alt_path
  tests: check_stderr consistently in Perl 5 tests
  tests: consistent tcp_start and unix_start across Perl 5 tests
  port t9000-preread-input.sh to Perl 5
  port t/t0116-client_body_buffer_size.sh to Perl 5
  tests: get rid of sha1sum.rb and rsha1() sh function
  early_hints supports Rack 3 array headers
  test_server: drop early_hints test
  t/integration.t: switch PUT tests to MD5, reuse buffers
  tests: move test_upload.rb tests to t/integration.t
  drop redundant IO#close_on_exec=false calls
  LISTEN_FDS-inherited sockets are immortal across SIGHUP

 GNUmakefile                                |   7 +-
 lib/unicorn/http_server.rb                 |  12 +-
 t/README                                   |  21 +-
 t/active-unix-socket.t                     | 113 +++++++
 t/bin/content-md5-put                      |  36 ---
 t/bin/sha1sum.rb                           |  17 --
 t/{t0116.ru => client_body_buffer_size.ru} |   2 -
 t/client_body_buffer_size.t                |  82 ++++++
 t/integration.ru                           | 114 +++++++
 t/integration.t                            | 326 +++++++++++++++++++++
 t/lib.perl                                 | 217 ++++++++++++++
 t/preread_input.ru                         |  21 +-
 t/rack-input-tests.ru                      |  21 --
 t/t0000-http-basic.sh                      |  50 ----
 t/t0002-parser-error.sh                    |  94 ------
 t/t0011-active-unix-socket.sh              |  79 -----
 t/t0018-write-on-close.sh                  |  23 --
 t/t0019-max_header_len.sh                  |  49 ----
 t/t0100-rack-input-tests.sh                | 124 --------
 t/t0116-client_body_buffer_size.sh         |  80 -----
 t/t9000-preread-input.sh                   |  48 ---
 t/test-lib.sh                              |   4 -
 t/write-on-close.ru                        |  11 -
 test/exec/test_exec.rb                     |  57 ----
 test/unit/test_response.rb                 | 111 -------
 test/unit/test_server.rb                   |  31 --
 test/unit/test_upload.rb                   | 301 -------------------
 27 files changed, 891 insertions(+), 1160 deletions(-)
 create mode 100644 t/active-unix-socket.t
 delete mode 100755 t/bin/content-md5-put
 delete mode 100755 t/bin/sha1sum.rb
 rename t/{t0116.ru => client_body_buffer_size.ru} (82%)
 create mode 100644 t/client_body_buffer_size.t
 create mode 100644 t/integration.ru
 create mode 100644 t/integration.t
 create mode 100644 t/lib.perl
 delete mode 100644 t/rack-input-tests.ru
 delete mode 100755 t/t0000-http-basic.sh
 delete mode 100755 t/t0002-parser-error.sh
 delete mode 100755 t/t0011-active-unix-socket.sh
 delete mode 100755 t/t0018-write-on-close.sh
 delete mode 100755 t/t0019-max_header_len.sh
 delete mode 100755 t/t0100-rack-input-tests.sh
 delete mode 100755 t/t0116-client_body_buffer_size.sh
 delete mode 100755 t/t9000-preread-input.sh
 delete mode 100644 t/write-on-close.ru
 delete mode 100644 test/unit/test_response.rb
 delete mode 100644 test/unit/test_upload.rb

[-- Attachment #2: 0001-switch-unit-test_response.rb-to-Perl-5-integration-t.patch --]
[-- Type: text/x-diff, Size: 15667 bytes --]

From 086e397abc0126556af24df77a976671294df2ee Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:30 +0000
Subject: [PATCH 01/23] switch unit/test_response.rb to Perl 5 integration test

http_response_write may benefit from API changes for Rack 3
support.

Since there's no benefit I can see from using a unit test,
switch to an integration test to avoid having to maintain the
unit test if our internal http_response_write method changes.

Of course, I can't trust tests written in Ruby since I've had to
put up with a constant stream of incompatibilities over the past
two decades :<   Perl is more widely installed than socat[1], and
nearly all the Perl I wrote 20 years ago still works
unmodified today.

[1] the rarest dependency of the Bourne shell integration tests
---
 GNUmakefile                |   5 +-
 t/README                   |  24 +++--
 t/integration.ru           |  38 ++++++++
 t/integration.t            |  64 +++++++++++++
 t/lib.perl                 | 189 +++++++++++++++++++++++++++++++++++++
 test/unit/test_response.rb | 111 ----------------------
 6 files changed, 313 insertions(+), 118 deletions(-)
 create mode 100644 t/integration.ru
 create mode 100644 t/integration.t
 create mode 100644 t/lib.perl
 delete mode 100644 test/unit/test_response.rb

diff --git a/GNUmakefile b/GNUmakefile
index 0e08ef0..5cca189 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -86,7 +86,7 @@ $(tmp_bin)/%: bin/% | $(tmp_bin)
 bins: $(tmp_bins)
 
 t_log := $(T_log) $(T_n_log)
-test: $(T) $(T_n)
+test: $(T) $(T_n) test-prove
 	@cat $(t_log) | $(MRI) test/aggregate.rb
 	@$(RM) $(t_log)
 
@@ -141,6 +141,9 @@ t/random_blob:
 
 test-integration: $(T_sh)
 
+test-prove:
+	prove -vw
+
 check: test-require test test-integration
 test-all: check
 
diff --git a/t/README b/t/README
index 14de559..8a5243e 100644
--- a/t/README
+++ b/t/README
@@ -5,16 +5,24 @@ TCP ports or Unix domain sockets.  They're all designed to run
 concurrently with other tests to minimize test time, but tests may be
 run independently as well.
 
-We write our tests in Bourne shell because that's what we're
-comfortable writing integration tests with.
+New tests are written in Perl 5 because we need a stable language
+to test real-world behavior and Ruby introduces incompatibilities
+at a far faster rate than Perl 5.  Perl is Ruby's older cousin, so
+it should be easy-to-learn for Rubyists.
+
+Old tests are in Bourne shell, but the socat(1) dependency was probably
+too rare compared to Perl 5.
 
 == Requirements
 
-* {Ruby 2.0.0+}[https://www.ruby-lang.org/en/] (duh!)
+* {Ruby 2.0.0+}[https://www.ruby-lang.org/en/]
+* {Perl 5.14+}[https://www.perl.org/] # your distro should have it
 * {GNU make}[https://www.gnu.org/software/make/]
+
+The following requirements will eventually be dropped.
+
 * {socat}[http://www.dest-unreach.org/socat/]
 * {curl}[https://curl.haxx.se/]
-* standard UNIX shell utilities (Bourne sh, awk, sed, grep, ...)
 
 We do not use bashisms or any non-portable, non-POSIX constructs
 in our shell code.  We use the "pipefail" option if available and
@@ -26,9 +34,13 @@ with {dash}[http://gondor.apana.org.au/~herbert/dash/] and
 
 To run the entire test suite with 8 tests running at once:
 
-  make -j8
+  make -j8 && prove -vw
+
+To run one individual test (Perl5):
+
+  prove -vw t/integration.t
 
-To run one individual test:
+To run one individual test (shell):
 
   make t0000-simple-http.sh
 
diff --git a/t/integration.ru b/t/integration.ru
new file mode 100644
index 0000000..6ef873c
--- /dev/null
+++ b/t/integration.ru
@@ -0,0 +1,38 @@
+#!ruby
+# Copyright (C) unicorn hackers <unicorn-public@80x24.org>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+
+# this goes for t/integration.t  We'll try to put as many tests
+# in here as possible to avoid startup overhead of Ruby.
+
+$orig_rack_200 = nil
+def tweak_status_code
+  $orig_rack_200 = Rack::Utils::HTTP_STATUS_CODES[200]
+  Rack::Utils::HTTP_STATUS_CODES[200] = "HI"
+  [ 200, {}, [] ]
+end
+
+def restore_status_code
+  $orig_rack_200 or return [ 500, {}, [] ]
+  Rack::Utils::HTTP_STATUS_CODES[200] = $orig_rack_200
+  [ 200, {}, [] ]
+end
+
+run(lambda do |env|
+  case env['REQUEST_METHOD']
+  when 'GET'
+    case env['PATH_INFO']
+    when '/rack-2-newline-headers'; [ 200, { 'X-R2' => "a\nb\nc" }, [] ]
+    when '/nil-header-value'; [ 200, { 'X-Nil' => nil }, [] ]
+    when '/unknown-status-pass-through'; [ '666 I AM THE BEAST', {}, [] ]
+    end # case PATH_INFO (GET)
+  when 'POST'
+    case env['PATH_INFO']
+    when '/tweak-status-code'; tweak_status_code
+    when '/restore-status-code'; restore_status_code
+    end # case PATH_INFO (POST)
+    # ...
+  when 'PUT'
+    # ...
+  end # case REQUEST_METHOD
+end) # run
diff --git a/t/integration.t b/t/integration.t
new file mode 100644
index 0000000..5569155
--- /dev/null
+++ b/t/integration.t
@@ -0,0 +1,64 @@
+#!perl -w
+# Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+
+use v5.14; BEGIN { require './t/lib.perl' };
+my $srv = tcp_server();
+my $t0 = time;
+my $ar = unicorn(qw(-E none t/integration.ru), { 3 => $srv });
+
+sub slurp_hdr {
+	my ($c) = @_;
+	local $/ = "\r\n\r\n"; # affects both readline+chomp
+	chomp(my $hdr = readline($c));
+	my ($status, @hdr) = split(/\r\n/, $hdr);
+	diag explain([ $status, \@hdr ]) if $ENV{V};
+	($status, \@hdr);
+}
+
+my ($c, $status, $hdr);
+
+# response header tests
+$c = tcp_connect($srv);
+print $c "GET /rack-2-newline-headers HTTP/1.0\r\n\r\n" or die $!;
+($status, $hdr) = slurp_hdr($c);
+like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
+my $orig_200_status = $status;
+is_deeply([ grep(/^X-R2: /, @$hdr) ],
+	[ 'X-R2: a', 'X-R2: b', 'X-R2: c' ],
+	'rack 2 LF-delimited headers supported') or diag(explain($hdr));
+
+SKIP: { # Date header check
+	my @d = grep(/^Date: /i, @$hdr);
+	is(scalar(@d), 1, 'got one date header') or diag(explain(\@d));
+	eval { require HTTP::Date } or skip "HTTP::Date missing: $@", 1;
+	$d[0] =~ s/^Date: //i or die 'BUG: did not strip date: prefix';
+	my $t = HTTP::Date::str2time($d[0]);
+	ok($t >= $t0 && $t > 0 && $t <= time, 'valid date') or
+		diag(explain([$t, $!, \@d]));
+};
+
+# cf. <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
+$c = tcp_connect($srv);
+print $c "GET /nil-header-value HTTP/1.0\r\n\r\n" or die $!;
+($status, $hdr) = slurp_hdr($c);
+is_deeply([grep(/^X-Nil:/, @$hdr)], ['X-Nil: '],
+	'nil header value accepted for broken apps') or diag(explain($hdr));
+
+if ('TODO: ensure Rack::Utils::HTTP_STATUS_CODES is available') {
+	$c = tcp_connect($srv);
+	print $c "POST /tweak-status-code HTTP/1.0\r\n\r\n" or die $!;
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 200 HI\b!, 'status tweaked');
+
+	$c = tcp_connect($srv);
+	print $c "POST /restore-status-code HTTP/1.0\r\n\r\n" or die $!;
+	($status, $hdr) = slurp_hdr($c);
+	is($status, $orig_200_status, 'original status restored');
+}
+
+
+# ... more stuff here
+undef $ar;
+diag slurp("$tmpdir/err.log") if $ENV{V};
+done_testing;
diff --git a/t/lib.perl b/t/lib.perl
new file mode 100644
index 0000000..dd9c6b7
--- /dev/null
+++ b/t/lib.perl
@@ -0,0 +1,189 @@
+#!perl -w
+# Copyright (C) unicorn hackers <unicorn-public@80x24.org>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+package UnicornTest;
+use v5.14;
+use parent qw(Exporter);
+use Test::More;
+use IO::Socket::INET;
+use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
+use File::Temp 0.19 (); # 0.19 for ->newdir
+our ($tmpdir, $errfh);
+our @EXPORT = qw(unicorn slurp tcp_server tcp_connect unicorn $tmpdir $errfh
+	SEEK_SET);
+
+my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
+$tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
+open($errfh, '>>', "$tmpdir/err.log") or die "open: $!";
+
+sub tcp_server {
+	my %opt = (
+		ReuseAddr => 1,
+		Proto => 'tcp',
+		Type => SOCK_STREAM,
+		Listen => SOMAXCONN,
+		Blocking => 0,
+		@_,
+	);
+	eval {
+		die 'IPv4-only' if $ENV{TEST_IPV4_ONLY};
+		require IO::Socket::INET6;
+		IO::Socket::INET6->new(%opt, LocalAddr => '[::1]')
+	} || eval {
+		die 'IPv6-only' if $ENV{TEST_IPV6_ONLY};
+		IO::Socket::INET->new(%opt, LocalAddr => '127.0.0.1')
+	} || BAIL_OUT "failed to create TCP server: $! ($@)";
+}
+
+sub tcp_host_port {
+	my ($s) = @_;
+	my ($h, $p) = ($s->sockhost, $s->sockport);
+	my $ipv4 = $s->sockdomain == AF_INET;
+	if (wantarray) {
+		$ipv4 ? ($h, $p) : ("[$h]", $p);
+	} else {
+		$ipv4 ? "$h:$p" : "[$h]:$p";
+	}
+}
+
+sub tcp_connect {
+	my ($dest, %opt) = @_;
+	my $addr = tcp_host_port($dest);
+	my $s = ref($dest)->new(
+		Proto => 'tcp',
+		Type => SOCK_STREAM,
+		PeerAddr => $addr,
+		%opt,
+	) or BAIL_OUT "failed to connect to $addr: $!";
+	$s->autoflush(1);
+	$s;
+}
+
+sub slurp {
+	open my $fh, '<', $_[0] or die "open($_[0]): $!";
+	local $/;
+	<$fh>;
+}
+
+sub spawn {
+	my $env = ref($_[0]) eq 'HASH' ? shift : undef;
+	my $opt = ref($_[-1]) eq 'HASH' ? pop : {};
+	my @cmd = @_;
+	my $old = POSIX::SigSet->new;
+	my $set = POSIX::SigSet->new;
+	$set->fillset or die "sigfillset: $!";
+	sigprocmask(SIG_SETMASK, $set, $old) or die "SIG_SETMASK: $!";
+	pipe(my ($r, $w)) or die "pipe: $!";
+	my $pid = fork // die "fork: $!";
+	if ($pid == 0) {
+		close $r;
+		$SIG{__DIE__} = sub {
+			warn(@_);
+			syswrite($w, my $num = $! + 0);
+			_exit(1);
+		};
+
+		# pretend to be systemd (cf. sd_listen_fds(3))
+		my $cfd;
+		for ($cfd = 0; ($cfd < 3) || defined($opt->{$cfd}); $cfd++) {
+			my $io = $opt->{$cfd} // next;
+			my $pfd = fileno($io) // die "fileno($io): $!";
+			if ($pfd == $cfd) {
+				fcntl($io, F_SETFD, 0) // die "F_SETFD: $!";
+			} else {
+				dup2($pfd, $cfd) // die "dup2($pfd, $cfd): $!";
+			}
+		}
+		if (($cfd - 3) > 0) {
+			$env->{LISTEN_PID} = $$;
+			$env->{LISTEN_FDS} = $cfd - 3;
+		}
+
+		if (defined(my $pgid = $opt->{pgid})) {
+			setpgid(0, $pgid) // die "setpgid(0, $pgid): $!";
+		}
+		$SIG{$_} = 'DEFAULT' for grep(!/^__/, keys %SIG);
+		if (defined(my $cd = $opt->{-C})) {
+			chdir $cd // die "chdir($cd): $!";
+		}
+		$old->delset(POSIX::SIGCHLD) or die "sigdelset CHLD: $!";
+		sigprocmask(SIG_SETMASK, $old) or die "SIG_SETMASK: ~CHLD: $!";
+		@ENV{keys %$env} = values(%$env) if $env;
+		exec { $cmd[0] } @cmd;
+		die "exec @cmd: $!";
+	}
+	close $w;
+	sigprocmask(SIG_SETMASK, $old) or die "SIG_SETMASK(old): $!";
+	if (my $cerrnum = do { local $/, <$r> }) {
+		$! = $cerrnum;
+		die "@cmd PID=$pid died: $!";
+	}
+	$pid;
+}
+
+sub which {
+	my ($file) = @_;
+	return $file if index($file, '/') >= 0;
+	for my $p (split(/:/, $ENV{PATH})) {
+		$p .= "/$file";
+		return $p if -x $p;
+	}
+	undef;
+}
+
+# returns an AutoReap object
+sub unicorn {
+	my %env;
+	if (ref($_[0]) eq 'HASH') {
+		my $e = shift;
+		%env = %$e;
+	}
+	my @args = @_;
+	push(@args, {}) if ref($args[-1]) ne 'HASH';
+	$args[-1]->{2} //= $errfh; # stderr default
+
+	state $ruby = which($ENV{RUBY} // 'ruby');
+	state $lib = File::Spec->rel2abs('lib');
+	state $ver = $ENV{TEST_RUBY_VERSION} // `$ruby -e 'print RUBY_VERSION'`;
+	state $eng = $ENV{TEST_RUBY_ENGINE} // `$ruby -e 'print RUBY_ENGINE'`;
+	state $ext = File::Spec->rel2abs("test/$eng-$ver/ext/unicorn_http");
+	state $exe = File::Spec->rel2abs('bin/unicorn');
+	my $pid = spawn(\%env, $ruby, '-I', $lib, '-I', $ext, $exe, @args);
+	UnicornTest::AutoReap->new($pid);
+}
+
+# automatically kill + reap children when this goes out-of-scope
+package UnicornTest::AutoReap;
+use v5.14;
+
+sub new {
+	my (undef, $pid) = @_;
+	bless { pid => $pid, owner => $$ }, __PACKAGE__
+}
+
+sub kill {
+	my ($self, $sig) = @_;
+	CORE::kill($sig // 'TERM', $self->{pid});
+}
+
+sub join {
+	my ($self, $sig) = @_;
+	my $pid = delete $self->{pid} or return;
+	CORE::kill($sig, $pid) if defined $sig;
+	my $ret = waitpid($pid, 0) // die "waitpid($pid): $!";
+	$ret == $pid or die "BUG: waitpid($pid) != $ret";
+}
+
+sub DESTROY {
+	my ($self) = @_;
+	return if $self->{owner} != $$;
+	$self->join('TERM');
+}
+
+package main; # inject ourselves into the t/*.t script
+UnicornTest->import;
+Test::More->import;
+# try to ensure ->DESTROY fires:
+$SIG{TERM} = sub { exit(15 + 128) };
+$SIG{INT} = sub { exit(2 + 128) };
+1;
diff --git a/test/unit/test_response.rb b/test/unit/test_response.rb
deleted file mode 100644
index fbe433f..0000000
--- a/test/unit/test_response.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-# -*- encoding: binary -*-
-
-# Copyright (c) 2005 Zed A. Shaw
-# You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
-# the GPLv2+ (GPLv3+ preferred)
-#
-# Additional work donated by contributors.  See git history
-# for more information.
-
-require './test/test_helper'
-require 'time'
-
-include Unicorn
-
-class ResponseTest < Test::Unit::TestCase
-  include Unicorn::HttpResponse
-
-  def test_httpdate
-    before = Time.now.to_i - 1
-    str = httpdate
-    assert_kind_of(String, str)
-    middle = Time.parse(str).to_i
-    after = Time.now.to_i
-    assert before <= middle
-    assert middle <= after
-  end
-
-  def test_response_headers
-    out = StringIO.new
-    http_response_write(out, 200, {"X-Whatever" => "stuff"}, ["cool"])
-    assert ! out.closed?
-
-    assert out.length > 0, "output didn't have data"
-  end
-
-  # ref: <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
-  def test_response_header_broken_nil
-    out = StringIO.new
-    http_response_write(out, 200, {"Nil" => nil}, %w(hysterical raisin))
-    assert ! out.closed?
-
-    assert_match %r{^Nil: \r\n}sm, out.string, 'nil accepted'
-  end
-
-  def test_response_string_status
-    out = StringIO.new
-    http_response_write(out,'200', {}, [])
-    assert ! out.closed?
-    assert out.length > 0, "output didn't have data"
-  end
-
-  def test_response_200
-    io = StringIO.new
-    http_response_write(io, 200, {}, [])
-    assert ! io.closed?
-    assert io.length > 0, "output didn't have data"
-  end
-
-  def test_response_with_default_reason
-    code = 400
-    io = StringIO.new
-    http_response_write(io, code, {}, [])
-    assert ! io.closed?
-    lines = io.string.split(/\r\n/)
-    assert_match(/.* Bad Request$/, lines.first,
-                 "wrong default reason phrase")
-  end
-
-  def test_rack_multivalue_headers
-    out = StringIO.new
-    http_response_write(out,200, {"X-Whatever" => "stuff\nbleh"}, [])
-    assert ! out.closed?
-    assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string)
-  end
-
-  # Even though Rack explicitly forbids "Status" in the header hash,
-  # some broken clients still rely on it
-  def test_status_header_added
-    out = StringIO.new
-    http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
-    assert ! out.closed?
-  end
-
-  def test_unknown_status_pass_through
-    out = StringIO.new
-    http_response_write(out,"666 I AM THE BEAST", {}, [] )
-    assert ! out.closed?
-    headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
-    assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
-  end
-
-  def test_modified_rack_http_status_codes_late
-    r, w = IO.pipe
-    pid = fork do
-      r.close
-      # Users may want to globally override the status text associated
-      # with an HTTP status code in their app.
-      Rack::Utils::HTTP_STATUS_CODES[200] = "HI"
-      http_response_write(w, 200, {}, [])
-      w.close
-    end
-    w.close
-    assert_equal "HTTP/1.1 200 HI\r\n", r.gets
-    r.read # just drain the pipe
-    pid, status = Process.waitpid2(pid)
-    assert status.success?, status.inspect
-  ensure
-    r.close
-    w.close unless w.closed?
-  end
-end

[-- Attachment #3: 0002-support-rack-3-multi-value-headers.patch --]
[-- Type: text/x-diff, Size: 1710 bytes --]

From ea0559c700fa029044464de4bd572662c10b7273 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:31 +0000
Subject: [PATCH 02/23] support rack 3 multi-value headers

The first step in adding Rack 3 support.  Rack supports
multi-value headers via array rather than newlines.

Tested-by: Martin Posthumus <martin.posthumus@gmail.com>
Link: https://yhbt.net/unicorn-public/7c851d8a-bc57-7df8-3240-2f5ab831c47c@gmail.com/
---
 t/integration.ru | 1 +
 t/integration.t  | 9 +++++++++
 2 files changed, 10 insertions(+)

diff --git a/t/integration.ru b/t/integration.ru
index 6ef873c..5183217 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -23,6 +23,7 @@ def restore_status_code
   when 'GET'
     case env['PATH_INFO']
     when '/rack-2-newline-headers'; [ 200, { 'X-R2' => "a\nb\nc" }, [] ]
+    when '/rack-3-array-headers'; [ 200, { 'x-r3' => %w(a b c) }, [] ]
     when '/nil-header-value'; [ 200, { 'X-Nil' => nil }, [] ]
     when '/unknown-status-pass-through'; [ '666 I AM THE BEAST', {}, [] ]
     end # case PATH_INFO (GET)
diff --git a/t/integration.t b/t/integration.t
index 5569155..e876c71 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -38,6 +38,15 @@ SKIP: { # Date header check
 		diag(explain([$t, $!, \@d]));
 };
 
+
+$c = tcp_connect($srv);
+print $c "GET /rack-3-array-headers HTTP/1.0\r\n\r\n" or die $!;
+($status, $hdr) = slurp_hdr($c);
+is_deeply([ grep(/^x-r3: /, @$hdr) ],
+	[ 'x-r3: a', 'x-r3: b', 'x-r3: c' ],
+	'rack 3 array headers supported') or diag(explain($hdr));
+
+
 # cf. <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
 $c = tcp_connect($srv);
 print $c "GET /nil-header-value HTTP/1.0\r\n\r\n" or die $!;

[-- Attachment #4: 0003-port-t0018-write-on-close.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 4091 bytes --]

From 295a6c616f8840bc04617a377c04c3422aeebddc Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:32 +0000
Subject: [PATCH 03/23] port t0018-write-on-close.sh to Perl 5

This doesn't require restarting, so it's a perfect candidate.
---
 t/integration.ru          | 15 +++++++++++++++
 t/integration.t           | 14 +++++++++++++-
 t/lib.perl                |  2 +-
 t/t0018-write-on-close.sh | 23 -----------------------
 t/write-on-close.ru       | 11 -----------
 5 files changed, 29 insertions(+), 36 deletions(-)
 delete mode 100755 t/t0018-write-on-close.sh
 delete mode 100644 t/write-on-close.ru

diff --git a/t/integration.ru b/t/integration.ru
index 5183217..12f5d48 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -18,6 +18,20 @@ def restore_status_code
   [ 200, {}, [] ]
 end
 
+class WriteOnClose
+  def each(&block)
+    @callback = block
+  end
+
+  def close
+    @callback.call "7\r\nGoodbye\r\n0\r\n\r\n"
+  end
+end
+
+def write_on_close
+  [ 200, { 'transfer-encoding' => 'chunked' }, WriteOnClose.new ]
+end
+
 run(lambda do |env|
   case env['REQUEST_METHOD']
   when 'GET'
@@ -26,6 +40,7 @@ def restore_status_code
     when '/rack-3-array-headers'; [ 200, { 'x-r3' => %w(a b c) }, [] ]
     when '/nil-header-value'; [ 200, { 'X-Nil' => nil }, [] ]
     when '/unknown-status-pass-through'; [ '666 I AM THE BEAST', {}, [] ]
+    when '/write_on_close'; write_on_close
     end # case PATH_INFO (GET)
   when 'POST'
     case env['PATH_INFO']
diff --git a/t/integration.t b/t/integration.t
index e876c71..3ab5c90 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -4,6 +4,7 @@
 
 use v5.14; BEGIN { require './t/lib.perl' };
 my $srv = tcp_server();
+my $host_port = tcp_host_port($srv);
 my $t0 = time;
 my $ar = unicorn(qw(-E none t/integration.ru), { 3 => $srv });
 
@@ -66,8 +67,19 @@ if ('TODO: ensure Rack::Utils::HTTP_STATUS_CODES is available') {
 	is($status, $orig_200_status, 'original status restored');
 }
 
+SKIP: {
+	eval { require HTTP::Tiny } or skip "HTTP::Tiny missing: $@", 1;
+	my $ht = HTTP::Tiny->new;
+	my $res = $ht->get("http://$host_port/write_on_close");
+	is($res->{content}, 'Goodbye', 'write-on-close body read');
+}
 
 # ... more stuff here
 undef $ar;
-diag slurp("$tmpdir/err.log") if $ENV{V};
+my @log = slurp("$tmpdir/err.log");
+diag("@log") if $ENV{V};
+my @err = grep(!/NameError.*Unicorn::Waiter/, grep(/error/i, @log));
+is_deeply(\@err, [], 'no unexpected errors in stderr');
+is_deeply([grep(/SIGKILL/, @log)], [], 'no SIGKILL in stderr');
+
 done_testing;
diff --git a/t/lib.perl b/t/lib.perl
index dd9c6b7..12deaf8 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -10,7 +10,7 @@ use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh);
 our @EXPORT = qw(unicorn slurp tcp_server tcp_connect unicorn $tmpdir $errfh
-	SEEK_SET);
+	SEEK_SET tcp_host_port);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
diff --git a/t/t0018-write-on-close.sh b/t/t0018-write-on-close.sh
deleted file mode 100755
index 3afefea..0000000
--- a/t/t0018-write-on-close.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 4 "write-on-close tests for funky response-bodies"
-
-t_begin "setup and start" && {
-	unicorn_setup
-	unicorn -D -c $unicorn_config write-on-close.ru
-	unicorn_wait_start
-}
-
-t_begin "write-on-close response body succeeds" && {
-	test xGoodbye = x"$(curl -sSf http://$listen/)"
-}
-
-t_begin "killing succeeds" && {
-	kill $unicorn_pid
-}
-
-t_begin "check stderr" && {
-	check_stderr
-}
-
-t_done
diff --git a/t/write-on-close.ru b/t/write-on-close.ru
deleted file mode 100644
index 725c4d6..0000000
--- a/t/write-on-close.ru
+++ /dev/null
@@ -1,11 +0,0 @@
-class WriteOnClose
-  def each(&block)
-    @callback = block
-  end
-
-  def close
-    @callback.call "7\r\nGoodbye\r\n0\r\n\r\n"
-  end
-end
-use Rack::ContentType, "text/plain"
-run(lambda { |_| [ 200, { 'transfer-encoding' => 'chunked' }, WriteOnClose.new ] })

[-- Attachment #5: 0004-port-t0000-http-basic.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 3372 bytes --]

From 1bb4362cee167ac7aeec910d3f52419e391f1e61 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:33 +0000
Subject: [PATCH 04/23] port t0000-http-basic.sh to Perl 5

One more socat dependency down...
---
 t/integration.ru      | 16 ++++++++++++++
 t/integration.t       | 11 ++++++++++
 t/t0000-http-basic.sh | 50 -------------------------------------------
 3 files changed, 27 insertions(+), 50 deletions(-)
 delete mode 100755 t/t0000-http-basic.sh

diff --git a/t/integration.ru b/t/integration.ru
index 12f5d48..c0bef99 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -32,6 +32,21 @@ def write_on_close
   [ 200, { 'transfer-encoding' => 'chunked' }, WriteOnClose.new ]
 end
 
+def env_dump(env)
+  require 'json'
+  h = {}
+  env.each do |k,v|
+    case v
+    when String, Integer, true, false; h[k] = v
+    else
+      case k
+      when 'rack.version', 'rack.after_reply'; h[k] = v
+      end
+    end
+  end
+  h.to_json
+end
+
 run(lambda do |env|
   case env['REQUEST_METHOD']
   when 'GET'
@@ -40,6 +55,7 @@ def write_on_close
     when '/rack-3-array-headers'; [ 200, { 'x-r3' => %w(a b c) }, [] ]
     when '/nil-header-value'; [ 200, { 'X-Nil' => nil }, [] ]
     when '/unknown-status-pass-through'; [ '666 I AM THE BEAST', {}, [] ]
+    when '/env_dump'; [ 200, {}, [ env_dump(env) ] ]
     when '/write_on_close'; write_on_close
     end # case PATH_INFO (GET)
   when 'POST'
diff --git a/t/integration.t b/t/integration.t
index 3ab5c90..ee22e7e 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -47,6 +47,17 @@ is_deeply([ grep(/^x-r3: /, @$hdr) ],
 	[ 'x-r3: a', 'x-r3: b', 'x-r3: c' ],
 	'rack 3 array headers supported') or diag(explain($hdr));
 
+SKIP: {
+	eval { require JSON::PP } or skip "JSON::PP missing: $@", 1;
+	$c = tcp_connect($srv);
+	print $c "GET /env_dump\r\n" or die $!;
+	my $json = do { local $/; readline($c) };
+	unlike($json, qr/^Connection: /smi, 'no connection header for 0.9');
+	unlike($json, qr!\AHTTP/!s, 'no HTTP/1.x prefix for 0.9');
+	my $env = JSON::PP->new->decode($json);
+	is(ref($env), 'HASH', 'JSON decoded body to hashref');
+	is($env->{SERVER_PROTOCOL}, 'HTTP/0.9', 'SERVER_PROTOCOL is 0.9');
+}
 
 # cf. <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
 $c = tcp_connect($srv);
diff --git a/t/t0000-http-basic.sh b/t/t0000-http-basic.sh
deleted file mode 100755
index 8ab58ac..0000000
--- a/t/t0000-http-basic.sh
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 8 "simple HTTP connection tests"
-
-t_begin "setup and start" && {
-	unicorn_setup
-	unicorn -D -c $unicorn_config env.ru
-	unicorn_wait_start
-}
-
-t_begin "single request" && {
-	curl -sSfv http://$listen/
-}
-
-t_begin "check stderr has no errors" && {
-	check_stderr
-}
-
-t_begin "HTTP/0.9 request should not return headers" && {
-	(
-		printf 'GET /\r\n'
-		cat $fifo > $tmp &
-		wait
-		echo ok > $ok
-	) | socat - TCP:$listen > $fifo
-}
-
-t_begin "env.inspect should've put everything on one line" && {
-	test 1 -eq $(count_lines < $tmp)
-}
-
-t_begin "no headers in output" && {
-	if grep ^Connection: $tmp
-	then
-		die "Connection header found in $tmp"
-	elif grep ^HTTP/ $tmp
-	then
-		die "HTTP/ found in $tmp"
-	fi
-}
-
-t_begin "killing succeeds" && {
-	kill $unicorn_pid
-}
-
-t_begin "check stderr has no errors" && {
-	check_stderr
-}
-
-t_done

[-- Attachment #6: 0005-port-t0002-parser-error.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 4875 bytes --]

From 2eb7b1662c291ab535ee5dabf5d96194ca6483d4 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:34 +0000
Subject: [PATCH 05/23] port t0002-parser-error.sh to Perl 5

Another socat dependency down...
---
 t/integration.t         | 33 +++++++++++++++
 t/lib.perl              |  9 +++-
 t/t0002-parser-error.sh | 94 -----------------------------------------
 3 files changed, 41 insertions(+), 95 deletions(-)
 delete mode 100755 t/t0002-parser-error.sh

diff --git a/t/integration.t b/t/integration.t
index ee22e7e..503b7eb 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -85,6 +85,39 @@ SKIP: {
 	is($res->{content}, 'Goodbye', 'write-on-close body read');
 }
 
+if ('bad requests') {
+	$c = start_req($srv, 'GET /env_dump HTTP/1/1');
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 400 \b!, 'got 400 on bad request');
+
+	$c = tcp_connect($srv);
+	print $c 'GET /' or die $!;
+	my $buf = join('', (0..9), 'ab');
+	for (0..1023) { print $c $buf or die $! }
+	print $c " HTTP/1.0\r\n\r\n" or die $!;
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 414 \b!,
+		'414 on REQUEST_PATH > (12 * 1024)');
+
+	$c = tcp_connect($srv);
+	print $c 'GET /hello-world?a' or die $!;
+	$buf = join('', (0..9));
+	for (0..1023) { print $c $buf or die $! }
+	print $c " HTTP/1.0\r\n\r\n" or die $!;
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 414 \b!,
+		'414 on QUERY_STRING > (10 * 1024)');
+
+	$c = tcp_connect($srv);
+	print $c 'GET /hello-world#a' or die $!;
+	$buf = join('', (0..9), 'a'..'f');
+	for (0..63) { print $c $buf or die $! }
+	print $c " HTTP/1.0\r\n\r\n" or die $!;
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 414 \b!, '414 on FRAGMENT > (1024)');
+}
+
+
 # ... more stuff here
 undef $ar;
 my @log = slurp("$tmpdir/err.log");
diff --git a/t/lib.perl b/t/lib.perl
index 12deaf8..7d712b5 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -10,7 +10,7 @@ use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh);
 our @EXPORT = qw(unicorn slurp tcp_server tcp_connect unicorn $tmpdir $errfh
-	SEEK_SET tcp_host_port);
+	SEEK_SET tcp_host_port start_req);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
@@ -59,6 +59,13 @@ sub tcp_connect {
 	$s;
 }
 
+sub start_req {
+	my ($srv, @req) = @_;
+	my $c = tcp_connect($srv);
+	print $c @req, "\r\n\r\n" or die "print: $!";
+	$c;
+}
+
 sub slurp {
 	open my $fh, '<', $_[0] or die "open($_[0]): $!";
 	local $/;
diff --git a/t/t0002-parser-error.sh b/t/t0002-parser-error.sh
deleted file mode 100755
index 9dc1cd2..0000000
--- a/t/t0002-parser-error.sh
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 11 "parser error test"
-
-t_begin "setup and startup" && {
-	unicorn_setup
-	unicorn -D env.ru -c $unicorn_config
-	unicorn_wait_start
-}
-
-t_begin "send a bad request" && {
-	(
-		printf 'GET / HTTP/1/1\r\nHost: example.com\r\n\r\n'
-		cat $fifo > $tmp &
-		wait
-		echo ok > $ok
-	) | socat - TCP:$listen > $fifo
-	test xok = x$(cat $ok)
-}
-
-dbgcat tmp
-
-t_begin "response should be a 400" && {
-	grep -F 'HTTP/1.1 400 Bad Request' $tmp
-}
-
-t_begin "send a huge Request URI (REQUEST_PATH > (12 * 1024))" && {
-	rm -f $tmp
-	cat $fifo > $tmp &
-	(
-		set -e
-		trap 'echo ok > $ok' EXIT
-		printf 'GET /'
-		for i in $(awk </dev/null 'BEGIN{for(i=0;i<1024;i++) print i}')
-		do
-			printf '0123456789ab'
-		done
-		printf ' HTTP/1.1\r\nHost: example.com\r\n\r\n'
-	) | socat - TCP:$listen > $fifo || :
-	test xok = x$(cat $ok)
-	wait
-}
-
-t_begin "response should be a 414 (REQUEST_PATH)" && {
-	grep -F 'HTTP/1.1 414 ' $tmp
-}
-
-t_begin "send a huge Request URI (QUERY_STRING > (10 * 1024))" && {
-	rm -f $tmp
-	cat $fifo > $tmp &
-	(
-		set -e
-		trap 'echo ok > $ok' EXIT
-		printf 'GET /hello-world?a'
-		for i in $(awk </dev/null 'BEGIN{for(i=0;i<1024;i++) print i}')
-		do
-			printf '0123456789'
-		done
-		printf ' HTTP/1.1\r\nHost: example.com\r\n\r\n'
-	) | socat - TCP:$listen > $fifo || :
-	test xok = x$(cat $ok)
-	wait
-}
-
-t_begin "response should be a 414 (QUERY_STRING)" && {
-	grep -F 'HTTP/1.1 414 ' $tmp
-}
-
-t_begin "send a huge Request URI (FRAGMENT > 1024)" && {
-	rm -f $tmp
-	cat $fifo > $tmp &
-	(
-		set -e
-		trap 'echo ok > $ok' EXIT
-		printf 'GET /hello-world#a'
-		for i in $(awk </dev/null 'BEGIN{for(i=0;i<64;i++) print i}')
-		do
-			printf '0123456789abcdef'
-		done
-		printf ' HTTP/1.1\r\nHost: example.com\r\n\r\n'
-	) | socat - TCP:$listen > $fifo || :
-	test xok = x$(cat $ok)
-	wait
-}
-
-t_begin "response should be a 414 (FRAGMENT)" && {
-	grep -F 'HTTP/1.1 414 ' $tmp
-}
-
-t_begin "server stderr should be clean" && check_stderr
-
-t_begin "term signal sent" && kill $unicorn_pid
-
-t_done

[-- Attachment #7: 0006-t-integration.t-use-start_req-to-simplify-test-sligh.patch --]
[-- Type: text/x-diff, Size: 2556 bytes --]

From 0bb06cc0c8c4f5b76514858067bbb2871dda0d6e Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:35 +0000
Subject: [PATCH 06/23] t/integration.t: use start_req to simplify test slighly

Less code is usually better.
---
 t/integration.t | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/t/integration.t b/t/integration.t
index 503b7eb..b7ba1fb 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -20,8 +20,7 @@ sub slurp_hdr {
 my ($c, $status, $hdr);
 
 # response header tests
-$c = tcp_connect($srv);
-print $c "GET /rack-2-newline-headers HTTP/1.0\r\n\r\n" or die $!;
+$c = start_req($srv, 'GET /rack-2-newline-headers HTTP/1.0');
 ($status, $hdr) = slurp_hdr($c);
 like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
 my $orig_200_status = $status;
@@ -40,8 +39,7 @@ SKIP: { # Date header check
 };
 
 
-$c = tcp_connect($srv);
-print $c "GET /rack-3-array-headers HTTP/1.0\r\n\r\n" or die $!;
+$c = start_req($srv, 'GET /rack-3-array-headers HTTP/1.0');
 ($status, $hdr) = slurp_hdr($c);
 is_deeply([ grep(/^x-r3: /, @$hdr) ],
 	[ 'x-r3: a', 'x-r3: b', 'x-r3: c' ],
@@ -49,8 +47,7 @@ is_deeply([ grep(/^x-r3: /, @$hdr) ],
 
 SKIP: {
 	eval { require JSON::PP } or skip "JSON::PP missing: $@", 1;
-	$c = tcp_connect($srv);
-	print $c "GET /env_dump\r\n" or die $!;
+	my $c = start_req($srv, 'GET /env_dump');
 	my $json = do { local $/; readline($c) };
 	unlike($json, qr/^Connection: /smi, 'no connection header for 0.9');
 	unlike($json, qr!\AHTTP/!s, 'no HTTP/1.x prefix for 0.9');
@@ -60,20 +57,17 @@ SKIP: {
 }
 
 # cf. <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
-$c = tcp_connect($srv);
-print $c "GET /nil-header-value HTTP/1.0\r\n\r\n" or die $!;
+$c = start_req($srv, 'GET /nil-header-value HTTP/1.0');
 ($status, $hdr) = slurp_hdr($c);
 is_deeply([grep(/^X-Nil:/, @$hdr)], ['X-Nil: '],
 	'nil header value accepted for broken apps') or diag(explain($hdr));
 
 if ('TODO: ensure Rack::Utils::HTTP_STATUS_CODES is available') {
-	$c = tcp_connect($srv);
-	print $c "POST /tweak-status-code HTTP/1.0\r\n\r\n" or die $!;
+	$c = start_req($srv, 'POST /tweak-status-code HTTP/1.0');
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 200 HI\b!, 'status tweaked');
 
-	$c = tcp_connect($srv);
-	print $c "POST /restore-status-code HTTP/1.0\r\n\r\n" or die $!;
+	$c = start_req($srv, 'POST /restore-status-code HTTP/1.0');
 	($status, $hdr) = slurp_hdr($c);
 	is($status, $orig_200_status, 'original status restored');
 }

[-- Attachment #8: 0007-port-t0011-active-unix-socket.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 6945 bytes --]

From 10c83beaca58df8b92d8228e798559069cd89beb Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:36 +0000
Subject: [PATCH 07/23] port t0011-active-unix-socket.sh to Perl 5

Another socat dependency down...  I've also started turning
FD_CLOEXEC off on a pipe as a mechanism to detect daemonized
process death in tests.
---
 t/active-unix-socket.t        | 117 ++++++++++++++++++++++++++++++++++
 t/integration.ru              |   1 +
 t/t0011-active-unix-socket.sh |  79 -----------------------
 3 files changed, 118 insertions(+), 79 deletions(-)
 create mode 100644 t/active-unix-socket.t
 delete mode 100755 t/t0011-active-unix-socket.sh

diff --git a/t/active-unix-socket.t b/t/active-unix-socket.t
new file mode 100644
index 0000000..6b5c218
--- /dev/null
+++ b/t/active-unix-socket.t
@@ -0,0 +1,117 @@
+#!perl -w
+# Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+
+use v5.14; BEGIN { require './t/lib.perl' };
+use IO::Socket::UNIX;
+my %to_kill;
+END { kill('TERM', values(%to_kill)) if keys %to_kill }
+my $u1 = "$tmpdir/u1.sock";
+my $u2 = "$tmpdir/u2.sock";
+my $unix_req = sub {
+	my $s = IO::Socket::UNIX->new(Peer => shift, Type => SOCK_STREAM);
+	print $s @_, "\r\n\r\n" or die $!;
+	$s;
+};
+{
+	use autodie;
+	open my $fh, '>', "$tmpdir/u1.conf.rb";
+	print $fh <<EOM;
+pid "$tmpdir/u.pid"
+listen "$u1"
+stderr_path "$tmpdir/err1.log"
+EOM
+	close $fh;
+
+	open $fh, '>', "$tmpdir/u2.conf.rb";
+	print $fh <<EOM;
+pid "$tmpdir/u.pid"
+listen "$u2"
+stderr_path "$tmpdir/err2.log"
+EOM
+	close $fh;
+
+	open $fh, '>', "$tmpdir/u3.conf.rb";
+	print $fh <<EOM;
+pid "$tmpdir/u3.pid"
+listen "$u1"
+stderr_path "$tmpdir/err3.log"
+EOM
+	close $fh;
+}
+
+my @uarg = qw(-D -E none t/integration.ru);
+
+# this pipe will be used to notify us when all daemons die:
+pipe(my ($p0, $p1)) or die "pipe: $!";
+fcntl($p1, POSIX::F_SETFD, 0) or die "fcntl: $!"; # clear FD_CLOEXEC
+
+# start the first instance
+unicorn('-c', "$tmpdir/u1.conf.rb", @uarg)->join;
+is($?, 0, 'daemonized 1st process');
+chomp($to_kill{u1} = slurp("$tmpdir/u.pid"));
+like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file');
+
+chomp(my $worker_pid = readline($unix_req->($u1, 'GET /pid')));
+like($worker_pid, qr/\A\d+\z/s, 'captured worker pid');
+ok(kill(0, $worker_pid), 'worker is kill-able');
+
+
+# 2nd process conflicts on PID
+unicorn('-c', "$tmpdir/u2.conf.rb", @uarg)->join;
+isnt($?, 0, 'conflicting PID file fails to start');
+
+chomp(my $pidf = slurp("$tmpdir/u.pid"));
+is($pidf, $to_kill{u1}, 'pid file contents unchanged after start failure');
+
+chomp(my $pid2 = readline($unix_req->($u1, 'GET /pid')));
+is($worker_pid, $pid2, 'worker PID unchanged');
+
+
+# 3rd process conflicts on socket
+unicorn('-c', "$tmpdir/u3.conf.rb", @uarg)->join;
+isnt($?, 0, 'conflicting UNIX socket fails to start');
+
+chomp($pid2 = readline($unix_req->($u1, 'GET /pid')));
+is($worker_pid, $pid2, 'worker PID still unchanged');
+
+chomp($pidf = slurp("$tmpdir/u.pid"));
+is($pidf, $to_kill{u1}, 'pid file contents unchanged after 2nd start failure');
+
+{ # teardown initial process via SIGKILL
+	ok(kill('KILL', delete $to_kill{u1}), 'SIGKILL initial daemon');
+	close $p1;
+	vec(my $rvec = '', fileno($p0), 1) = 1;
+	is(select($rvec, undef, undef, 5), 1, 'timeout for pipe HUP');
+	is(my $undef = <$p0>, undef, 'process closed pipe writer at exit');
+	ok(-f "$tmpdir/u.pid", 'pid file stayed after SIGKILL');
+	ok(-S $u1, 'socket stayed after SIGKILL');
+	is(IO::Socket::UNIX->new(Peer => $u1, Type => SOCK_STREAM), undef,
+		'fail to connect to u1');
+	ok(!kill(0, $worker_pid), 'worker gone after parent dies');
+}
+
+# restart the first instance
+{
+	pipe(($p0, $p1)) or die "pipe: $!";
+	fcntl($p1, POSIX::F_SETFD, 0) or die "fcntl: $!"; # clear FD_CLOEXEC
+	unicorn('-c', "$tmpdir/u1.conf.rb", @uarg)->join;
+	is($?, 0, 'daemonized 1st process');
+	chomp($to_kill{u1} = slurp("$tmpdir/u.pid"));
+	like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file');
+
+	chomp($pid2 = readline($unix_req->($u1, 'GET /pid')));
+	like($pid2, qr/\A\d+\z/, 'worker running');
+
+	ok(kill('TERM', delete $to_kill{u1}), 'SIGTERM restarted daemon');
+	close $p1;
+	vec(my $rvec = '', fileno($p0), 1) = 1;
+	is(select($rvec, undef, undef, 5), 1, 'timeout for pipe HUP');
+	is(my $undef = <$p0>, undef, 'process closed pipe writer at exit');
+	ok(!-f "$tmpdir/u.pid", 'pid file gone after SIGTERM');
+	ok(-S $u1, 'socket stays after SIGTERM');
+}
+
+my @log = slurp("$tmpdir/err.log");
+diag("@log") if $ENV{V};
+done_testing;
diff --git a/t/integration.ru b/t/integration.ru
index c0bef99..21f5449 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -57,6 +57,7 @@ def env_dump(env)
     when '/unknown-status-pass-through'; [ '666 I AM THE BEAST', {}, [] ]
     when '/env_dump'; [ 200, {}, [ env_dump(env) ] ]
     when '/write_on_close'; write_on_close
+    when '/pid'; [ 200, {}, [ "#$$\n" ] ]
     end # case PATH_INFO (GET)
   when 'POST'
     case env['PATH_INFO']
diff --git a/t/t0011-active-unix-socket.sh b/t/t0011-active-unix-socket.sh
deleted file mode 100755
index fae0b6c..0000000
--- a/t/t0011-active-unix-socket.sh
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 11 "existing UNIX domain socket check"
-
-read_pid_unix () {
-	x=$(printf 'GET / HTTP/1.0\r\n\r\n' | \
-	    socat - UNIX:$unix_socket | \
-	    tail -1)
-	test -n "$x"
-	y="$(expr "$x" : '\([0-9][0-9]*\)')"
-	test x"$x" = x"$y"
-	test -n "$y"
-	echo "$y"
-}
-
-t_begin "setup and start" && {
-	rtmpfiles unix_socket unix_config
-	rm -f $unix_socket
-	unicorn_setup
-	grep -v ^listen < $unicorn_config > $unix_config
-	echo "listen '$unix_socket'" >> $unix_config
-	unicorn -D -c $unix_config pid.ru
-	unicorn_wait_start
-	orig_master_pid=$unicorn_pid
-}
-
-t_begin "get pid of worker" && {
-	worker_pid=$(read_pid_unix)
-	t_info "worker_pid=$worker_pid"
-}
-
-t_begin "fails to start with existing pid file" && {
-	rm -f $ok
-	unicorn -D -c $unix_config pid.ru || echo ok > $ok
-	test x"$(cat $ok)" = xok
-}
-
-t_begin "worker pid unchanged" && {
-	test x"$(read_pid_unix)" = x$worker_pid
-	> $r_err
-}
-
-t_begin "fails to start with listening UNIX domain socket bound" && {
-	rm $ok $pid
-	unicorn -D -c $unix_config pid.ru || echo ok > $ok
-	test x"$(cat $ok)" = xok
-	> $r_err
-}
-
-t_begin "worker pid unchanged (again)" && {
-	test x"$(read_pid_unix)" = x$worker_pid
-}
-
-t_begin "nuking the existing Unicorn succeeds" && {
-	kill -9 $unicorn_pid
-	while kill -0 $unicorn_pid
-	do
-		sleep 1
-	done
-	check_stderr
-}
-
-t_begin "succeeds in starting with leftover UNIX domain socket bound" && {
-	test -S $unix_socket
-	unicorn -D -c $unix_config pid.ru
-	unicorn_wait_start
-}
-
-t_begin "worker pid changed" && {
-	test x"$(read_pid_unix)" != x$worker_pid
-}
-
-t_begin "killing succeeds" && {
-	kill $unicorn_pid
-}
-
-t_begin "no errors" && check_stderr
-
-t_done

[-- Attachment #9: 0008-port-t0100-rack-input-tests.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 11722 bytes --]

From b4ed148186295f2d5c8448eab7f2b201615d1e4e Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:37 +0000
Subject: [PATCH 08/23] port t0100-rack-input-tests.sh to Perl 5

Yet another socat dependency gone \o/
---
 t/bin/content-md5-put       |  36 -----------
 t/integration.ru            |  27 +++++++-
 t/integration.t             |  97 +++++++++++++++++++++++++++-
 t/lib.perl                  |   3 +-
 t/rack-input-tests.ru       |  21 ------
 t/t0100-rack-input-tests.sh | 124 ------------------------------------
 6 files changed, 124 insertions(+), 184 deletions(-)
 delete mode 100755 t/bin/content-md5-put
 delete mode 100644 t/rack-input-tests.ru
 delete mode 100755 t/t0100-rack-input-tests.sh

diff --git a/t/bin/content-md5-put b/t/bin/content-md5-put
deleted file mode 100755
index 01da0bb..0000000
--- a/t/bin/content-md5-put
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env ruby
-# -*- encoding: binary -*-
-# simple chunked HTTP PUT request generator (and just that),
-# it reads stdin and writes to stdout so socat can write to a
-# UNIX or TCP socket (or to another filter or file) along with
-# a Content-MD5 trailer.
-require 'digest/md5'
-$stdout.sync = $stderr.sync = true
-$stdout.binmode
-$stdin.binmode
-
-bs = ENV['bs'] ? ENV['bs'].to_i : 4096
-
-if ARGV.grep("--no-headers").empty?
-  $stdout.write(
-      "PUT / HTTP/1.1\r\n" \
-      "Host: example.com\r\n" \
-      "Transfer-Encoding: chunked\r\n" \
-      "Trailer: Content-MD5\r\n" \
-      "\r\n"
-    )
-end
-
-digest = Digest::MD5.new
-if buf = $stdin.readpartial(bs)
-  begin
-    digest.update(buf)
-    $stdout.write("%x\r\n" % [ buf.size ])
-    $stdout.write(buf)
-    $stdout.write("\r\n")
-  end while $stdin.read(bs, buf)
-end
-
-digest = [ digest.digest ].pack('m').strip
-$stdout.write("0\r\n")
-$stdout.write("Content-MD5: #{digest}\r\n\r\n")
diff --git a/t/integration.ru b/t/integration.ru
index 21f5449..98528f6 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -47,6 +47,29 @@ def env_dump(env)
   h.to_json
 end
 
+def rack_input_tests(env)
+  return [ 100, {}, [] ] if /\A100-continue\z/i =~ env['HTTP_EXPECT']
+  cap = 16384
+  require 'digest/sha1'
+  digest = Digest::SHA1.new
+  input = env['rack.input']
+  case env['PATH_INFO']
+  when '/rack_input/size_first'; input.size
+  when '/rack_input/rewind_first'; input.rewind
+  when '/rack_input'; # OK
+  else
+    abort "bad path: #{env['PATH_INFO']}"
+  end
+  if buf = input.read(rand(cap))
+    begin
+      raise "#{buf.size} > #{cap}" if buf.size > cap
+      digest.update(buf)
+    end while input.read(rand(cap), buf)
+  end
+  [ 200, {'content-length' => '40', 'content-type' => 'text/plain'},
+    [ digest.hexdigest ] ]
+end
+
 run(lambda do |env|
   case env['REQUEST_METHOD']
   when 'GET'
@@ -66,6 +89,8 @@ def env_dump(env)
     end # case PATH_INFO (POST)
     # ...
   when 'PUT'
-    # ...
+    case env['PATH_INFO']
+    when %r{\A/rack_input}; rack_input_tests(env)
+    end
   end # case REQUEST_METHOD
 end) # run
diff --git a/t/integration.t b/t/integration.t
index b7ba1fb..8cef561 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -1,13 +1,16 @@
 #!perl -w
 # Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+# this is the main integration test for things which don't require
+# restarting or signals
 
 use v5.14; BEGIN { require './t/lib.perl' };
 my $srv = tcp_server();
 my $host_port = tcp_host_port($srv);
 my $t0 = time;
 my $ar = unicorn(qw(-E none t/integration.ru), { 3 => $srv });
-
+my $curl = which('curl');
+END { diag slurp("$tmpdir/err.log") if $tmpdir };
 sub slurp_hdr {
 	my ($c) = @_;
 	local $/ = "\r\n\r\n"; # affects both readline+chomp
@@ -17,6 +20,48 @@ sub slurp_hdr {
 	($status, \@hdr);
 }
 
+my %PUT = (
+	chunked_md5 => sub {
+		my ($in, $out, $path, %opt) = @_;
+		my $bs = $opt{bs} // 16384;
+		require Digest::MD5;
+		my $dig = Digest::MD5->new;
+		print $out <<EOM;
+PUT $path HTTP/1.1\r
+Transfer-Encoding: chunked\r
+Trailer: Content-MD5\r
+\r
+EOM
+		my ($buf, $r);
+		while (1) {
+			$r = read($in, $buf, $bs) // die "read: $!";
+			last if $r == 0;
+			printf $out "%x\r\n", length($buf);
+			print $out $buf, "\r\n";
+			$dig->add($buf);
+		}
+		print $out "0\r\nContent-MD5: ", $dig->b64digest, "\r\n\r\n";
+	},
+	identity => sub {
+		my ($in, $out, $path, %opt) = @_;
+		my $bs = $opt{bs} // 16384;
+		my $clen = $opt{-s} // -s $in;
+		print $out <<EOM;
+PUT $path HTTP/1.0\r
+Content-Length: $clen\r
+\r
+EOM
+		my ($buf, $r, $len);
+		while ($clen) {
+			$len = $clen > $bs ? $bs : $clen;
+			$r = read($in, $buf, $len) // die "read: $!";
+			die 'premature EOF' if $r == 0;
+			print $out $buf;
+			$clen -= $r;
+		}
+	},
+);
+
 my ($c, $status, $hdr);
 
 # response header tests
@@ -111,6 +156,55 @@ if ('bad requests') {
 	like($status, qr!\AHTTP/1\.[01] 414 \b!, '414 on FRAGMENT > (1024)');
 }
 
+# input tests
+my ($blob_size, $blob_hash);
+SKIP: {
+	open(my $rh, '<', 't/random_blob') or
+		skip "t/random_blob not generated $!", 1;
+	$blob_size = -s $rh;
+	require Digest::SHA;
+	$blob_hash = Digest::SHA->new(1)->addfile($rh)->hexdigest;
+
+	my $ck_hash = sub {
+		my ($sub, $path, %opt) = @_;
+		seek($rh, 0, SEEK_SET) // die "seek: $!";
+		$c = tcp_connect($srv);
+		$c->autoflush(0);
+		$PUT{$sub}->($rh, $c, $path, %opt);
+		$c->flush or die "flush: $!";
+		($status, $hdr) = slurp_hdr($c);
+		is(readline($c), $blob_hash, "$sub $path");
+	};
+	$ck_hash->('identity', '/rack_input', -s => $blob_size);
+	$ck_hash->('chunked_md5', '/rack_input');
+	$ck_hash->('identity', '/rack_input/size_first', -s => $blob_size);
+	$ck_hash->('identity', '/rack_input/rewind_first', -s => $blob_size);
+	$ck_hash->('chunked_md5', '/rack_input/size_first');
+	$ck_hash->('chunked_md5', '/rack_input/rewind_first');
+
+
+	$curl // skip 'no curl found in PATH', 1;
+
+	my ($copt, $cout);
+	my $url = "http://$host_port/rack_input";
+	my $do_curl = sub {
+		my (@arg) = @_;
+		pipe(my $cout, $copt->{1}) or die "pipe: $!";
+		open $copt->{2}, '>', "$tmpdir/curl.err" or die $!;
+		my $cpid = spawn($curl, '-sSf', @arg, $url, $copt);
+		close(delete $copt->{1}) or die "close: $!";
+		is(readline($cout), $blob_hash, "curl @arg response");
+		is(waitpid($cpid, 0), $cpid, "curl @arg exited");
+		is($?, 0, "no error from curl @arg");
+		is(slurp("$tmpdir/curl.err"), '', "no stderr from curl @arg");
+	};
+
+	$do_curl->(qw(-T t/random_blob));
+
+	seek($rh, 0, SEEK_SET) // die "seek: $!";
+	$copt->{0} = $rh;
+	$do_curl->('-T-');
+}
 
 # ... more stuff here
 undef $ar;
@@ -120,4 +214,5 @@ my @err = grep(!/NameError.*Unicorn::Waiter/, grep(/error/i, @log));
 is_deeply(\@err, [], 'no unexpected errors in stderr');
 is_deeply([grep(/SIGKILL/, @log)], [], 'no SIGKILL in stderr');
 
+undef $tmpdir;
 done_testing;
diff --git a/t/lib.perl b/t/lib.perl
index 7d712b5..ae9f197 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -10,7 +10,7 @@ use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh);
 our @EXPORT = qw(unicorn slurp tcp_server tcp_connect unicorn $tmpdir $errfh
-	SEEK_SET tcp_host_port start_req);
+	SEEK_SET tcp_host_port start_req which spawn);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
@@ -193,4 +193,5 @@ Test::More->import;
 # try to ensure ->DESTROY fires:
 $SIG{TERM} = sub { exit(15 + 128) };
 $SIG{INT} = sub { exit(2 + 128) };
+$SIG{PIPE} = sub { exit(13 + 128) };
 1;
diff --git a/t/rack-input-tests.ru b/t/rack-input-tests.ru
deleted file mode 100644
index 5459e85..0000000
--- a/t/rack-input-tests.ru
+++ /dev/null
@@ -1,21 +0,0 @@
-# SHA1 checksum generator
-require 'digest/sha1'
-use Rack::ContentLength
-cap = 16384
-app = lambda do |env|
-  /\A100-continue\z/i =~ env['HTTP_EXPECT'] and
-    return [ 100, {}, [] ]
-  digest = Digest::SHA1.new
-  input = env['rack.input']
-  input.size if env["PATH_INFO"] == "/size_first"
-  input.rewind if env["PATH_INFO"] == "/rewind_first"
-  if buf = input.read(rand(cap))
-    begin
-      raise "#{buf.size} > #{cap}" if buf.size > cap
-      digest.update(buf)
-    end while input.read(rand(cap), buf)
-  end
-
-  [ 200, {'content-type' => 'text/plain'}, [ digest.hexdigest << "\n" ] ]
-end
-run app
diff --git a/t/t0100-rack-input-tests.sh b/t/t0100-rack-input-tests.sh
deleted file mode 100755
index ee7a437..0000000
--- a/t/t0100-rack-input-tests.sh
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-test -r random_blob || die "random_blob required, run with 'make $0'"
-
-t_plan 10 "rack.input read tests"
-
-t_begin "setup and startup" && {
-	rtmpfiles curl_out curl_err
-	unicorn_setup
-	unicorn -E none -D rack-input-tests.ru -c $unicorn_config
-	blob_sha1=$(rsha1 < random_blob)
-	blob_size=$(count_bytes < random_blob)
-	t_info "blob_sha1=$blob_sha1"
-	unicorn_wait_start
-}
-
-t_begin "corked identity request" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		printf 'PUT / HTTP/1.0\r\n'
-		printf 'Content-Length: %d\r\n\r\n' $blob_size
-		cat random_blob
-		wait
-		echo ok > $ok
-	) | ( sleep 1 && socat - TCP4:$listen > $fifo )
-	test 1 -eq $(grep $blob_sha1 $tmp |count_lines)
-	test x"$(cat $ok)" = xok
-}
-
-t_begin "corked chunked request" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		content-md5-put < random_blob
-		wait
-		echo ok > $ok
-	) | ( sleep 1 && socat - TCP4:$listen > $fifo )
-	test 1 -eq $(grep $blob_sha1 $tmp |count_lines)
-	test x"$(cat $ok)" = xok
-}
-
-t_begin "corked identity request (input#size first)" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		printf 'PUT /size_first HTTP/1.0\r\n'
-		printf 'Content-Length: %d\r\n\r\n' $blob_size
-		cat random_blob
-		wait
-		echo ok > $ok
-	) | ( sleep 1 && socat - TCP4:$listen > $fifo )
-	test 1 -eq $(grep $blob_sha1 $tmp |count_lines)
-	test x"$(cat $ok)" = xok
-}
-
-t_begin "corked identity request (input#rewind first)" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		printf 'PUT /rewind_first HTTP/1.0\r\n'
-		printf 'Content-Length: %d\r\n\r\n' $blob_size
-		cat random_blob
-		wait
-		echo ok > $ok
-	) | ( sleep 1 && socat - TCP4:$listen > $fifo )
-	test 1 -eq $(grep $blob_sha1 $tmp |count_lines)
-	test x"$(cat $ok)" = xok
-}
-
-t_begin "corked chunked request (input#size first)" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		printf 'PUT /size_first HTTP/1.1\r\n'
-		printf 'Host: example.com\r\n'
-		printf 'Transfer-Encoding: chunked\r\n'
-		printf 'Trailer: Content-MD5\r\n'
-		printf '\r\n'
-		content-md5-put --no-headers < random_blob
-		wait
-		echo ok > $ok
-	) | ( sleep 1 && socat - TCP4:$listen > $fifo )
-	test 1 -eq $(grep $blob_sha1 $tmp |count_lines)
-	test 1 -eq $(grep $blob_sha1 $tmp |count_lines)
-	test x"$(cat $ok)" = xok
-}
-
-t_begin "corked chunked request (input#rewind first)" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		printf 'PUT /rewind_first HTTP/1.1\r\n'
-		printf 'Host: example.com\r\n'
-		printf 'Transfer-Encoding: chunked\r\n'
-		printf 'Trailer: Content-MD5\r\n'
-		printf '\r\n'
-		content-md5-put --no-headers < random_blob
-		wait
-		echo ok > $ok
-	) | ( sleep 1 && socat - TCP4:$listen > $fifo )
-	test 1 -eq $(grep $blob_sha1 $tmp |count_lines)
-	test x"$(cat $ok)" = xok
-}
-
-t_begin "regular request" && {
-	curl -sSf -T random_blob http://$listen/ > $curl_out 2> $curl_err
-        test x$blob_sha1 = x$(cat $curl_out)
-        test ! -s $curl_err
-}
-
-t_begin "chunked request" && {
-	curl -sSf -T- < random_blob http://$listen/ > $curl_out 2> $curl_err
-        test x$blob_sha1 = x$(cat $curl_out)
-        test ! -s $curl_err
-}
-
-dbgcat r_err
-
-t_begin "shutdown" && {
-	kill $unicorn_pid
-}
-
-t_done

[-- Attachment #10: 0009-tests-use-autodie-to-simplify-error-checking.patch --]
[-- Type: text/x-diff, Size: 8495 bytes --]

From 3a1d015a3859b639d8e4463e9436a49f4f0f720e Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:38 +0000
Subject: [PATCH 09/23] tests: use autodie to simplify error checking

autodie is bundled with Perl 5.10+ and simplifies error
checking in most cases.  Some subroutines aren't perfectly
translatable and their call sites had to be tweaked, but
most of them are.
---
 t/active-unix-socket.t | 13 +++++++------
 t/integration.t        | 37 +++++++++++++++++++------------------
 t/lib.perl             | 30 +++++++++++++++---------------
 3 files changed, 41 insertions(+), 39 deletions(-)

diff --git a/t/active-unix-socket.t b/t/active-unix-socket.t
index 6b5c218..1241904 100644
--- a/t/active-unix-socket.t
+++ b/t/active-unix-socket.t
@@ -4,17 +4,18 @@
 
 use v5.14; BEGIN { require './t/lib.perl' };
 use IO::Socket::UNIX;
+use autodie;
+no autodie 'kill';
 my %to_kill;
 END { kill('TERM', values(%to_kill)) if keys %to_kill }
 my $u1 = "$tmpdir/u1.sock";
 my $u2 = "$tmpdir/u2.sock";
 my $unix_req = sub {
 	my $s = IO::Socket::UNIX->new(Peer => shift, Type => SOCK_STREAM);
-	print $s @_, "\r\n\r\n" or die $!;
+	print $s @_, "\r\n\r\n";
 	$s;
 };
 {
-	use autodie;
 	open my $fh, '>', "$tmpdir/u1.conf.rb";
 	print $fh <<EOM;
 pid "$tmpdir/u.pid"
@@ -43,8 +44,8 @@ EOM
 my @uarg = qw(-D -E none t/integration.ru);
 
 # this pipe will be used to notify us when all daemons die:
-pipe(my ($p0, $p1)) or die "pipe: $!";
-fcntl($p1, POSIX::F_SETFD, 0) or die "fcntl: $!"; # clear FD_CLOEXEC
+pipe(my $p0, my $p1);
+fcntl($p1, POSIX::F_SETFD, 0);
 
 # start the first instance
 unicorn('-c', "$tmpdir/u1.conf.rb", @uarg)->join;
@@ -93,8 +94,8 @@ is($pidf, $to_kill{u1}, 'pid file contents unchanged after 2nd start failure');
 
 # restart the first instance
 {
-	pipe(($p0, $p1)) or die "pipe: $!";
-	fcntl($p1, POSIX::F_SETFD, 0) or die "fcntl: $!"; # clear FD_CLOEXEC
+	pipe($p0, $p1);
+	fcntl($p1, POSIX::F_SETFD, 0);
 	unicorn('-c', "$tmpdir/u1.conf.rb", @uarg)->join;
 	is($?, 0, 'daemonized 1st process');
 	chomp($to_kill{u1} = slurp("$tmpdir/u.pid"));
diff --git a/t/integration.t b/t/integration.t
index 8cef561..af17d51 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -5,6 +5,7 @@
 # restarting or signals
 
 use v5.14; BEGIN { require './t/lib.perl' };
+use autodie;
 my $srv = tcp_server();
 my $host_port = tcp_host_port($srv);
 my $t0 = time;
@@ -34,7 +35,7 @@ Trailer: Content-MD5\r
 EOM
 		my ($buf, $r);
 		while (1) {
-			$r = read($in, $buf, $bs) // die "read: $!";
+			$r = read($in, $buf, $bs);
 			last if $r == 0;
 			printf $out "%x\r\n", length($buf);
 			print $out $buf, "\r\n";
@@ -54,7 +55,7 @@ EOM
 		my ($buf, $r, $len);
 		while ($clen) {
 			$len = $clen > $bs ? $bs : $clen;
-			$r = read($in, $buf, $len) // die "read: $!";
+			$r = read($in, $buf, $len);
 			die 'premature EOF' if $r == 0;
 			print $out $buf;
 			$clen -= $r;
@@ -130,28 +131,28 @@ if ('bad requests') {
 	like($status, qr!\AHTTP/1\.[01] 400 \b!, 'got 400 on bad request');
 
 	$c = tcp_connect($srv);
-	print $c 'GET /' or die $!;
+	print $c 'GET /';
 	my $buf = join('', (0..9), 'ab');
-	for (0..1023) { print $c $buf or die $! }
-	print $c " HTTP/1.0\r\n\r\n" or die $!;
+	for (0..1023) { print $c $buf }
+	print $c " HTTP/1.0\r\n\r\n";
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 414 \b!,
 		'414 on REQUEST_PATH > (12 * 1024)');
 
 	$c = tcp_connect($srv);
-	print $c 'GET /hello-world?a' or die $!;
+	print $c 'GET /hello-world?a';
 	$buf = join('', (0..9));
-	for (0..1023) { print $c $buf or die $! }
-	print $c " HTTP/1.0\r\n\r\n" or die $!;
+	for (0..1023) { print $c $buf }
+	print $c " HTTP/1.0\r\n\r\n";
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 414 \b!,
 		'414 on QUERY_STRING > (10 * 1024)');
 
 	$c = tcp_connect($srv);
-	print $c 'GET /hello-world#a' or die $!;
+	print $c 'GET /hello-world#a';
 	$buf = join('', (0..9), 'a'..'f');
-	for (0..63) { print $c $buf or die $! }
-	print $c " HTTP/1.0\r\n\r\n" or die $!;
+	for (0..63) { print $c $buf }
+	print $c " HTTP/1.0\r\n\r\n";
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 414 \b!, '414 on FRAGMENT > (1024)');
 }
@@ -159,7 +160,7 @@ if ('bad requests') {
 # input tests
 my ($blob_size, $blob_hash);
 SKIP: {
-	open(my $rh, '<', 't/random_blob') or
+	CORE::open(my $rh, '<', 't/random_blob') or
 		skip "t/random_blob not generated $!", 1;
 	$blob_size = -s $rh;
 	require Digest::SHA;
@@ -167,11 +168,11 @@ SKIP: {
 
 	my $ck_hash = sub {
 		my ($sub, $path, %opt) = @_;
-		seek($rh, 0, SEEK_SET) // die "seek: $!";
+		seek($rh, 0, SEEK_SET);
 		$c = tcp_connect($srv);
 		$c->autoflush(0);
 		$PUT{$sub}->($rh, $c, $path, %opt);
-		$c->flush or die "flush: $!";
+		$c->flush or die $!;
 		($status, $hdr) = slurp_hdr($c);
 		is(readline($c), $blob_hash, "$sub $path");
 	};
@@ -189,10 +190,10 @@ SKIP: {
 	my $url = "http://$host_port/rack_input";
 	my $do_curl = sub {
 		my (@arg) = @_;
-		pipe(my $cout, $copt->{1}) or die "pipe: $!";
-		open $copt->{2}, '>', "$tmpdir/curl.err" or die $!;
+		pipe(my $cout, $copt->{1});
+		open $copt->{2}, '>', "$tmpdir/curl.err";
 		my $cpid = spawn($curl, '-sSf', @arg, $url, $copt);
-		close(delete $copt->{1}) or die "close: $!";
+		close(delete $copt->{1});
 		is(readline($cout), $blob_hash, "curl @arg response");
 		is(waitpid($cpid, 0), $cpid, "curl @arg exited");
 		is($?, 0, "no error from curl @arg");
@@ -201,7 +202,7 @@ SKIP: {
 
 	$do_curl->(qw(-T t/random_blob));
 
-	seek($rh, 0, SEEK_SET) // die "seek: $!";
+	seek($rh, 0, SEEK_SET);
 	$copt->{0} = $rh;
 	$do_curl->('-T-');
 }
diff --git a/t/lib.perl b/t/lib.perl
index ae9f197..49632cf 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -4,6 +4,7 @@
 package UnicornTest;
 use v5.14;
 use parent qw(Exporter);
+use autodie;
 use Test::More;
 use IO::Socket::INET;
 use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
@@ -14,7 +15,7 @@ our @EXPORT = qw(unicorn slurp tcp_server tcp_connect unicorn $tmpdir $errfh
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
-open($errfh, '>>', "$tmpdir/err.log") or die "open: $!";
+open($errfh, '>>', "$tmpdir/err.log");
 
 sub tcp_server {
 	my %opt = (
@@ -62,14 +63,14 @@ sub tcp_connect {
 sub start_req {
 	my ($srv, @req) = @_;
 	my $c = tcp_connect($srv);
-	print $c @req, "\r\n\r\n" or die "print: $!";
+	print $c @req, "\r\n\r\n";
 	$c;
 }
 
 sub slurp {
-	open my $fh, '<', $_[0] or die "open($_[0]): $!";
+	open my $fh, '<', $_[0];
 	local $/;
-	<$fh>;
+	readline($fh);
 }
 
 sub spawn {
@@ -80,8 +81,8 @@ sub spawn {
 	my $set = POSIX::SigSet->new;
 	$set->fillset or die "sigfillset: $!";
 	sigprocmask(SIG_SETMASK, $set, $old) or die "SIG_SETMASK: $!";
-	pipe(my ($r, $w)) or die "pipe: $!";
-	my $pid = fork // die "fork: $!";
+	pipe(my $r, my $w);
+	my $pid = fork;
 	if ($pid == 0) {
 		close $r;
 		$SIG{__DIE__} = sub {
@@ -94,9 +95,9 @@ sub spawn {
 		my $cfd;
 		for ($cfd = 0; ($cfd < 3) || defined($opt->{$cfd}); $cfd++) {
 			my $io = $opt->{$cfd} // next;
-			my $pfd = fileno($io) // die "fileno($io): $!";
+			my $pfd = fileno($io);
 			if ($pfd == $cfd) {
-				fcntl($io, F_SETFD, 0) // die "F_SETFD: $!";
+				fcntl($io, F_SETFD, 0);
 			} else {
 				dup2($pfd, $cfd) // die "dup2($pfd, $cfd): $!";
 			}
@@ -110,9 +111,7 @@ sub spawn {
 			setpgid(0, $pgid) // die "setpgid(0, $pgid): $!";
 		}
 		$SIG{$_} = 'DEFAULT' for grep(!/^__/, keys %SIG);
-		if (defined(my $cd = $opt->{-C})) {
-			chdir $cd // die "chdir($cd): $!";
-		}
+		if (defined(my $cd = $opt->{-C})) { chdir $cd }
 		$old->delset(POSIX::SIGCHLD) or die "sigdelset CHLD: $!";
 		sigprocmask(SIG_SETMASK, $old) or die "SIG_SETMASK: ~CHLD: $!";
 		@ENV{keys %$env} = values(%$env) if $env;
@@ -162,22 +161,23 @@ sub unicorn {
 # automatically kill + reap children when this goes out-of-scope
 package UnicornTest::AutoReap;
 use v5.14;
+use autodie;
 
 sub new {
 	my (undef, $pid) = @_;
 	bless { pid => $pid, owner => $$ }, __PACKAGE__
 }
 
-sub kill {
+sub do_kill {
 	my ($self, $sig) = @_;
-	CORE::kill($sig // 'TERM', $self->{pid});
+	kill($sig // 'TERM', $self->{pid});
 }
 
 sub join {
 	my ($self, $sig) = @_;
 	my $pid = delete $self->{pid} or return;
-	CORE::kill($sig, $pid) if defined $sig;
-	my $ret = waitpid($pid, 0) // die "waitpid($pid): $!";
+	kill($sig, $pid) if defined $sig;
+	my $ret = waitpid($pid, 0);
 	$ret == $pid or die "BUG: waitpid($pid) != $ret";
 }
 

[-- Attachment #11: 0010-port-t0019-max_header_len.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 5571 bytes --]

From 43c7d73b8b9e6995b5a986b10a8623395e89a538 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:39 +0000
Subject: [PATCH 10/23] port t0019-max_header_len.sh to Perl 5

This was the final socat requirement for integration tests.
I think curl will remain an optional dependency for tests
since it's probably the most widely-installed HTTP client.
---
 GNUmakefile               |  2 +-
 t/README                  |  7 +-----
 t/integration.ru          |  1 +
 t/integration.t           | 43 +++++++++++++++++++++++++++++++---
 t/t0019-max_header_len.sh | 49 ---------------------------------------
 5 files changed, 43 insertions(+), 59 deletions(-)
 delete mode 100755 t/t0019-max_header_len.sh

diff --git a/GNUmakefile b/GNUmakefile
index 5cca189..eab9082 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -125,7 +125,7 @@ $(T_sh): dep $(test_prereq) t/random_blob t/trash/.gitignore
 t/trash/.gitignore : | t/trash
 	echo '*' >$@
 
-dependencies := socat curl
+dependencies := curl
 deps := $(addprefix t/.dep+,$(dependencies))
 $(deps): dep_bin = $(lastword $(subst +, ,$@))
 $(deps):
diff --git a/t/README b/t/README
index 8a5243e..d09c715 100644
--- a/t/README
+++ b/t/README
@@ -10,18 +10,13 @@ to test real-world behavior and Ruby introduces incompatibilities
 at a far faster rate than Perl 5.  Perl is Ruby's older cousin, so
 it should be easy-to-learn for Rubyists.
 
-Old tests are in Bourne shell, but the socat(1) dependency was probably
-too rare compared to Perl 5.
+Old tests are in Bourne shell and slowly being ported to Perl 5.
 
 == Requirements
 
 * {Ruby 2.0.0+}[https://www.ruby-lang.org/en/]
 * {Perl 5.14+}[https://www.perl.org/] # your distro should have it
 * {GNU make}[https://www.gnu.org/software/make/]
-
-The following requirements will eventually be dropped.
-
-* {socat}[http://www.dest-unreach.org/socat/]
 * {curl}[https://curl.haxx.se/]
 
 We do not use bashisms or any non-portable, non-POSIX constructs
diff --git a/t/integration.ru b/t/integration.ru
index 98528f6..edc408c 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -81,6 +81,7 @@ def rack_input_tests(env)
     when '/env_dump'; [ 200, {}, [ env_dump(env) ] ]
     when '/write_on_close'; write_on_close
     when '/pid'; [ 200, {}, [ "#$$\n" ] ]
+    else '/'; [ 200, {}, [ env_dump(env) ] ]
     end # case PATH_INFO (GET)
   when 'POST'
     case env['PATH_INFO']
diff --git a/t/integration.t b/t/integration.t
index af17d51..c687655 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -1,15 +1,19 @@
 #!perl -w
 # Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
-# this is the main integration test for things which don't require
-# restarting or signals
+
+# This is the main integration test for fast-ish things to minimize
+# Ruby startup time penalties.
 
 use v5.14; BEGIN { require './t/lib.perl' };
 use autodie;
 my $srv = tcp_server();
 my $host_port = tcp_host_port($srv);
 my $t0 = time;
-my $ar = unicorn(qw(-E none t/integration.ru), { 3 => $srv });
+my $conf = "$tmpdir/u.conf.rb";
+open my $conf_fh, '>', $conf;
+$conf_fh->autoflush(1);
+my $ar = unicorn(qw(-E none t/integration.ru -c), $conf, { 3 => $srv });
 my $curl = which('curl');
 END { diag slurp("$tmpdir/err.log") if $tmpdir };
 sub slurp_hdr {
@@ -207,7 +211,40 @@ SKIP: {
 	$do_curl->('-T-');
 }
 
+
 # ... more stuff here
+
+# SIGHUP-able stuff goes here
+
+if ('max_header_len internal API') {
+	undef $c;
+	my $req = 'GET / HTTP/1.0';
+	my $len = length($req."\r\n\r\n");
+	my $fifo = "$tmpdir/fifo";
+	POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
+	print $conf_fh <<EOM;
+Unicorn::HttpParser.max_header_len = $len
+listen "$host_port" # TODO: remove this requirement for SIGHUP
+after_fork { |_,_| File.open('$fifo', 'w') { |fp| fp.write "pid=#\$\$" } }
+EOM
+	$ar->do_kill('HUP');
+	open my $fifo_fh, '<', $fifo;
+	my $wpid = readline($fifo_fh);
+	like($wpid, qr/\Apid=\d+\z/a , 'new worker ready');
+	close $fifo_fh;
+	$wpid =~ s/\Apid=// or die;
+	ok(CORE::kill(0, $wpid), 'worker PID retrieved');
+
+	$c = start_req($srv, $req);
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 200\b!, 'minimal request succeeds');
+
+	$c = start_req($srv, 'GET /xxxxxx HTTP/1.0');
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 413\b!, 'big request fails');
+}
+
+
 undef $ar;
 my @log = slurp("$tmpdir/err.log");
 diag("@log") if $ENV{V};
diff --git a/t/t0019-max_header_len.sh b/t/t0019-max_header_len.sh
deleted file mode 100755
index 6a355b4..0000000
--- a/t/t0019-max_header_len.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 5 "max_header_len setting (only intended for Rainbows!)"
-
-t_begin "setup and start" && {
-	unicorn_setup
-	req='GET / HTTP/1.0\r\n\r\n'
-	len=$(printf "$req" | count_bytes)
-	echo Unicorn::HttpParser.max_header_len = $len >> $unicorn_config
-	unicorn -D -c $unicorn_config env.ru
-	unicorn_wait_start
-}
-
-t_begin "minimal request succeeds" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		printf "$req"
-		wait
-		echo ok > $ok
-	) | socat - TCP:$listen > $fifo
-	test xok = x$(cat $ok)
-
-	fgrep "HTTP/1.1 200 OK" $tmp
-}
-
-t_begin "big request fails" && {
-	rm -f $tmp
-	(
-		cat $fifo > $tmp &
-		printf 'GET /xxxxxx HTTP/1.0\r\n\r\n'
-		wait
-		echo ok > $ok
-	) | socat - TCP:$listen > $fifo
-	test xok = x$(cat $ok)
-	fgrep "HTTP/1.1 413" $tmp
-}
-
-dbgcat tmp
-
-t_begin "killing succeeds" && {
-	kill $unicorn_pid
-}
-
-t_begin "check stderr" && {
-	check_stderr
-}
-
-t_done

[-- Attachment #12: 0011-test_exec-drop-sd_listen_fds-emulation-test.patch --]
[-- Type: text/x-diff, Size: 1751 bytes --]

From 5d828a4ef7683345bcf2ff659442fed0a6fb7a97 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:40 +0000
Subject: [PATCH 11/23] test_exec: drop sd_listen_fds emulation test

The Perl 5 tests already rely on this implicitly, and there was
never a point when Perl 5 couldn't emulate systemd behavior.
---
 test/exec/test_exec.rb | 33 ---------------------------------
 1 file changed, 33 deletions(-)

diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb
index 2929b2e..1d3a0fd 100644
--- a/test/exec/test_exec.rb
+++ b/test/exec/test_exec.rb
@@ -97,39 +97,6 @@ def teardown
     end
   end
 
-  def test_sd_listen_fds_emulation
-    # [ruby-core:69895] [Bug #11336] fixed by r51576
-    return if RUBY_VERSION.to_f < 2.3
-
-    File.open("config.ru", "wb") { |fp| fp.write(HI) }
-    sock = TCPServer.new(@addr, @port)
-
-    [ %W(-l #@addr:#@port), nil ].each do |l|
-      sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 0)
-
-      pid = xfork do
-        redirect_test_io do
-          # pretend to be systemd
-          ENV['LISTEN_PID'] = "#$$"
-          ENV['LISTEN_FDS'] = '1'
-
-          # 3 = SD_LISTEN_FDS_START
-          args = [ $unicorn_bin ]
-          args.concat(l) if l
-          args << { 3 => sock }
-          exec(*args)
-        end
-      end
-      res = hit(["http://#@addr:#@port/"])
-      assert_equal [ "HI\n" ], res
-      assert_shutdown(pid)
-      assert sock.getsockopt(:SOL_SOCKET, :SO_KEEPALIVE).bool,
-                  'unicorn should always set SO_KEEPALIVE on inherited sockets'
-    end
-  ensure
-    sock.close if sock
-  end
-
   def test_inherit_listener_unspecified
     File.open("config.ru", "wb") { |fp| fp.write(HI) }
     sock = TCPServer.new(@addr, @port)

[-- Attachment #13: 0012-test_exec-drop-test_basic-and-test_config_ru_alt_pat.patch --]
[-- Type: text/x-diff, Size: 1667 bytes --]

From 548593c6b3d52a4bebd52542ad9c423ed2b7252d Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:41 +0000
Subject: [PATCH 12/23] test_exec: drop test_basic and test_config_ru_alt_path

We already have coverage for these basic things elsewhere.
---
 test/exec/test_exec.rb | 24 ------------------------
 1 file changed, 24 deletions(-)

diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb
index 1d3a0fd..55f828e 100644
--- a/test/exec/test_exec.rb
+++ b/test/exec/test_exec.rb
@@ -265,16 +265,6 @@ def test_exit_signals
     end
   end
 
-  def test_basic
-    File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
-    pid = fork do
-      redirect_test_io { exec($unicorn_bin, "-l", "#{@addr}:#{@port}") }
-    end
-    results = retry_hit(["http://#{@addr}:#{@port}/"])
-    assert_equal String, results[0].class
-    assert_shutdown(pid)
-  end
-
   def test_rack_env_unset
     File.open("config.ru", "wb") { |fp| fp.syswrite(SHOW_RACK_ENV) }
     pid = fork { redirect_test_io { exec($unicorn_bin, "-l#@addr:#@port") } }
@@ -638,20 +628,6 @@ def test_read_embedded_cli_switches
     assert_shutdown(pid)
   end
 
-  def test_config_ru_alt_path
-    config_path = "#{@tmpdir}/foo.ru"
-    File.open(config_path, "wb") { |fp| fp.syswrite(HI) }
-    pid = fork do
-      redirect_test_io do
-        Dir.chdir("/")
-        exec($unicorn_bin, "-l#{@addr}:#{@port}", config_path)
-      end
-    end
-    results = retry_hit(["http://#{@addr}:#{@port}/"])
-    assert_equal String, results[0].class
-    assert_shutdown(pid)
-  end
-
   def test_load_module
     libdir = "#{@tmpdir}/lib"
     FileUtils.mkpath([ libdir ])

[-- Attachment #14: 0013-tests-check_stderr-consistently-in-Perl-5-tests.patch --]
[-- Type: text/x-diff, Size: 2415 bytes --]

From cd7ee67fc8ebadec9bdd913d49ed3f214596ea47 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:42 +0000
Subject: [PATCH 13/23] tests: check_stderr consistently in Perl 5 tests

The Bourne shell tests did, so lets not let stuff sneak past us.
---
 t/active-unix-socket.t |  5 ++---
 t/integration.t        |  7 ++-----
 t/lib.perl             | 10 +++++++++-
 3 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/t/active-unix-socket.t b/t/active-unix-socket.t
index 1241904..c132dc2 100644
--- a/t/active-unix-socket.t
+++ b/t/active-unix-socket.t
@@ -20,7 +20,7 @@ my $unix_req = sub {
 	print $fh <<EOM;
 pid "$tmpdir/u.pid"
 listen "$u1"
-stderr_path "$tmpdir/err1.log"
+stderr_path "$tmpdir/err.log"
 EOM
 	close $fh;
 
@@ -113,6 +113,5 @@ is($pidf, $to_kill{u1}, 'pid file contents unchanged after 2nd start failure');
 	ok(-S $u1, 'socket stays after SIGTERM');
 }
 
-my @log = slurp("$tmpdir/err.log");
-diag("@log") if $ENV{V};
+check_stderr;
 done_testing;
diff --git a/t/integration.t b/t/integration.t
index c687655..939dc24 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -246,11 +246,8 @@ EOM
 
 
 undef $ar;
-my @log = slurp("$tmpdir/err.log");
-diag("@log") if $ENV{V};
-my @err = grep(!/NameError.*Unicorn::Waiter/, grep(/error/i, @log));
-is_deeply(\@err, [], 'no unexpected errors in stderr');
-is_deeply([grep(/SIGKILL/, @log)], [], 'no SIGKILL in stderr');
+
+check_stderr;
 
 undef $tmpdir;
 done_testing;
diff --git a/t/lib.perl b/t/lib.perl
index 49632cf..315ef2d 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -11,12 +11,20 @@ use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh);
 our @EXPORT = qw(unicorn slurp tcp_server tcp_connect unicorn $tmpdir $errfh
-	SEEK_SET tcp_host_port start_req which spawn);
+	SEEK_SET tcp_host_port start_req which spawn check_stderr);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
 open($errfh, '>>', "$tmpdir/err.log");
 
+sub check_stderr () {
+	my @log = slurp("$tmpdir/err.log");
+	diag("@log") if $ENV{V};
+	my @err = grep(!/NameError.*Unicorn::Waiter/, grep(/error/i, @log));
+	is_deeply(\@err, [], 'no unexpected errors in stderr');
+	is_deeply([grep(/SIGKILL/, @log)], [], 'no SIGKILL in stderr');
+}
+
 sub tcp_server {
 	my %opt = (
 		ReuseAddr => 1,

[-- Attachment #15: 0014-tests-consistent-tcp_start-and-unix_start-across-Per.patch --]
[-- Type: text/x-diff, Size: 8017 bytes --]

From 0dcd8bd569813a175ad43837db3ab07019a95b99 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:43 +0000
Subject: [PATCH 14/23] tests: consistent tcp_start and unix_start across Perl
 5 tests

I'll be using Unix sockets more in tests since there's no
risk of system-wide conflicts with TCP port allocation.
Furthermore, curl supports `--unix-socket' nowadays; so
there's little reason to rely on TCP sockets and the conflicts
they bring in tests.
---
 t/active-unix-socket.t | 13 ++++---------
 t/integration.t        | 28 ++++++++++++++--------------
 t/lib.perl             | 30 ++++++++++++++++--------------
 3 files changed, 34 insertions(+), 37 deletions(-)

diff --git a/t/active-unix-socket.t b/t/active-unix-socket.t
index c132dc2..8723137 100644
--- a/t/active-unix-socket.t
+++ b/t/active-unix-socket.t
@@ -10,11 +10,6 @@ my %to_kill;
 END { kill('TERM', values(%to_kill)) if keys %to_kill }
 my $u1 = "$tmpdir/u1.sock";
 my $u2 = "$tmpdir/u2.sock";
-my $unix_req = sub {
-	my $s = IO::Socket::UNIX->new(Peer => shift, Type => SOCK_STREAM);
-	print $s @_, "\r\n\r\n";
-	$s;
-};
 {
 	open my $fh, '>', "$tmpdir/u1.conf.rb";
 	print $fh <<EOM;
@@ -53,7 +48,7 @@ is($?, 0, 'daemonized 1st process');
 chomp($to_kill{u1} = slurp("$tmpdir/u.pid"));
 like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file');
 
-chomp(my $worker_pid = readline($unix_req->($u1, 'GET /pid')));
+chomp(my $worker_pid = readline(unix_start($u1, 'GET /pid')));
 like($worker_pid, qr/\A\d+\z/s, 'captured worker pid');
 ok(kill(0, $worker_pid), 'worker is kill-able');
 
@@ -65,7 +60,7 @@ isnt($?, 0, 'conflicting PID file fails to start');
 chomp(my $pidf = slurp("$tmpdir/u.pid"));
 is($pidf, $to_kill{u1}, 'pid file contents unchanged after start failure');
 
-chomp(my $pid2 = readline($unix_req->($u1, 'GET /pid')));
+chomp(my $pid2 = readline(unix_start($u1, 'GET /pid')));
 is($worker_pid, $pid2, 'worker PID unchanged');
 
 
@@ -73,7 +68,7 @@ is($worker_pid, $pid2, 'worker PID unchanged');
 unicorn('-c', "$tmpdir/u3.conf.rb", @uarg)->join;
 isnt($?, 0, 'conflicting UNIX socket fails to start');
 
-chomp($pid2 = readline($unix_req->($u1, 'GET /pid')));
+chomp($pid2 = readline(unix_start($u1, 'GET /pid')));
 is($worker_pid, $pid2, 'worker PID still unchanged');
 
 chomp($pidf = slurp("$tmpdir/u.pid"));
@@ -101,7 +96,7 @@ is($pidf, $to_kill{u1}, 'pid file contents unchanged after 2nd start failure');
 	chomp($to_kill{u1} = slurp("$tmpdir/u.pid"));
 	like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file');
 
-	chomp($pid2 = readline($unix_req->($u1, 'GET /pid')));
+	chomp($pid2 = readline(unix_start($u1, 'GET /pid')));
 	like($pid2, qr/\A\d+\z/, 'worker running');
 
 	ok(kill('TERM', delete $to_kill{u1}), 'SIGTERM restarted daemon');
diff --git a/t/integration.t b/t/integration.t
index 939dc24..b33e3c3 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -70,7 +70,7 @@ EOM
 my ($c, $status, $hdr);
 
 # response header tests
-$c = start_req($srv, 'GET /rack-2-newline-headers HTTP/1.0');
+$c = tcp_start($srv, 'GET /rack-2-newline-headers HTTP/1.0');
 ($status, $hdr) = slurp_hdr($c);
 like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
 my $orig_200_status = $status;
@@ -89,7 +89,7 @@ SKIP: { # Date header check
 };
 
 
-$c = start_req($srv, 'GET /rack-3-array-headers HTTP/1.0');
+$c = tcp_start($srv, 'GET /rack-3-array-headers HTTP/1.0');
 ($status, $hdr) = slurp_hdr($c);
 is_deeply([ grep(/^x-r3: /, @$hdr) ],
 	[ 'x-r3: a', 'x-r3: b', 'x-r3: c' ],
@@ -97,7 +97,7 @@ is_deeply([ grep(/^x-r3: /, @$hdr) ],
 
 SKIP: {
 	eval { require JSON::PP } or skip "JSON::PP missing: $@", 1;
-	my $c = start_req($srv, 'GET /env_dump');
+	my $c = tcp_start($srv, 'GET /env_dump');
 	my $json = do { local $/; readline($c) };
 	unlike($json, qr/^Connection: /smi, 'no connection header for 0.9');
 	unlike($json, qr!\AHTTP/!s, 'no HTTP/1.x prefix for 0.9');
@@ -107,17 +107,17 @@ SKIP: {
 }
 
 # cf. <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
-$c = start_req($srv, 'GET /nil-header-value HTTP/1.0');
+$c = tcp_start($srv, 'GET /nil-header-value HTTP/1.0');
 ($status, $hdr) = slurp_hdr($c);
 is_deeply([grep(/^X-Nil:/, @$hdr)], ['X-Nil: '],
 	'nil header value accepted for broken apps') or diag(explain($hdr));
 
 if ('TODO: ensure Rack::Utils::HTTP_STATUS_CODES is available') {
-	$c = start_req($srv, 'POST /tweak-status-code HTTP/1.0');
+	$c = tcp_start($srv, 'POST /tweak-status-code HTTP/1.0');
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 200 HI\b!, 'status tweaked');
 
-	$c = start_req($srv, 'POST /restore-status-code HTTP/1.0');
+	$c = tcp_start($srv, 'POST /restore-status-code HTTP/1.0');
 	($status, $hdr) = slurp_hdr($c);
 	is($status, $orig_200_status, 'original status restored');
 }
@@ -130,12 +130,12 @@ SKIP: {
 }
 
 if ('bad requests') {
-	$c = start_req($srv, 'GET /env_dump HTTP/1/1');
+	$c = tcp_start($srv, 'GET /env_dump HTTP/1/1');
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 400 \b!, 'got 400 on bad request');
 
-	$c = tcp_connect($srv);
-	print $c 'GET /';
+	$c = tcp_start($srv);
+	print $c 'GET /';;
 	my $buf = join('', (0..9), 'ab');
 	for (0..1023) { print $c $buf }
 	print $c " HTTP/1.0\r\n\r\n";
@@ -143,7 +143,7 @@ if ('bad requests') {
 	like($status, qr!\AHTTP/1\.[01] 414 \b!,
 		'414 on REQUEST_PATH > (12 * 1024)');
 
-	$c = tcp_connect($srv);
+	$c = tcp_start($srv);
 	print $c 'GET /hello-world?a';
 	$buf = join('', (0..9));
 	for (0..1023) { print $c $buf }
@@ -152,7 +152,7 @@ if ('bad requests') {
 	like($status, qr!\AHTTP/1\.[01] 414 \b!,
 		'414 on QUERY_STRING > (10 * 1024)');
 
-	$c = tcp_connect($srv);
+	$c = tcp_start($srv);
 	print $c 'GET /hello-world#a';
 	$buf = join('', (0..9), 'a'..'f');
 	for (0..63) { print $c $buf }
@@ -173,7 +173,7 @@ SKIP: {
 	my $ck_hash = sub {
 		my ($sub, $path, %opt) = @_;
 		seek($rh, 0, SEEK_SET);
-		$c = tcp_connect($srv);
+		$c = tcp_start($srv);
 		$c->autoflush(0);
 		$PUT{$sub}->($rh, $c, $path, %opt);
 		$c->flush or die $!;
@@ -235,11 +235,11 @@ EOM
 	$wpid =~ s/\Apid=// or die;
 	ok(CORE::kill(0, $wpid), 'worker PID retrieved');
 
-	$c = start_req($srv, $req);
+	$c = tcp_start($srv, $req);
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 200\b!, 'minimal request succeeds');
 
-	$c = start_req($srv, 'GET /xxxxxx HTTP/1.0');
+	$c = tcp_start($srv, 'GET /xxxxxx HTTP/1.0');
 	($status, $hdr) = slurp_hdr($c);
 	like($status, qr!\AHTTP/1\.[01] 413\b!, 'big request fails');
 }
diff --git a/t/lib.perl b/t/lib.perl
index 315ef2d..1d6e78d 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -10,8 +10,8 @@ use IO::Socket::INET;
 use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh);
-our @EXPORT = qw(unicorn slurp tcp_server tcp_connect unicorn $tmpdir $errfh
-	SEEK_SET tcp_host_port start_req which spawn check_stderr);
+our @EXPORT = qw(unicorn slurp tcp_server tcp_start unicorn $tmpdir $errfh
+	SEEK_SET tcp_host_port which spawn check_stderr unix_start);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
@@ -55,26 +55,28 @@ sub tcp_host_port {
 	}
 }
 
-sub tcp_connect {
-	my ($dest, %opt) = @_;
-	my $addr = tcp_host_port($dest);
-	my $s = ref($dest)->new(
+sub unix_start ($@) {
+	my ($dst, @req) = @_;
+	my $s = IO::Socket::UNIX->new(Peer => $dst, Type => SOCK_STREAM) or
+		BAIL_OUT "unix connect $dst: $!";
+	$s->autoflush(1);
+	print $s @req, "\r\n\r\n" if @req;
+	$s;
+}
+
+sub tcp_start ($@) {
+	my ($dst, @req) = @_;
+	my $addr = tcp_host_port($dst);
+	my $s = ref($dst)->new(
 		Proto => 'tcp',
 		Type => SOCK_STREAM,
 		PeerAddr => $addr,
-		%opt,
 	) or BAIL_OUT "failed to connect to $addr: $!";
 	$s->autoflush(1);
+	print $s @req, "\r\n\r\n" if @req;
 	$s;
 }
 
-sub start_req {
-	my ($srv, @req) = @_;
-	my $c = tcp_connect($srv);
-	print $c @req, "\r\n\r\n";
-	$c;
-}
-
 sub slurp {
 	open my $fh, '<', $_[0];
 	local $/;

[-- Attachment #16: 0015-port-t9000-preread-input.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 3856 bytes --]

From 1b8840d8d13491eecd2fa92e06f73c65eadd33ba Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:44 +0000
Subject: [PATCH 15/23] port t9000-preread-input.sh to Perl 5

Stuffing it into t/integration.t for now so we can save on
startup costs.
---
 t/integration.t          | 32 ++++++++++++++++++++++++---
 t/lib.perl               |  2 +-
 t/preread_input.ru       |  4 +---
 t/t9000-preread-input.sh | 48 ----------------------------------------
 4 files changed, 31 insertions(+), 55 deletions(-)
 delete mode 100755 t/t9000-preread-input.sh

diff --git a/t/integration.t b/t/integration.t
index b33e3c3..f5afd5d 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -7,8 +7,8 @@
 
 use v5.14; BEGIN { require './t/lib.perl' };
 use autodie;
-my $srv = tcp_server();
-my $host_port = tcp_host_port($srv);
+our $srv = tcp_server();
+our $host_port = tcp_host_port($srv);
 my $t0 = time;
 my $conf = "$tmpdir/u.conf.rb";
 open my $conf_fh, '>', $conf;
@@ -209,8 +209,34 @@ SKIP: {
 	seek($rh, 0, SEEK_SET);
 	$copt->{0} = $rh;
 	$do_curl->('-T-');
-}
 
+	diag 'testing Unicorn::PrereadInput...';
+	local $srv = tcp_server();
+	local $host_port = tcp_host_port($srv);
+	check_stderr;
+	truncate($errfh, 0);
+
+	my $pri = unicorn(qw(-E none t/preread_input.ru), { 3 => $srv });
+	$url = "http://$host_port/";
+
+	$do_curl->(qw(-T t/random_blob));
+	seek($rh, 0, SEEK_SET);
+	$copt->{0} = $rh;
+	$do_curl->('-T-');
+
+	my @pr_err = slurp("$tmpdir/err.log");
+	is(scalar(grep(/app dispatch:/, @pr_err)), 2, 'app dispatched twice');
+
+	# abort a chunked request by blocking curl on a FIFO:
+	$c = tcp_start($srv, "PUT / HTTP/1.1\r\nTransfer-Encoding: chunked");
+	close $c;
+	@pr_err = slurp("$tmpdir/err.log");
+	is(scalar(grep(/app dispatch:/, @pr_err)), 2,
+			'app did not dispatch on aborted request');
+	undef $pri;
+	check_stderr;
+	diag 'Unicorn::PrereadInput middleware tests done';
+}
 
 # ... more stuff here
 
diff --git a/t/lib.perl b/t/lib.perl
index 1d6e78d..b6148cf 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -79,7 +79,7 @@ sub tcp_start ($@) {
 
 sub slurp {
 	open my $fh, '<', $_[0];
-	local $/;
+	local $/ if !wantarray;
 	readline($fh);
 }
 
diff --git a/t/preread_input.ru b/t/preread_input.ru
index 79685c4..f0a1748 100644
--- a/t/preread_input.ru
+++ b/t/preread_input.ru
@@ -1,8 +1,6 @@
 #\-E none
 require 'digest/sha1'
 require 'unicorn/preread_input'
-use Rack::ContentLength
-use Rack::ContentType, "text/plain"
 use Unicorn::PrereadInput
 nr = 0
 run lambda { |env|
@@ -13,5 +11,5 @@
     dig.update(buf)
   end
 
-  [ 200, {}, [ "#{dig.hexdigest}\n" ] ]
+  [ 200, {}, [ dig.hexdigest ] ]
 }
diff --git a/t/t9000-preread-input.sh b/t/t9000-preread-input.sh
deleted file mode 100755
index d6c73ab..0000000
--- a/t/t9000-preread-input.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 9 "PrereadInput middleware tests"
-
-t_begin "setup and start" && {
-	random_blob_sha1=$(rsha1 < random_blob)
-	unicorn_setup
-	unicorn  -D -c $unicorn_config preread_input.ru
-	unicorn_wait_start
-}
-
-t_begin "single identity request" && {
-	curl -sSf -T random_blob http://$listen/ > $tmp
-}
-
-t_begin "sha1 matches" && {
-	test x"$(cat $tmp)" = x"$random_blob_sha1"
-}
-
-t_begin "single chunked request" && {
-	curl -sSf -T- < random_blob http://$listen/ > $tmp
-}
-
-t_begin "sha1 matches" && {
-	test x"$(cat $tmp)" = x"$random_blob_sha1"
-}
-
-t_begin "app only dispatched twice" && {
-	test 2 -eq "$(grep 'app dispatch:' < $r_err | count_lines )"
-}
-
-t_begin "aborted chunked request" && {
-	rm -f $tmp
-	curl -sSf -T- < $fifo http://$listen/ > $tmp &
-	curl_pid=$!
-	kill -9 $curl_pid
-	wait
-}
-
-t_begin "app only dispatched twice" && {
-	test 2 -eq "$(grep 'app dispatch:' < $r_err | count_lines )"
-}
-
-t_begin "killing succeeds" && {
-	kill -QUIT $unicorn_pid
-}
-
-t_done

[-- Attachment #17: 0016-port-t-t0116-client_body_buffer_size.sh-to-Perl-5.patch --]
[-- Type: text/x-diff, Size: 8861 bytes --]

From e9593301044f305d4a0e074f77eea35015ca0ec4 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:45 +0000
Subject: [PATCH 16/23] port t/t0116-client_body_buffer_size.sh to Perl 5

While I'm fine with depending on curl for certain things,
there's no need for it here since unicorn has had lazy
rack.input for over a decade, at this point.
---
 t/active-unix-socket.t                     |  1 +
 t/{t0116.ru => client_body_buffer_size.ru} |  2 -
 t/client_body_buffer_size.t                | 83 ++++++++++++++++++++++
 t/integration.t                            | 10 ---
 t/lib.perl                                 | 12 +++-
 t/t0116-client_body_buffer_size.sh         | 80 ---------------------
 6 files changed, 95 insertions(+), 93 deletions(-)
 rename t/{t0116.ru => client_body_buffer_size.ru} (82%)
 create mode 100644 t/client_body_buffer_size.t
 delete mode 100755 t/t0116-client_body_buffer_size.sh

diff --git a/t/active-unix-socket.t b/t/active-unix-socket.t
index 8723137..4e11837 100644
--- a/t/active-unix-socket.t
+++ b/t/active-unix-socket.t
@@ -109,4 +109,5 @@ is($pidf, $to_kill{u1}, 'pid file contents unchanged after 2nd start failure');
 }
 
 check_stderr;
+undef $tmpdir;
 done_testing;
diff --git a/t/t0116.ru b/t/client_body_buffer_size.ru
similarity index 82%
rename from t/t0116.ru
rename to t/client_body_buffer_size.ru
index fab5fce..44161a5 100644
--- a/t/t0116.ru
+++ b/t/client_body_buffer_size.ru
@@ -1,6 +1,4 @@
 #\ -E none
-use Rack::ContentLength
-use Rack::ContentType, 'text/plain'
 app = lambda do |env|
   input = env['rack.input']
   case env["PATH_INFO"]
diff --git a/t/client_body_buffer_size.t b/t/client_body_buffer_size.t
new file mode 100644
index 0000000..b1a99f3
--- /dev/null
+++ b/t/client_body_buffer_size.t
@@ -0,0 +1,83 @@
+#!perl -w
+# Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+
+use v5.14; BEGIN { require './t/lib.perl' };
+use autodie;
+my $uconf = "$tmpdir/u.conf.rb";
+
+open my $conf_fh, '>', $uconf;
+$conf_fh->autoflush(1);
+print $conf_fh <<EOM;
+client_body_buffer_size 0
+EOM
+my $srv = tcp_server();
+my $host_port = tcp_host_port($srv);
+my @uarg = (qw(-E none t/client_body_buffer_size.ru -c), $uconf);
+my $ar = unicorn(@uarg, { 3 => $srv });
+my ($c, $status, $hdr);
+my $mem_class = 'StringIO';
+my $fs_class = 'Unicorn::TmpIO';
+
+$c = tcp_start($srv, "PUT /input_class HTTP/1.0\r\nContent-Length: 0");
+($status, $hdr) = slurp_hdr($c);
+like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
+is(readline($c), $mem_class, 'zero-byte file is StringIO');
+
+$c = tcp_start($srv, "PUT /tmp_class HTTP/1.0\r\nContent-Length: 1");
+print $c '.';
+($status, $hdr) = slurp_hdr($c);
+like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
+is(readline($c), $fs_class, '1 byte file is filesystem-backed');
+
+
+my $fifo = "$tmpdir/fifo";
+POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
+seek($conf_fh, 0, SEEK_SET);
+truncate($conf_fh, 0);
+print $conf_fh <<EOM;
+listen "$host_port" # TODO: remove this requirement for SIGHUP
+after_fork { |_,_| File.open('$fifo', 'w') { |fp| fp.write "pid=#\$\$" } }
+EOM
+$ar->do_kill('HUP');
+open my $fifo_fh, '<', $fifo;
+like(my $wpid = readline($fifo_fh), qr/\Apid=\d+\z/a ,
+	'reloaded w/ default client_body_buffer_size');
+
+
+$c = tcp_start($srv, "PUT /tmp_class HTTP/1.0\r\nContent-Length: 1");
+($status, $hdr) = slurp_hdr($c);
+like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
+is(readline($c), $mem_class, 'class for a 1 byte file is memory-backed');
+
+
+my $one_meg = 1024 ** 2;
+$c = tcp_start($srv, "PUT /tmp_class HTTP/1.0\r\nContent-Length: $one_meg");
+($status, $hdr) = slurp_hdr($c);
+like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
+is(readline($c), $fs_class, '1 megabyte file is FS-backed');
+
+# reload with bigger client_body_buffer_size
+say $conf_fh "client_body_buffer_size $one_meg";
+$ar->do_kill('HUP');
+open $fifo_fh, '<', $fifo;
+like($wpid = readline($fifo_fh), qr/\Apid=\d+\z/a ,
+	'reloaded w/ bigger client_body_buffer_size');
+
+
+$c = tcp_start($srv, "PUT /tmp_class HTTP/1.0\r\nContent-Length: $one_meg");
+($status, $hdr) = slurp_hdr($c);
+like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
+is(readline($c), $mem_class, '1 megabyte file is now memory-backed');
+
+my $too_big = $one_meg + 1;
+$c = tcp_start($srv, "PUT /tmp_class HTTP/1.0\r\nContent-Length: $too_big");
+($status, $hdr) = slurp_hdr($c);
+like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid');
+is(readline($c), $fs_class, '1 megabyte + 1 byte file is FS-backed');
+
+
+undef $ar;
+check_stderr;
+undef $tmpdir;
+done_testing;
diff --git a/t/integration.t b/t/integration.t
index f5afd5d..855c260 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -15,16 +15,6 @@ open my $conf_fh, '>', $conf;
 $conf_fh->autoflush(1);
 my $ar = unicorn(qw(-E none t/integration.ru -c), $conf, { 3 => $srv });
 my $curl = which('curl');
-END { diag slurp("$tmpdir/err.log") if $tmpdir };
-sub slurp_hdr {
-	my ($c) = @_;
-	local $/ = "\r\n\r\n"; # affects both readline+chomp
-	chomp(my $hdr = readline($c));
-	my ($status, @hdr) = split(/\r\n/, $hdr);
-	diag explain([ $status, \@hdr ]) if $ENV{V};
-	($status, \@hdr);
-}
-
 my %PUT = (
 	chunked_md5 => sub {
 		my ($in, $out, $path, %opt) = @_;
diff --git a/t/lib.perl b/t/lib.perl
index b6148cf..2685c3b 100644
--- a/t/lib.perl
+++ b/t/lib.perl
@@ -11,11 +11,12 @@ use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
 use File::Temp 0.19 (); # 0.19 for ->newdir
 our ($tmpdir, $errfh);
 our @EXPORT = qw(unicorn slurp tcp_server tcp_start unicorn $tmpdir $errfh
-	SEEK_SET tcp_host_port which spawn check_stderr unix_start);
+	SEEK_SET tcp_host_port which spawn check_stderr unix_start slurp_hdr);
 
 my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
 $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
 open($errfh, '>>', "$tmpdir/err.log");
+END { diag slurp("$tmpdir/err.log") if $tmpdir };
 
 sub check_stderr () {
 	my @log = slurp("$tmpdir/err.log");
@@ -25,6 +26,15 @@ sub check_stderr () {
 	is_deeply([grep(/SIGKILL/, @log)], [], 'no SIGKILL in stderr');
 }
 
+sub slurp_hdr {
+	my ($c) = @_;
+	local $/ = "\r\n\r\n"; # affects both readline+chomp
+	chomp(my $hdr = readline($c));
+	my ($status, @hdr) = split(/\r\n/, $hdr);
+	diag explain([ $status, \@hdr ]) if $ENV{V};
+	($status, \@hdr);
+}
+
 sub tcp_server {
 	my %opt = (
 		ReuseAddr => 1,
diff --git a/t/t0116-client_body_buffer_size.sh b/t/t0116-client_body_buffer_size.sh
deleted file mode 100755
index c9e17c7..0000000
--- a/t/t0116-client_body_buffer_size.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/sh
-. ./test-lib.sh
-t_plan 12 "client_body_buffer_size settings"
-
-t_begin "setup and start" && {
-	unicorn_setup
-	rtmpfiles unicorn_config_tmp one_meg
-	dd if=/dev/zero bs=1M count=1 of=$one_meg
-	cat >> $unicorn_config <<EOF
-after_fork do |server, worker|
-  File.open("$fifo", "wb") { |fp| fp.syswrite "START" }
-end
-EOF
-	cat $unicorn_config > $unicorn_config_tmp
-	echo client_body_buffer_size 0 >> $unicorn_config
-	unicorn -D -c $unicorn_config t0116.ru
-	unicorn_wait_start
-	fs_class=Unicorn::TmpIO
-	mem_class=StringIO
-
-	test x"$(cat $fifo)" = xSTART
-}
-
-t_begin "class for a zero-byte file should be StringIO" && {
-	> $tmp
-	test xStringIO = x"$(curl -T $tmp -sSf http://$listen/input_class)"
-}
-
-t_begin "class for a 1 byte file should be filesystem-backed" && {
-	echo > $tmp
-	test x$fs_class = x"$(curl -T $tmp -sSf http://$listen/tmp_class)"
-}
-
-t_begin "reload with default client_body_buffer_size" && {
-	mv $unicorn_config_tmp $unicorn_config
-	kill -HUP $unicorn_pid
-	test x"$(cat $fifo)" = xSTART
-}
-
-t_begin "class for a 1 byte file should be memory-backed" && {
-	echo > $tmp
-	test x$mem_class = x"$(curl -T $tmp -sSf http://$listen/tmp_class)"
-}
-
-t_begin "class for a random blob file should be filesystem-backed" && {
-	resp="$(curl -T random_blob -sSf http://$listen/tmp_class)"
-	test x$fs_class = x"$resp"
-}
-
-t_begin "one megabyte file should be filesystem-backed" && {
-	resp="$(curl -T $one_meg -sSf http://$listen/tmp_class)"
-	test x$fs_class = x"$resp"
-}
-
-t_begin "reload with a big client_body_buffer_size" && {
-	echo "client_body_buffer_size(1024 * 1024)" >> $unicorn_config
-	kill -HUP $unicorn_pid
-	test x"$(cat $fifo)" = xSTART
-}
-
-t_begin "one megabyte file should be memory-backed" && {
-	resp="$(curl -T $one_meg -sSf http://$listen/tmp_class)"
-	test x$mem_class = x"$resp"
-}
-
-t_begin "one megabyte + 1 byte file should be filesystem-backed" && {
-	echo >> $one_meg
-	resp="$(curl -T $one_meg -sSf http://$listen/tmp_class)"
-	test x$fs_class = x"$resp"
-}
-
-t_begin "killing succeeds" && {
-	kill $unicorn_pid
-}
-
-t_begin "check stderr" && {
-	check_stderr
-}
-
-t_done

[-- Attachment #18: 0017-tests-get-rid-of-sha1sum.rb-and-rsha1-sh-function.patch --]
[-- Type: text/x-diff, Size: 1255 bytes --]

From b47912160f2336dde3901e588cc23fb2c2f8d9dc Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:46 +0000
Subject: [PATCH 17/23] tests: get rid of sha1sum.rb and rsha1() sh function

These are no longer needed since Perl has long included
Digest::SHA
---
 t/bin/sha1sum.rb | 17 -----------------
 t/test-lib.sh    |  4 ----
 2 files changed, 21 deletions(-)
 delete mode 100755 t/bin/sha1sum.rb

diff --git a/t/bin/sha1sum.rb b/t/bin/sha1sum.rb
deleted file mode 100755
index 53d68ce..0000000
--- a/t/bin/sha1sum.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env ruby
-# -*- encoding: binary -*-
-# Reads from stdin and outputs the SHA1 hex digest of the input
-
-require 'digest/sha1'
-$stdout.sync = $stderr.sync = true
-$stdout.binmode
-$stdin.binmode
-bs = 16384
-digest = Digest::SHA1.new
-if buf = $stdin.read(bs)
-  begin
-    digest.update(buf)
-  end while $stdin.read(bs, buf)
-end
-
-$stdout.syswrite("#{digest.hexdigest}\n")
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e70d0c6..8613144 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -123,7 +123,3 @@ unicorn_wait_start () {
 	# no need to play tricks with FIFOs since we got "ready_pipe" now
 	unicorn_pid=$(cat $pid)
 }
-
-rsha1 () {
-	sha1sum.rb
-}

[-- Attachment #19: 0018-early_hints-supports-Rack-3-array-headers.patch --]
[-- Type: text/x-diff, Size: 4606 bytes --]

From 6ad9f4b54ee16ffecea7e16b710552b45db33a16 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:47 +0000
Subject: [PATCH 18/23] early_hints supports Rack 3 array headers

We can hoist out append_headers into a new method and use it in
both e103_response_write and http_response_write.

t/integration.t now tests early_hints with both possible
values of check_client_connection.
---
 t/integration.ru |  7 +++++++
 t/integration.t  | 47 ++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/t/integration.ru b/t/integration.ru
index edc408c..dab384d 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -5,6 +5,11 @@
 # this goes for t/integration.t  We'll try to put as many tests
 # in here as possible to avoid startup overhead of Ruby.
 
+def early_hints(env, val)
+  env['rack.early_hints'].call('link' => val) # val may be ary or string
+  [ 200, {}, [ val.class.to_s ] ]
+end
+
 $orig_rack_200 = nil
 def tweak_status_code
   $orig_rack_200 = Rack::Utils::HTTP_STATUS_CODES[200]
@@ -81,6 +86,8 @@ def rack_input_tests(env)
     when '/env_dump'; [ 200, {}, [ env_dump(env) ] ]
     when '/write_on_close'; write_on_close
     when '/pid'; [ 200, {}, [ "#$$\n" ] ]
+    when '/early_hints_rack2'; early_hints(env, "r\n2")
+    when '/early_hints_rack3'; early_hints(env, %w(r 3))
     else '/'; [ 200, {}, [ env_dump(env) ] ]
     end # case PATH_INFO (GET)
   when 'POST'
diff --git a/t/integration.t b/t/integration.t
index 855c260..8433497 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -13,8 +13,16 @@ my $t0 = time;
 my $conf = "$tmpdir/u.conf.rb";
 open my $conf_fh, '>', $conf;
 $conf_fh->autoflush(1);
+my $u1 = "$tmpdir/u1";
+print $conf_fh <<EOM;
+early_hints true
+listen "$u1"
+listen "$host_port" # TODO: remove this requirement for SIGHUP
+EOM
 my $ar = unicorn(qw(-E none t/integration.ru -c), $conf, { 3 => $srv });
 my $curl = which('curl');
+my $fifo = "$tmpdir/fifo";
+POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
 my %PUT = (
 	chunked_md5 => sub {
 		my ($in, $out, $path, %opt) = @_;
@@ -102,6 +110,26 @@ $c = tcp_start($srv, 'GET /nil-header-value HTTP/1.0');
 is_deeply([grep(/^X-Nil:/, @$hdr)], ['X-Nil: '],
 	'nil header value accepted for broken apps') or diag(explain($hdr));
 
+my $ck_early_hints = sub {
+	my ($note) = @_;
+	$c = unix_start($u1, 'GET /early_hints_rack2 HTTP/1.0');
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 103\b!, 'got 103 for rack 2 value');
+	is_deeply(['link: r', 'link: 2'], $hdr, 'rack 2 hints match '.$note);
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 200\b!, 'got 200 afterwards');
+	is(readline($c), 'String', 'early hints used a String for rack 2');
+
+	$c = unix_start($u1, 'GET /early_hints_rack3 HTTP/1.0');
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 103\b!, 'got 103 for rack 3');
+	is_deeply(['link: r', 'link: 3'], $hdr, 'rack 3 hints match '.$note);
+	($status, $hdr) = slurp_hdr($c);
+	like($status, qr!\AHTTP/1\.[01] 200\b!, 'got 200 afterwards');
+	is(readline($c), 'Array', 'early hints used a String for rack 3');
+};
+$ck_early_hints->('ccc off'); # we'll retest later
+
 if ('TODO: ensure Rack::Utils::HTTP_STATUS_CODES is available') {
 	$c = tcp_start($srv, 'POST /tweak-status-code HTTP/1.0');
 	($status, $hdr) = slurp_hdr($c);
@@ -154,6 +182,7 @@ if ('bad requests') {
 # input tests
 my ($blob_size, $blob_hash);
 SKIP: {
+	skip 'SKIP_EXPENSIVE on', 1 if $ENV{SKIP_EXPENSIVE};
 	CORE::open(my $rh, '<', 't/random_blob') or
 		skip "t/random_blob not generated $!", 1;
 	$blob_size = -s $rh;
@@ -232,16 +261,24 @@ SKIP: {
 
 # SIGHUP-able stuff goes here
 
+if ('check_client_connection') {
+	print $conf_fh <<EOM; # appending to existing
+check_client_connection true
+after_fork { |_,_| File.open('$fifo', 'w') { |fp| fp.write "pid=#\$\$" } }
+EOM
+	$ar->do_kill('HUP');
+	open my $fifo_fh, '<', $fifo;
+	my $wpid = readline($fifo_fh);
+	like($wpid, qr/\Apid=\d+\z/a , 'new worker ready');
+	$ck_early_hints->('ccc on');
+}
+
 if ('max_header_len internal API') {
 	undef $c;
 	my $req = 'GET / HTTP/1.0';
 	my $len = length($req."\r\n\r\n");
-	my $fifo = "$tmpdir/fifo";
-	POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
-	print $conf_fh <<EOM;
+	print $conf_fh <<EOM; # appending to existing
 Unicorn::HttpParser.max_header_len = $len
-listen "$host_port" # TODO: remove this requirement for SIGHUP
-after_fork { |_,_| File.open('$fifo', 'w') { |fp| fp.write "pid=#\$\$" } }
 EOM
 	$ar->do_kill('HUP');
 	open my $fifo_fh, '<', $fifo;

[-- Attachment #20: 0019-test_server-drop-early_hints-test.patch --]
[-- Type: text/x-diff, Size: 1737 bytes --]

From 3e6bc9fb589fd88469349a38a77704c3333623e0 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:48 +0000
Subject: [PATCH 19/23] test_server: drop early_hints test

t/integration.t already is more complete in that it tests
both Rack 2 and 3 along with both possible values of
check_client_connection.
---
 test/unit/test_server.rb | 31 -------------------------------
 1 file changed, 31 deletions(-)

diff --git a/test/unit/test_server.rb b/test/unit/test_server.rb
index fe98fcc..0a710d1 100644
--- a/test/unit/test_server.rb
+++ b/test/unit/test_server.rb
@@ -23,17 +23,6 @@ def call(env)
   end
 end
 
-class TestEarlyHintsHandler
-  def call(env)
-    while env['rack.input'].read(4096)
-    end
-    env['rack.early_hints'].call(
-      "Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload"
-    )
-    [200, { 'content-type' => 'text/plain' }, ['hello!\n']]
-  end
-end
-
 class TestRackAfterReply
   def initialize
     @called = false
@@ -112,26 +101,6 @@ def test_preload_app_config
     tmp.close!
   end
 
-  def test_early_hints
-    teardown
-    redirect_test_io do
-      @server = HttpServer.new(TestEarlyHintsHandler.new,
-                               :listeners => [ "127.0.0.1:#@port"],
-                               :early_hints => true)
-      @server.start
-    end
-
-    sock = tcp_socket('127.0.0.1', @port)
-    sock.syswrite("GET / HTTP/1.0\r\n\r\n")
-
-    responses = sock.read(4096)
-    assert_match %r{\AHTTP/1.[01] 103\b}, responses
-    assert_match %r{^Link: </style\.css>}, responses
-    assert_match %r{^Link: </script\.js>}, responses
-
-    assert_match %r{^HTTP/1.[01] 200\b}, responses
-  end
-
   def test_after_reply
     teardown
 

[-- Attachment #21: 0020-t-integration.t-switch-PUT-tests-to-MD5-reuse-buffer.patch --]
[-- Type: text/x-diff, Size: 3740 bytes --]

From cb826915cdd1881cbcfc1fb4e645d26244dfda71 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:49 +0000
Subject: [PATCH 20/23] t/integration.t: switch PUT tests to MD5, reuse buffers

MD5 is faster, and these tests aren't meant to be secure,
they're just for checking for data corruption.
Furthermore, Content-MD5 is a supported HTTP trailer and
we can verify that here to obsolete other tests.

Furthermore, we can reuse buffers on env['rack.input'].read
calls to avoid malloc(3) and GC overhead.

Combined, these give roughly a 3% speedup for t/integration.t
on my system.
---
 t/integration.ru   | 20 +++++++++++++++-----
 t/integration.t    |  5 ++---
 t/preread_input.ru | 17 ++++++++++++-----
 3 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/t/integration.ru b/t/integration.ru
index dab384d..086126a 100644
--- a/t/integration.ru
+++ b/t/integration.ru
@@ -55,8 +55,8 @@ def env_dump(env)
 def rack_input_tests(env)
   return [ 100, {}, [] ] if /\A100-continue\z/i =~ env['HTTP_EXPECT']
   cap = 16384
-  require 'digest/sha1'
-  digest = Digest::SHA1.new
+  require 'digest/md5'
+  dig = Digest::MD5.new
   input = env['rack.input']
   case env['PATH_INFO']
   when '/rack_input/size_first'; input.size
@@ -68,11 +68,21 @@ def rack_input_tests(env)
   if buf = input.read(rand(cap))
     begin
       raise "#{buf.size} > #{cap}" if buf.size > cap
-      digest.update(buf)
+      dig.update(buf)
     end while input.read(rand(cap), buf)
+    buf.clear # remove this call if Ruby ever gets escape analysis
   end
-  [ 200, {'content-length' => '40', 'content-type' => 'text/plain'},
-    [ digest.hexdigest ] ]
+  h = { 'content-type' => 'text/plain' }
+  if env['HTTP_TRAILER'] =~ /\bContent-MD5\b/i
+    cmd5_b64 = env['HTTP_CONTENT_MD5'] or return [500, {}, ['No Content-MD5']]
+    cmd5_bin = cmd5_b64.unpack('m')[0]
+    if cmd5_bin != dig.digest
+      h['content-length'] = cmd5_b64.size.to_s
+      return [ 500, h, [ cmd5_b64 ] ]
+    end
+  end
+  h['content-length'] = '32'
+  [ 200, h, [ dig.hexdigest ] ]
 end
 
 run(lambda do |env|
diff --git a/t/integration.t b/t/integration.t
index 8433497..38a9675 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -27,7 +27,6 @@ my %PUT = (
 	chunked_md5 => sub {
 		my ($in, $out, $path, %opt) = @_;
 		my $bs = $opt{bs} // 16384;
-		require Digest::MD5;
 		my $dig = Digest::MD5->new;
 		print $out <<EOM;
 PUT $path HTTP/1.1\r
@@ -186,8 +185,8 @@ SKIP: {
 	CORE::open(my $rh, '<', 't/random_blob') or
 		skip "t/random_blob not generated $!", 1;
 	$blob_size = -s $rh;
-	require Digest::SHA;
-	$blob_hash = Digest::SHA->new(1)->addfile($rh)->hexdigest;
+	require Digest::MD5;
+	$blob_hash = Digest::MD5->new->addfile($rh)->hexdigest;
 
 	my $ck_hash = sub {
 		my ($sub, $path, %opt) = @_;
diff --git a/t/preread_input.ru b/t/preread_input.ru
index f0a1748..18af221 100644
--- a/t/preread_input.ru
+++ b/t/preread_input.ru
@@ -1,15 +1,22 @@
 #\-E none
-require 'digest/sha1'
+require 'digest/md5'
 require 'unicorn/preread_input'
 use Unicorn::PrereadInput
 nr = 0
 run lambda { |env|
   $stderr.write "app dispatch: #{nr += 1}\n"
   input = env["rack.input"]
-  dig = Digest::SHA1.new
-  while buf = input.read(16384)
-    dig.update(buf)
+  dig = Digest::MD5.new
+  if buf = input.read(16384)
+    begin
+      dig.update(buf)
+    end while input.read(16384, buf)
+    buf.clear # remove this call if Ruby ever gets escape analysis
+  end
+  if env['HTTP_TRAILER'] =~ /\bContent-MD5\b/i
+    cmd5_b64 = env['HTTP_CONTENT_MD5'] or return [500, {}, ['No Content-MD5']]
+    cmd5_bin = cmd5_b64.unpack('m')[0]
+    return [500, {}, [ cmd5_b64 ] ] if cmd5_bin != dig.digest
   end
-
   [ 200, {}, [ dig.hexdigest ] ]
 }

[-- Attachment #22: 0021-tests-move-test_upload.rb-tests-to-t-integration.t.patch --]
[-- Type: text/x-diff, Size: 12280 bytes --]

From 181e4b5b6339fc5e9c3ad7d3690b736f6bd038aa Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:50 +0000
Subject: [PATCH 21/23] tests: move test_upload.rb tests to t/integration.t

The overread tests are ported over, and checksumming alone
is enough to guard against data corruption.

Randomizing the size of `read' calls on the client side will
shake out any boundary bugs on the server side.
---
 t/integration.t          |  32 ++++-
 test/unit/test_upload.rb | 301 ---------------------------------------
 2 files changed, 27 insertions(+), 306 deletions(-)
 delete mode 100644 test/unit/test_upload.rb

diff --git a/t/integration.t b/t/integration.t
index 38a9675..a568758 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -26,7 +26,6 @@ POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
 my %PUT = (
 	chunked_md5 => sub {
 		my ($in, $out, $path, %opt) = @_;
-		my $bs = $opt{bs} // 16384;
 		my $dig = Digest::MD5->new;
 		print $out <<EOM;
 PUT $path HTTP/1.1\r
@@ -36,7 +35,7 @@ Trailer: Content-MD5\r
 EOM
 		my ($buf, $r);
 		while (1) {
-			$r = read($in, $buf, $bs);
+			$r = read($in, $buf, 999 + int(rand(0xffff)));
 			last if $r == 0;
 			printf $out "%x\r\n", length($buf);
 			print $out $buf, "\r\n";
@@ -46,15 +45,15 @@ EOM
 	},
 	identity => sub {
 		my ($in, $out, $path, %opt) = @_;
-		my $bs = $opt{bs} // 16384;
 		my $clen = $opt{-s} // -s $in;
 		print $out <<EOM;
 PUT $path HTTP/1.0\r
 Content-Length: $clen\r
 \r
 EOM
-		my ($buf, $r, $len);
+		my ($buf, $r, $len, $bs);
 		while ($clen) {
+			$bs = 999 + int(rand(0xffff));
 			$len = $clen > $bs ? $bs : $clen;
 			$r = read($in, $buf, $len);
 			die 'premature EOF' if $r == 0;
@@ -192,8 +191,10 @@ SKIP: {
 		my ($sub, $path, %opt) = @_;
 		seek($rh, 0, SEEK_SET);
 		$c = tcp_start($srv);
-		$c->autoflush(0);
+		$c->autoflush($opt{sync} // 0);
 		$PUT{$sub}->($rh, $c, $path, %opt);
+		defined($opt{overwrite}) and
+			print { $c } ('x' x $opt{overwrite});
 		$c->flush or die $!;
 		($status, $hdr) = slurp_hdr($c);
 		is(readline($c), $blob_hash, "$sub $path");
@@ -205,6 +206,27 @@ SKIP: {
 	$ck_hash->('chunked_md5', '/rack_input/size_first');
 	$ck_hash->('chunked_md5', '/rack_input/rewind_first');
 
+	$ck_hash->('identity', '/rack_input', -s => $blob_size, sync => 1);
+	$ck_hash->('chunked_md5', '/rack_input', sync => 1);
+
+	# ensure small overwrites don't get checksummed
+	$ck_hash->('identity', '/rack_input', -s => $blob_size,
+			overwrite => 1); # one extra byte
+
+	# excessive overwrite truncated
+	$c = tcp_start($srv);
+	$c->autoflush(0);
+	print $c "PUT /rack_input HTTP/1.0\r\nContent-Length: 1\r\n\r\n";
+	if (1) {
+		local $SIG{PIPE} = 'IGNORE';
+		my $buf = "\0" x 8192;
+		my $n = 0;
+		my $end = time + 5;
+		$! = 0;
+		while (print $c $buf and time < $end) { ++$n }
+		ok($!, 'overwrite truncated') or diag "n=$n err=$! ".time;
+	}
+	undef $c;
 
 	$curl // skip 'no curl found in PATH', 1;
 
diff --git a/test/unit/test_upload.rb b/test/unit/test_upload.rb
deleted file mode 100644
index 76e6c1c..0000000
--- a/test/unit/test_upload.rb
+++ /dev/null
@@ -1,301 +0,0 @@
-# -*- encoding: binary -*-
-
-# Copyright (c) 2009 Eric Wong
-require './test/test_helper'
-require 'digest/md5'
-
-include Unicorn
-
-class UploadTest < Test::Unit::TestCase
-
-  def setup
-    @addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
-    @port = unused_port
-    @hdr = {'Content-Type' => 'text/plain', 'Content-Length' => '0'}
-    @bs = 4096
-    @count = 256
-    @server = nil
-
-    # we want random binary data to test 1.9 encoding-aware IO craziness
-    @random = File.open('/dev/urandom','rb')
-    @sha1 = Digest::SHA1.new
-    @sha1_app = lambda do |env|
-      input = env['rack.input']
-      resp = {}
-
-      @sha1.reset
-      while buf = input.read(@bs)
-        @sha1.update(buf)
-      end
-      resp[:sha1] = @sha1.hexdigest
-
-      # rewind and read again
-      input.rewind
-      @sha1.reset
-      while buf = input.read(@bs)
-        @sha1.update(buf)
-      end
-
-      if resp[:sha1] == @sha1.hexdigest
-        resp[:sysread_read_byte_match] = true
-      end
-
-      if expect_size = env['HTTP_X_EXPECT_SIZE']
-        if expect_size.to_i == input.size
-          resp[:expect_size_match] = true
-        end
-      end
-      resp[:size] = input.size
-      resp[:content_md5] = env['HTTP_CONTENT_MD5']
-
-      [ 200, @hdr.merge({'X-Resp' => resp.inspect}), [] ]
-    end
-  end
-
-  def teardown
-    redirect_test_io { @server.stop(false) } if @server
-    @random.close
-    reset_sig_handlers
-  end
-
-  def test_put
-    start_server(@sha1_app)
-    sock = tcp_socket(@addr, @port)
-    sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
-    @count.times do |i|
-      buf = @random.sysread(@bs)
-      @sha1.update(buf)
-      sock.syswrite(buf)
-    end
-    read = sock.read.split(/\r\n/)
-    assert_equal "HTTP/1.1 200 OK", read[0]
-    resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
-    assert_equal length, resp[:size]
-    assert_equal @sha1.hexdigest, resp[:sha1]
-  end
-
-  def test_put_content_md5
-    md5 = Digest::MD5.new
-    start_server(@sha1_app)
-    sock = tcp_socket(@addr, @port)
-    sock.syswrite("PUT / HTTP/1.0\r\nTransfer-Encoding: chunked\r\n" \
-                  "Trailer: Content-MD5\r\n\r\n")
-    @count.times do |i|
-      buf = @random.sysread(@bs)
-      @sha1.update(buf)
-      md5.update(buf)
-      sock.syswrite("#{'%x' % buf.size}\r\n")
-      sock.syswrite(buf << "\r\n")
-    end
-    sock.syswrite("0\r\n")
-
-    content_md5 = [ md5.digest! ].pack('m').strip.freeze
-    sock.syswrite("Content-MD5: #{content_md5}\r\n\r\n")
-    read = sock.read.split(/\r\n/)
-    assert_equal "HTTP/1.1 200 OK", read[0]
-    resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
-    assert_equal length, resp[:size]
-    assert_equal @sha1.hexdigest, resp[:sha1]
-    assert_equal content_md5, resp[:content_md5]
-  end
-
-  def test_put_trickle_small
-    @count, @bs = 2, 128
-    start_server(@sha1_app)
-    assert_equal 256, length
-    sock = tcp_socket(@addr, @port)
-    hdr = "PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n"
-    @count.times do
-      buf = @random.sysread(@bs)
-      @sha1.update(buf)
-      hdr << buf
-      sock.syswrite(hdr)
-      hdr = ''
-      sleep 0.6
-    end
-    read = sock.read.split(/\r\n/)
-    assert_equal "HTTP/1.1 200 OK", read[0]
-    resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
-    assert_equal length, resp[:size]
-    assert_equal @sha1.hexdigest, resp[:sha1]
-  end
-
-  def test_put_keepalive_truncates_small_overwrite
-    start_server(@sha1_app)
-    sock = tcp_socket(@addr, @port)
-    to_upload = length + 1
-    sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{to_upload}\r\n\r\n")
-    @count.times do
-      buf = @random.sysread(@bs)
-      @sha1.update(buf)
-      sock.syswrite(buf)
-    end
-    sock.syswrite('12345') # write 4 bytes more than we expected
-    @sha1.update('1')
-
-    buf = sock.readpartial(4096)
-    while buf !~ /\r\n\r\n/
-      buf << sock.readpartial(4096)
-    end
-    read = buf.split(/\r\n/)
-    assert_equal "HTTP/1.1 200 OK", read[0]
-    resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
-    assert_equal to_upload, resp[:size]
-    assert_equal @sha1.hexdigest, resp[:sha1]
-  end
-
-  def test_put_excessive_overwrite_closed
-    tmp = Tempfile.new('overwrite_check')
-    tmp.sync = true
-    start_server(lambda { |env|
-      nr = 0
-      while buf = env['rack.input'].read(65536)
-        nr += buf.size
-      end
-      tmp.write(nr.to_s)
-      [ 200, @hdr, [] ]
-    })
-    sock = tcp_socket(@addr, @port)
-    buf = ' ' * @bs
-    sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
-
-    @count.times { sock.syswrite(buf) }
-    assert_raise(Errno::ECONNRESET, Errno::EPIPE) do
-      ::Unicorn::Const::CHUNK_SIZE.times { sock.syswrite(buf) }
-    end
-    sock.gets
-    tmp.rewind
-    assert_equal length, tmp.read.to_i
-  end
-
-  # Despite reading numerous articles and inspecting the 1.9.1-p0 C
-  # source, Eric Wong will never trust that we're always handling
-  # encoding-aware IO objects correctly.  Thus this test uses shell
-  # utilities that should always operate on files/sockets on a
-  # byte-level.
-  def test_uncomfortable_with_onenine_encodings
-    # POSIX doesn't require all of these to be present on a system
-    which('curl') or return
-    which('sha1sum') or return
-    which('dd') or return
-
-    start_server(@sha1_app)
-
-    tmp = Tempfile.new('dd_dest')
-    assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
-                        "bs=#{@bs}", "count=#{@count}"),
-           "dd #@random to #{tmp}")
-    sha1_re = %r!\b([a-f0-9]{40})\b!
-    sha1_out = `sha1sum #{tmp.path}`
-    assert $?.success?, 'sha1sum ran OK'
-
-    assert_match(sha1_re, sha1_out)
-    sha1 = sha1_re.match(sha1_out)[1]
-    resp = `curl -isSfN -T#{tmp.path} http://#@addr:#@port/`
-    assert $?.success?, 'curl ran OK'
-    assert_match(%r!\b#{sha1}\b!, resp)
-    assert_match(/sysread_read_byte_match/, resp)
-
-    # small StringIO path
-    assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
-                        "bs=1024", "count=1"),
-           "dd #@random to #{tmp}")
-    sha1_re = %r!\b([a-f0-9]{40})\b!
-    sha1_out = `sha1sum #{tmp.path}`
-    assert $?.success?, 'sha1sum ran OK'
-
-    assert_match(sha1_re, sha1_out)
-    sha1 = sha1_re.match(sha1_out)[1]
-    resp = `curl -isSfN -T#{tmp.path} http://#@addr:#@port/`
-    assert $?.success?, 'curl ran OK'
-    assert_match(%r!\b#{sha1}\b!, resp)
-    assert_match(/sysread_read_byte_match/, resp)
-  end
-
-  def test_chunked_upload_via_curl
-    # POSIX doesn't require all of these to be present on a system
-    which('curl') or return
-    which('sha1sum') or return
-    which('dd') or return
-
-    start_server(@sha1_app)
-
-    tmp = Tempfile.new('dd_dest')
-    assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
-                        "bs=#{@bs}", "count=#{@count}"),
-           "dd #@random to #{tmp}")
-    sha1_re = %r!\b([a-f0-9]{40})\b!
-    sha1_out = `sha1sum #{tmp.path}`
-    assert $?.success?, 'sha1sum ran OK'
-
-    assert_match(sha1_re, sha1_out)
-    sha1 = sha1_re.match(sha1_out)[1]
-    cmd = "curl -H 'X-Expect-Size: #{tmp.size}' --tcp-nodelay \
-           -isSf --no-buffer -T- " \
-          "http://#@addr:#@port/"
-    resp = Tempfile.new('resp')
-    resp.sync = true
-
-    rd, wr = IO.pipe.each do |io|
-      io.sync = io.close_on_exec = true
-    end
-    pid = spawn(*cmd, { 0 => rd, 1 => resp })
-    rd.close
-
-    tmp.rewind
-    @count.times { |i|
-      wr.write(tmp.read(@bs))
-      sleep(rand / 10) if 0 == i % 8
-    }
-    wr.close
-    pid, status = Process.waitpid2(pid)
-
-    resp.rewind
-    resp = resp.read
-    assert status.success?, 'curl ran OK'
-    assert_match(%r!\b#{sha1}\b!, resp)
-    assert_match(/sysread_read_byte_match/, resp)
-    assert_match(/expect_size_match/, resp)
-  end
-
-  def test_curl_chunked_small
-    # POSIX doesn't require all of these to be present on a system
-    which('curl') or return
-    which('sha1sum') or return
-    which('dd') or return
-
-    start_server(@sha1_app)
-
-    tmp = Tempfile.new('dd_dest')
-    # small StringIO path
-    assert(system("dd", "if=#{@random.path}", "of=#{tmp.path}",
-                        "bs=1024", "count=1"),
-           "dd #@random to #{tmp}")
-    sha1_re = %r!\b([a-f0-9]{40})\b!
-    sha1_out = `sha1sum #{tmp.path}`
-    assert $?.success?, 'sha1sum ran OK'
-
-    assert_match(sha1_re, sha1_out)
-    sha1 = sha1_re.match(sha1_out)[1]
-    resp = `curl -H 'X-Expect-Size: #{tmp.size}' --tcp-nodelay \
-            -isSf --no-buffer -T- http://#@addr:#@port/ < #{tmp.path}`
-    assert $?.success?, 'curl ran OK'
-    assert_match(%r!\b#{sha1}\b!, resp)
-    assert_match(/sysread_read_byte_match/, resp)
-    assert_match(/expect_size_match/, resp)
-  end
-
-  private
-
-  def length
-    @bs * @count
-  end
-
-  def start_server(app)
-    redirect_test_io do
-      @server = HttpServer.new(app, :listeners => [ "#{@addr}:#{@port}" ] )
-      @server.start
-    end
-  end
-
-end

[-- Attachment #23: 0022-drop-redundant-IO-close_on_exec-false-calls.patch --]
[-- Type: text/x-diff, Size: 891 bytes --]

From 841b9e756beb1aa00d0f89097a808adcbbf45397 Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:51 +0000
Subject: [PATCH 22/23] drop redundant IO#close_on_exec=false calls

Passing the `{ FD => IO }' mapping to #spawn or #exec already
ensures Ruby will clear FD_CLOEXEC on these FDs before execve(2).
---
 lib/unicorn/http_server.rb | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 348e745..dd92b38 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -472,10 +472,7 @@ def worker_spawn(worker)
 
   def listener_sockets
     listener_fds = {}
-    LISTENERS.each do |sock|
-      sock.close_on_exec = false
-      listener_fds[sock.fileno] = sock
-    end
+    LISTENERS.each { |sock| listener_fds[sock.fileno] = sock }
     listener_fds
   end
 

[-- Attachment #24: 0023-LISTEN_FDS-inherited-sockets-are-immortal-across-SIG.patch --]
[-- Type: text/x-diff, Size: 3229 bytes --]

From 6ff8785c9277c5978e6dc01cb1b3da25d6bae2db Mon Sep 17 00:00:00 2001
From: Eric Wong <BOFH@YHBT.net>
Date: Mon, 5 Jun 2023 10:12:52 +0000
Subject: [PATCH 23/23] LISTEN_FDS-inherited sockets are immortal across SIGHUP

When using systemd-style socket activation, consider the
inherited socket immortal and do not drop it on SIGHUP.
This means configs w/o any `listen' directives at all can
continue to work after SIGHUP.

I only noticed this while writing some tests in Perl 5 and
the test suite is two lines shorter to test this feature :>
---
 lib/unicorn/http_server.rb  | 7 ++++++-
 t/client_body_buffer_size.t | 1 -
 t/integration.t             | 1 -
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index dd92b38..f1b4a54 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -77,6 +77,7 @@ def initialize(app, options = {})
     options[:use_defaults] = true
     self.config = Unicorn::Configurator.new(options)
     self.listener_opts = {}
+    @immortal = [] # immortal inherited sockets from systemd
 
     # We use @self_pipe differently in the master and worker processes:
     #
@@ -158,6 +159,7 @@ def listeners=(listeners)
     end
     set_names = listener_names(listeners)
     dead_names.concat(cur_names - set_names).uniq!
+    dead_names -= @immortal.map { |io| sock_name(io) }
 
     LISTENERS.delete_if do |io|
       if dead_names.include?(sock_name(io))
@@ -807,17 +809,20 @@ def inherit_listeners!
     # inherit sockets from parents, they need to be plain Socket objects
     # before they become Kgio::UNIXServer or Kgio::TCPServer
     inherited = ENV['UNICORN_FD'].to_s.split(',')
+    immortal = []
 
     # emulate sd_listen_fds() for systemd
     sd_pid, sd_fds = ENV.values_at('LISTEN_PID', 'LISTEN_FDS')
     if sd_pid.to_i == $$ # n.b. $$ can never be zero
       # 3 = SD_LISTEN_FDS_START
-      inherited.concat((3...(3 + sd_fds.to_i)).to_a)
+      immortal = (3...(3 + sd_fds.to_i)).to_a
+      inherited.concat(immortal)
     end
     # to ease debugging, we will not unset LISTEN_PID and LISTEN_FDS
 
     inherited.map! do |fd|
       io = Socket.for_fd(fd.to_i)
+      @immortal << io if immortal.include?(fd)
       io.autoclose = false
       io = server_cast(io)
       set_server_sockopt(io, listener_opts[sock_name(io)])
diff --git a/t/client_body_buffer_size.t b/t/client_body_buffer_size.t
index b1a99f3..3067f28 100644
--- a/t/client_body_buffer_size.t
+++ b/t/client_body_buffer_size.t
@@ -36,7 +36,6 @@ POSIX::mkfifo($fifo, 0600) or die "mkfifo: $!";
 seek($conf_fh, 0, SEEK_SET);
 truncate($conf_fh, 0);
 print $conf_fh <<EOM;
-listen "$host_port" # TODO: remove this requirement for SIGHUP
 after_fork { |_,_| File.open('$fifo', 'w') { |fp| fp.write "pid=#\$\$" } }
 EOM
 $ar->do_kill('HUP');
diff --git a/t/integration.t b/t/integration.t
index a568758..bb2ab51 100644
--- a/t/integration.t
+++ b/t/integration.t
@@ -17,7 +17,6 @@ my $u1 = "$tmpdir/u1";
 print $conf_fh <<EOM;
 early_hints true
 listen "$u1"
-listen "$host_port" # TODO: remove this requirement for SIGHUP
 EOM
 my $ar = unicorn(qw(-E none t/integration.ru -c), $conf, { 3 => $srv });
 my $curl = which('curl');

^ permalink raw reply related	[relevance 1%]

* Re: [PATCH] Master promotion with SIGURG (CoW optimization)
  2022-07-08  6:22  3%           ` Jean Boussier
@ 2022-09-21 22:16  0%             ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2022-09-21 22:16 UTC (permalink / raw)
  To: Jean Boussier; +Cc: unicorn-public

Jean Boussier <jean.boussier@shopify.com> wrote:
> Oh, that's what I missed, that makes sense. I'll try the C wrapper
> to send both the message and the IO at the same time.
> 
> Thanks again, I'll likely get back to you next week with a patch.

Btw, did you ever get anywhere with this?  I'll have to cut a
release at some point soonish since rack 3.0 was released
earlier this month and there's incompatibilities :<

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] Master promotion with SIGURG (CoW optimization)
  @ 2022-07-08  6:22  3%           ` Jean Boussier
  2022-09-21 22:16  0%             ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jean Boussier @ 2022-07-08  6:22 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

> (fully quoting original message for archival purposes, original
> was sent privately)

Sorry for this. Again our company laptops got locked down recently
and now I'm just struggling with shitty mail clients I don't know how to use.


> I don't think Ruby 2.3+ is even necessary, just yet, nor kgio.
> None of these new code paths should be exception-intensive to be
> a performance problem.

True.


> > If anything, the reliable timeout is the number one reason why I still
> > believe unicorn
> > is the superior server out there.
>
> /me hides under a desk :<
>
> Honestly, you have no idea how embarrased I am of that (mis)feature.

It's a bit off topic, but really you shouldn't. A proper timeout mechanism
is absolutely essential to ensure resiliency of the service.

Unless your app is really trivial, it's pretty much impossible to ensure
that all your endpoints will always complete in a reasonable amount
of time, even more so when you have malicious actors trying to DOS
you, or some bugs in database clients and such.

Killing these stuck workers allows to keep the service healthy until the
problem is fixed. The only thing we changed is that we first send SIGTERM
so that we can report the backtrace and other debug data.

> send pipes or any other IO objects across.

Oh, that's what I missed, that makes sense. I'll try the C wrapper
to send both the message and the IO at the same time.

Thanks again, I'll likely get back to you next week with a patch.

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] Master promotion with SIGURG (CoW optimization)
  2022-07-06  7:40  0%   ` Jean Boussier
@ 2022-07-07 10:23  0%     ` Eric Wong
       [not found]           ` <CANPRWbHTNiEcYq5qhN6Kio8Wg9a+2gXmc2bAcB2oVw4LZv8rcw@mail.gmail.com>
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2022-07-07 10:23 UTC (permalink / raw)
  To: Jean Boussier; +Cc: unicorn-public

Jean Boussier <jean.boussier@shopify.com> wrote:
> > OK, any numbers from Puma users which can be used to project
> > improvements for unicorn (and other servers)?
> 
> There are some user reports here: https://github.com/puma/puma/issues/2258
> but they are mixed in reports for two other new featurs.
> 
> Some reports are up to 20-30% savings, but I'd expect unicorn to benefit
> even more from it, given that typical puma users spawn less processes
> than with unicorn.

OK, those numbers sound very good (but I can't view
graphics/screenshots from w3m)

> > > They also include automatic reforking after a predetermined amount
> > > of requests (1k by default).
> >
> > 1k seems like a lot of requests (more on this below)
> 
> Agreed. Shared pages start being invalidated much faster than that.
> 
> > > - master: on `SIGURG`
> > >   - Forward `SIGURG` to a chosen worker.
> >
> > OK, I guess SIGURG is safe to use since nobody else relies on it (right?).
> 
> That's my understanding, an alternative could be to re-use USR2 and have a
> config flag to define wether it is a rexec or refork.

SIGURG is fine, having SIGUSR2 mean two things seems fragile and error-prone

> > Right.  PID file races and corner cases were painful to deal
> > with in the earliest days of this project, and I don't look
> > forward to having to deal with them ever again...
> >
> > It looked like the world was moving towards socket-activation
> > with dedicated process managers and away from fragile PID files;
> > so being self-daemonization dependent seems like a step back.
> 
> Agreed. We're currently running unicorn as PID 1 inside containers
> and I'm not exactly looking forward to have to minitor a PID file.
> 
> Another avenue I explored was to keep the existing master and
> refork from one of the worker like puma, but re-assign the parent
> to the orignal master using PR_SET_CHILD_SUBREAPER.
> 
> Here's my notes of how it works:
> 
> ### Requirements:
> 
> - Need `PR_SET_CHILD_SUBREAPER`, so Linux 3.4+ only (May 2012)

I'm fine with requiring Linux for this feature, existing
features need to continue working on *BSD...

> - Need `File.mkfifo`, so Ruby 2.3 (Dec 2015), but can be shimed for older
>   rubies.

I don't think it's necessary to use FIFOs at all (see below)

> ### Flow:
> 
> - master: set `PR_SET_CHILD_SUBREAPER` during boot.
> - master: create a temporary directory (`$TMP`).
> - master: spawn initial workers the old way.
> 
> - master: on `SIGCHLD` (a child died):
>   - Fake signal oldest worker (`SIGURG`).
>   - Write the new worker number in the fake signal pipe (at the same time).
> 
> - worker: on `SIGURG` (should spawn a sibling):
>   - Note: worker fake signals are processed after the current request is
>     completed.
>   - Run `before_fork` callback (MAYBE: need a special `before_refork`
>     callback?)
>   - create a pipe.
>   - daemonize (fork twice so that the new process is attributed to the
>     nearest `CHILD_SUBREAPER`, aka the original master).
>   - wait for the first child to exit.
>   - write into the pipe to signal the parent is dead.
>   - Run `after_fork` callback (MAYBE: need a special `after_refork` callback?)

*_fork callbacks already work for your current patch, though, right?;
so hopefully *_refork won't be needed...

> - new sibling after fork:
>   - wait for the parent to exit (though the temporary pipe).
>   - create a named pipe (`File.mkfifo`) at `$TMP/$WORKER_NUM.pipe`.
>   - create a pidfile at `$TMP/WORKER_NUM.pid`.
>   - Open the named pipe in `IO::RDONLY | IO::NONBLOCK` (otherwise it would
>     block until the master open in write mode).
>     - NB: Need to convert it to a `Kgio::Pipe` with
>       `Kgio::Pipe.for_fd(f.to_i)`, and keep `f` need `autoclose = false`.

sidenote: no need for kgio, I've been meaning to get rid of it
(Ruby 2.3+ has everything we'd need, I think...)

>   - Create the `Unicorn::Worker` instance with that pipe and worker number.
>   - Send `SIGURG` to the parent process (should be the master).
>   - Wait for `SIGCONT` in the named pipe.
>   - enter the worker loop.
> 
> - master: on `SIGURG`:
>   - for each outstanding refork order:
>     - Look for `$TMP/$WORKER_NUM.pid` and `$TMP/$WORKER_NUM.pipe`
>     - Read the pidfile
>     - Open the pipe with `IO::RDONLY | IO::NONBLOCK`
>       - NB: Need to convert it to a `Kgio::Pipe` with
>         `Kgio::Pipe.for_fd(f.to_i)`, and keep `f` need `autoclose = false`.
>     - Delete pidfile and named pipe.
>     - Register that new worker normally.
>     - Write `SIGCONT` into the pipe.
> 
> I can share the patch if you are interested, but the extra complexity
> and Linux only features made me prefer the master-promotion approach.

I would prefer this Linux-only approach if it gets rid of PID
files and doesn't introduce new temporary files/FIFOs.

It seems socketpair(..., SOCK_SEQPACKET) can be used for
packetized bidirectional IPC, perhaps with send_io + recv_io iff
necessary.  There shouldn't be any need for new, fragile FS
interactions.


Another idea, have you considered letting master work on some requests?
Signal handling would be delayed, and EINTR handling bugs in
gems could be exposed, but it'd be completely portable...

> > > However it work well enough for demonstration.
> > >
> > > So I figured I'd ask wether the feature is desired upstream
> > > before investing more effort into it.
> >
> > It really depends on:
> >
> > 1) what memory savings and/or speedups can be achieved
> >
> > 2) what support can we expect from you (and other users) for
> >    this feature and potential regressions going forward?
> >
> > I don't have the free time nor mental capacity to deal with
> > fallout from major new features such as this, myself.
> 
> Yeah, this is just a RFC, I wouldn't submit a final patch without
> first deploying it on our infra with good results. I'm just on the
> fence between trying to get this upstream vs maintaining our own
> server that solely work like this, hence somewhat simpler and that
> can make more assumptions.

Of course you also realize unicorn remains culturally-incompatible
with 99.9% of the open source world since I refuse to rely on
graphics drivers to work on a daemon, proprietary software,
terms-of-service, etc...

If anything goes wrong, I'd likely CC you anyways.

> > The hook interface would be yet another documentation+support
> > burden I'm worried about (and also more unicorn-specific stuff
> > to lock people in :<).
> 
> Understandable. I suppose it can be done with a monitoring process.
> Check `smaps_rollup` and send `SIGURG` when deemed necessary.
> 
> More moving pieces but keep unicorn simpler.

*shrug*  I've also been favoring more vertical integration in
other places to keep the overall stack simpler.

It depends on whether the monitoring process/library can work
with other Rack servers, probably; and signal compatibility.

> > A completely different idea which should achieve the same goal,
> > completely independent of unicorn:
> >
> >   App startup/loading can be made to trigger warmup paths which
> >   hit common endpoints.  IOW, app loading could run a small
> >   warmup suite which simulates common requests to heat up caches
> >   and trigger JIT.
> 
> Yeah, I don't really believe in this for a few reasons:
> 
>   - That would slow boot time.

Yes, and startup time is already a nasty thing, anyways...

>   - On large apps there's just too many codepath for this to be realistic.

I was thinking a lazy solution could be to have some middleware
measure coverage and log requests to support auto-replay, somehow.

>   - Many endpoints require database and other network access you probably
>     don't want in the master.

Wouldn't the *_fork hooks handle that, anyways?

>   - Endpoints may have side effects.

Yeah, this would optimize the GET endpoints, at least.
Not sure what percentage of POST needs to be optimized...

> > Ultimately (long-term for ruby-core), it would be better to make
> > JIT (and perhaps even VM cache) results persistent and shareable
> > across different processes, somehow...  That would help all Ruby
> > tools, even short-lived CLI tools.  Portable locking would be a
> > pain, I suppose, but proprietary OSes are always a pain :P
> 
> Based on my limited understanding of both JIT and VM caches, I don't think
> that's really possible.

Ugh, I still think it can be made possible, somehow.  But I no longer
use Ruby for new projects...

> The VM itself could definitely do better CoW wise, and I have some proposals
> on that front (https://bugs.ruby-lang.org/issues/18885) but that will take
> time and will never be perfect.
> 
> That's why I think periodically promoting a new master has potential.
> 
> > There's some line-wrapping issues caused by your MUA;
> 
> Urk. Ok, trying another client now, and I'll resend the patch.

Nope, still wrapped.  According to the git-format-patch(1) manpage,
Thunderbird can work w/o extensions, actually.

IME attachments might work mostly well, nowadays (test sending
to yourself and using "git am" to apply, first, of course).
Most on vger will disagree with me on attachments, though...

"git send-email" and "git imap-send" remain the recommended
options, but mutt works well, too.

> > Perhaps documenting this as experimental and subject to removal
> > at any time would make the addition of a major new feature an
> > easier pill to swallow; but I still think this is better outside
> > of app servers.
> 
> Up to you, if you don't feel like maintaining such a feature I would
> perfectly understand.

I'm fine with "maintaining it" if it just means Cc-ing you on
bugs related to this :>

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] Master promotion with SIGURG (CoW optimization)
  2022-07-06  2:33  1% ` Eric Wong
@ 2022-07-06  7:40  0%   ` Jean Boussier
  2022-07-07 10:23  0%     ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jean Boussier @ 2022-07-06  7:40 UTC (permalink / raw)
  Cc: unicorn-public

> OK, any numbers from Puma users which can be used to project
> improvements for unicorn (and other servers)?

There are some user reports here: https://github.com/puma/puma/issues/2258
but they are mixed in reports for two other new featurs.

Some reports are up to 20-30% savings, but I'd expect unicorn to benefit
even more from it, given that typical puma users spawn less processes
than with unicorn.

> > They also include automatic reforking after a predetermined amount
> > of requests (1k by default).
>
> 1k seems like a lot of requests (more on this below)

Agreed. Shared pages start being invalidated much faster than that.

> > - master: on `SIGURG`
> >   - Forward `SIGURG` to a chosen worker.
>
> OK, I guess SIGURG is safe to use since nobody else relies on it (right?).

That's my understanding, an alternative could be to re-use USR2 and have a
config flag to define wether it is a rexec or refork.

> Right.  PID file races and corner cases were painful to deal
> with in the earliest days of this project, and I don't look
> forward to having to deal with them ever again...
>
> It looked like the world was moving towards socket-activation
> with dedicated process managers and away from fragile PID files;
> so being self-daemonization dependent seems like a step back.

Agreed. We're currently running unicorn as PID 1 inside containers
and I'm not exactly looking forward to have to minitor a PID file.

Another avenue I explored was to keep the existing master and
refork from one of the worker like puma, but re-assign the parent
to the orignal master using PR_SET_CHILD_SUBREAPER.

Here's my notes of how it works:

### Requirements:

- Need `PR_SET_CHILD_SUBREAPER`, so Linux 3.4+ only (May 2012)
- Need `File.mkfifo`, so Ruby 2.3 (Dec 2015), but can be shimed for older
  rubies.

### Flow:

- master: set `PR_SET_CHILD_SUBREAPER` during boot.
- master: create a temporary directory (`$TMP`).
- master: spawn initial workers the old way.

- master: on `SIGCHLD` (a child died):
  - Fake signal oldest worker (`SIGURG`).
  - Write the new worker number in the fake signal pipe (at the same time).

- worker: on `SIGURG` (should spawn a sibling):
  - Note: worker fake signals are processed after the current request is
    completed.
  - Run `before_fork` callback (MAYBE: need a special `before_refork`
    callback?)
  - create a pipe.
  - daemonize (fork twice so that the new process is attributed to the
    nearest `CHILD_SUBREAPER`, aka the original master).
  - wait for the first child to exit.
  - write into the pipe to signal the parent is dead.
  - Run `after_fork` callback (MAYBE: need a special `after_refork` callback?)

- new sibling after fork:
  - wait for the parent to exit (though the temporary pipe).
  - create a named pipe (`File.mkfifo`) at `$TMP/$WORKER_NUM.pipe`.
  - create a pidfile at `$TMP/WORKER_NUM.pid`.
  - Open the named pipe in `IO::RDONLY | IO::NONBLOCK` (otherwise it would
    block until the master open in write mode).
    - NB: Need to convert it to a `Kgio::Pipe` with
      `Kgio::Pipe.for_fd(f.to_i)`, and keep `f` need `autoclose = false`.
  - Create the `Unicorn::Worker` instance with that pipe and worker number.
  - Send `SIGURG` to the parent process (should be the master).
  - Wait for `SIGCONT` in the named pipe.
  - enter the worker loop.

- master: on `SIGURG`:
  - for each outstanding refork order:
    - Look for `$TMP/$WORKER_NUM.pid` and `$TMP/$WORKER_NUM.pipe`
    - Read the pidfile
    - Open the pipe with `IO::RDONLY | IO::NONBLOCK`
      - NB: Need to convert it to a `Kgio::Pipe` with
        `Kgio::Pipe.for_fd(f.to_i)`, and keep `f` need `autoclose = false`.
    - Delete pidfile and named pipe.
    - Register that new worker normally.
    - Write `SIGCONT` into the pipe.

I can share the patch if you are interested, but the extra complexity
and Linux only features made me prefer the master-promotion approach.

> > However it work well enough for demonstration.
> >
> > So I figured I'd ask wether the feature is desired upstream
> > before investing more effort into it.
>
> It really depends on:
>
> 1) what memory savings and/or speedups can be achieved
>
> 2) what support can we expect from you (and other users) for
>    this feature and potential regressions going forward?
>
> I don't have the free time nor mental capacity to deal with
> fallout from major new features such as this, myself.

Yeah, this is just a RFC, I wouldn't submit a final patch without
first deploying it on our infra with good results. I'm just on the
fence between trying to get this upstream vs maintaining our own
server that solely work like this, hence somewhat simpler and that
can make more assumptions.

> The hook interface would be yet another documentation+support
> burden I'm worried about (and also more unicorn-specific stuff
> to lock people in :<).

Understandable. I suppose it can be done with a monitoring process.
Check `smaps_rollup` and send `SIGURG` when deemed necessary.

More moving pieces but keep unicorn simpler.

> A completely different idea which should achieve the same goal,
> completely independent of unicorn:
>
>   App startup/loading can be made to trigger warmup paths which
>   hit common endpoints.  IOW, app loading could run a small
>   warmup suite which simulates common requests to heat up caches
>   and trigger JIT.

Yeah, I don't really believe in this for a few reasons:

  - That would slow boot time.
  - On large apps there's just too many codepath for this to be realistic.
  - Many endpoints require database and other network access you probably
    don't want in the master.
  - Endpoints may have side effects.

> Ultimately (long-term for ruby-core), it would be better to make
> JIT (and perhaps even VM cache) results persistent and shareable
> across different processes, somehow...  That would help all Ruby
> tools, even short-lived CLI tools.  Portable locking would be a
> pain, I suppose, but proprietary OSes are always a pain :P

Based on my limited understanding of both JIT and VM caches, I don't think
that's really possible.

The VM itself could definitely do better CoW wise, and I have some proposals
on that front (https://bugs.ruby-lang.org/issues/18885) but that will take
time and will never be perfect.

That's why I think periodically promoting a new master has potential.

> There's some line-wrapping issues caused by your MUA;

Urk. Ok, trying another client now, and I'll resend the patch.

> Perhaps documenting this as experimental and subject to removal
> at any time would make the addition of a major new feature an
> easier pill to swallow; but I still think this is better outside
> of app servers.

Up to you, if you don't feel like maintaining such a feature I would
perfectly understand.

---
 SIGNALS                        |   4 ++
 lib/unicorn.rb                 |   2 +-
 lib/unicorn/http_server.rb     | 115 +++++++++++++++++++++++++--------
 lib/unicorn/promoted_worker.rb |  40 ++++++++++++
 4 files changed, 134 insertions(+), 27 deletions(-)
 create mode 100644 lib/unicorn/promoted_worker.rb

diff --git a/SIGNALS b/SIGNALS
index 7321f2b..f5716b9 100644
--- a/SIGNALS
+++ b/SIGNALS
@@ -39,6 +39,10 @@ https://yhbt.net/unicorn/examples/init.sh
 * WINCH - gracefully stops workers but keep the master running.
   This will only work for daemonized processes.

+* URG - promote one of the existing workers as a new master, and gracefully
+  stop workers.
+  This will only work for daemonized processes.
+
 * TTIN - increment the number of worker processes by one

 * TTOU - decrement the number of worker processes by one
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index 1a50631..832f78d 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -133,6 +133,6 @@ def self.pipe # :nodoc:
 # :enddoc:

 %w(const socket_helper stream_input tee_input http_request configurator
-   tmpio util http_response worker http_server).each do |s|
+   tmpio util http_response worker promoted_worker http_server).each do |s|
   require_relative "unicorn/#{s}"
 end
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 21f2a05..dd8f021 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -50,6 +50,7 @@ class Unicorn::HttpServer
   #   Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.3.0/bin/unicorn"
   START_CTX = {
     :argv => ARGV.map(&:dup),
+    :generation => 0,
     0 => $0.dup,
   }
   # We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
@@ -106,7 +107,7 @@ def initialize(app, options = {})
     @orig_app = app
     # list of signals we care about and trap in master.
     @queue_sigs = [
-      :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
+      :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU, :URG ]

     @worker_data = if worker_data = ENV['UNICORN_WORKER']
       worker_data = worker_data.split(',').map!(&:to_i)
@@ -119,7 +120,24 @@ def initialize(app, options = {})

   # Runs the thing.  Returns self so you can run join on it
   def start
+    @new_master = false
     inherit_listeners!
+    promote
+
+    # write pid early for Mongrel compatibility if we're not inheriting sockets
+    # This is needed for compatibility some Monit setups at least.
+    # This unfortunately has the side effect of clobbering valid PID if
+    # we upgrade and the upgrade breaks during preload_app==true && build_app!
+    self.pid = config[:pid]
+
+    build_app! if preload_app
+    bind_new_listeners!
+
+    spawn_missing_workers
+    self
+  end
+
+  def promote
     # this pipe is used to wake us up from select(2) in #join when signals
     # are trapped.  See trap_deferred.
     @self_pipe.replace(Unicorn.pipe)
@@ -130,18 +148,6 @@ def start
     # Note that signals don't actually get handled until the #join method
     @queue_sigs.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
     trap(:CHLD) { awaken_master }
-
-    # write pid early for Mongrel compatibility if we're not inheriting sockets
-    # This is needed for compatibility some Monit setups at least.
-    # This unfortunately has the side effect of clobbering valid PID if
-    # we upgrade and the upgrade breaks during preload_app==true && build_app!
-    self.pid = config[:pid]
-
-    build_app! if preload_app
-    bind_new_listeners!
-
-    spawn_missing_workers
-    self
   end

   # replaces current listener set with +listeners+.  This will
@@ -178,16 +184,16 @@ def logger=(obj)
     Unicorn::HttpRequest::DEFAULTS["rack.logger"] = @logger = obj
   end

-  def clobber_pid(path)
+  def clobber_pid(path, content = $$)
     unlink_pid_safe(@pid) if @pid
     if path
       fp = begin
-        tmp = "#{File.dirname(path)}/#{rand}.#$$"
+        tmp = "#{File.dirname(path)}/#{rand}.#{content}"
         File.open(tmp, File::RDWR|File::CREAT|File::EXCL, 0644)
       rescue Errno::EEXIST
         retry
       end
-      fp.syswrite("#$$\n")
+      fp.syswrite("#{content}\n")
       File.rename(fp.path, path)
       fp.close
     end
@@ -279,6 +285,11 @@ def join
       end
       @ready_pipe = @ready_pipe.close rescue nil
     end
+
+    if @promoted
+      Process.kill(:WINCH, Process.ppid)
+    end
+
     begin
       reap_all_workers
       case @sig_queue.shift
@@ -292,6 +303,13 @@ def join
           @logger.debug("waiting #{sleep_time}s after suspend/hibernation")
         end
         maintain_worker_count if respawn
+
+        if @new_master && @new_master.ready? && @workers.empty?
+          # TODO: we should handle the new master dying like with reexec.
+          clobber_pid(pid, @new_master.pid)
+          break
+        end
+
         master_sleep(sleep_time)
       when :QUIT # graceful shutdown
         break
@@ -305,10 +323,20 @@ def join
         soft_kill_each_worker(:USR1)
       when :USR2 # exec binary, stay alive in case something went wrong
         reexec
+      when :URG
+        if $stdin.tty?
+          logger.info "SIGURG ignored because we're not daemonized"
+        else
+          promote_new_master
+        end
       when :WINCH
         if $stdin.tty?
           logger.info "SIGWINCH ignored because we're not daemonized"
         else
+          if @new_master
+            @new_master.ready!
+          end
+
           respawn = false
           logger.info "gracefully stopping all workers"
           soft_kill_each_worker(:QUIT)
@@ -408,11 +436,30 @@ def reap_all_workers
         worker = @workers.delete(wpid) and worker.close rescue nil
         @after_worker_exit.call(self, worker, status)
       end
+
+      if @new_master && @new_master.ready?
+        @new_master.scale(@workers.size)
+      end
     rescue Errno::ECHILD
       break
     end while true
   end

+  def promote_new_master
+    # Promoting the oldest worker
+    # TODO: handle `@new_master` being dead.
+    if @new_master
+      logger.error "can't promote because worker=#{@new_master.nr} is
being promoted"
+    elsif pair = @workers.first
+      @new_master = Unicorn::PromotedWorker.new(*pair, worker_processes)
+      @workers.delete(@new_master.pid)
+      logger.info "master promoting worker=#{@new_master.worker.nr}"
+      @new_master.promote
+    else
+      logger.error "can't promote because there is no existing workers"
+    end
+  end
+
   # reexecutes the START_CTX with a new binary
   def reexec
     if @reexec_pid > 0
@@ -516,10 +563,11 @@ def murder_lazy_workers
   end

   def after_fork_internal
+    self.worker_processes = 0
     @self_pipe.each(&:close).clear # this is master-only, now
     @ready_pipe.close if @ready_pipe
     Unicorn::Configurator::RACKUP.clear
-    @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
+    @ready_pipe = nil

     # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
     # dying workers can recycle pids
@@ -545,6 +593,13 @@ def spawn_missing_workers
       unless pid
         after_fork_internal
         worker_loop(worker)
+
+        if @promoted
+          worker.tick = 0
+          promote
+          join
+        end
+
         exit
       end

@@ -678,19 +733,22 @@ def init_worker_process(worker)
     trap(:CHLD, 'DEFAULT')
     @sig_queue.clear
     proc_name "worker[#{worker.nr}]"
-    START_CTX.clear
     @workers.clear

     after_fork.call(self, worker) # can drop perms and create listeners
     LISTENERS.each { |sock| sock.close_on_exec = true }

     worker.user(*user) if user.kind_of?(Array) && ! worker.switched
-    @config = nil
     build_app! unless preload_app
-    @after_fork = @listener_opts = @orig_app = nil
+    @listener_opts = @orig_app = nil
     readers = LISTENERS.dup
     readers << worker
     trap(:QUIT) { nuke_listeners!(readers) }
+    @promoted = false
+    trap(:URG) do
+      @promoted = true
+      START_CTX[:generation] += 1
+    end
     readers
   end

@@ -706,11 +764,11 @@ def reopen_worker_logs(worker_nr)

   def prep_readers(readers)
     wtr = Unicorn::Waiter.prep_readers(readers)
-    @timeout *= 500 # to milliseconds for epoll, but halved
+    @select_timeout = @timeout * 500 # to milliseconds for epoll, but halved
     wtr
   rescue
     require_relative 'select_waiter'
-    @timeout /= 2.0 # halved for IO.select
+    @select_timeout = @timeout / 2.0 # halved for IO.select
     Unicorn::SelectWaiter.new
   end

@@ -720,7 +778,7 @@ def prep_readers(readers)
   def worker_loop(worker)
     readers = init_worker_process(worker)
     waiter = prep_readers(readers)
-    reopen = false
+    promote = reopen = false

     # this only works immediately if the master sent us the signal
     # (which is the normal case)
@@ -739,12 +797,17 @@ def worker_loop(worker)
           process_client(client)
           worker.tick = time_now.to_i
         end
+        if @promoted
+          worker.tick = time_now.to_i
+          return
+        end
+
         break if reopen
       end

       # timeout so we can .tick and keep parent from SIGKILL-ing us
       worker.tick = time_now.to_i
-      waiter.get_readers(ready, readers, @timeout)
+      waiter.get_readers(ready, readers, @select_timeout)
     rescue => e
       redo if reopen && readers[0]
       Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
@@ -823,8 +886,8 @@ def build_app!
   end

   def proc_name(tag)
-    $0 = ([ File.basename(START_CTX[0]), tag
-          ]).concat(START_CTX[:argv]).join(' ')
+    $0 = ([ File.basename(START_CTX[0]), tag, "(gen:
#{START_CTX[:generation]})",
+          ]).concat(START_CTX[:argv]).compact.join(' ')
   end

   def redirect_io(io, path)
diff --git a/lib/unicorn/promoted_worker.rb b/lib/unicorn/promoted_worker.rb
new file mode 100644
index 0000000..2595182
--- /dev/null
+++ b/lib/unicorn/promoted_worker.rb
@@ -0,0 +1,40 @@
+# -*- encoding: binary -*-
+
+class Unicorn::PromotedWorker
+  attr_reader :pid, :worker
+
+  def initialize(pid, worker, expected_worker_processes)
+    @pid = pid
+    @worker = worker
+    @worker_processes = 0
+    @expected_worker_processes = expected_worker_processes
+    @ready = false
+  end
+
+  def ready?
+    @ready
+  end
+
+  def ready!
+    @ready = true
+  end
+
+  def promote
+    @worker.soft_kill(:URG)
+  end
+
+  def scale(old_master_worker_processes)
+    diff = @expected_worker_processes -
+      old_master_worker_processes -
+      @worker_processes
+
+    if diff > 0
+      diff.times { kill(:TTIN) }
+      @worker_processes += diff
+    end
+  end
+
+  def kill(sig)
+    Process.kill(sig, @pid)
+  end
+end
-- 
2.35.1

^ permalink raw reply related	[relevance 0%]

* Re: [PATCH] Master promotion with SIGURG (CoW optimization)
  @ 2022-07-06  2:33  1% ` Eric Wong
  2022-07-06  7:40  0%   ` Jean Boussier
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2022-07-06  2:33 UTC (permalink / raw)
  To: Jean Boussier; +Cc: unicorn-public

Jean Boussier <jean.boussier@shopify.com> wrote:
> Unicorn rely on Copy on Write to limit applications' memory usage,
> however Ruby Copy on Write performance isn't exactly perfect.
> As code get executed various internal performance caches get warmed
> which cause shared pages to be invalidated.
> 
> While there's certainly some improvements to be made to Ruby itself
> on that front, it will likely get worse in the near future if YJIT
> become popular, as it will generate executable code in the workers
> hence won't be shared.
> 
> One way effective CoW performance could be improved would be to
> periodically promote one worker as the new master. Since that worker
> processed some requests, VM caches, JITed code, etc should be somewhat
> warm already, hence shared pages should be dirtied less frequently after
> each promotion.

Agreed, there's potential for savings, here...

> Puma 5.0.0 introduced a similar experimental feature called
> `fork_worker` or `refork`.
> 
> It's a bit more limited though, as instead of promoting a new
> master and exiting, they only ask worker #0 to fork itself to
> replace its siblings. So once all workers are replaces, there
> is 3 levels of processes: cluster -> first_worker -> other_workers.

OK, any numbers from Puma users which can be used to project
improvements for unicorn (and other servers)?

> They also include automatic reforking after a predetermined amount
> of requests (1k by default).

1k seems like a lot of requests (more on this below)

> The happy path works pretty much like this:
> 
> - Assume daemonized mode.
> 
> - master: on `SIGURG`
>   - Forward `SIGURG` to a chosen worker.

OK, I guess SIGURG is safe to use since nobody else relies on it (right?).

> - worker: on `SIGURG` (become a master)
>   - do the prep, register traps, open self-pipe, etc
>   - don't spawn any worker, set number of wokers to 0.
>   - send `SIGWHINCH` to master.
> 
> - old master: on `SIGWHINCH`
>   - Gracefully shutdown workers, like during a reexec.

nit: only one `H' in `SIGWINCH`

> - old master: when a worker is reaped.
>   - Send `SIGTTIN` to the new master
>   - If it was the last worker:
>     - replace pidfile
>     - exit
> 
> This patch is not exactly production quality yet:
> 
>   - I need to write some tests
>   - There's a race condition that can cause the promoted master
>     master to have one less worker than required. Need to be addressed.
>   - The pidfile replacement should be improved.
>   - Multiple corner cases like a `QUIT` during promotion are not handled.

Right.  PID file races and corner cases were painful to deal
with in the earliest days of this project, and I don't look
forward to having to deal with them ever again...

It looked like the world was moving towards socket-activation
with dedicated process managers and away from fragile PID files;
so being self-daemonization dependent seems like a step back.

> However it work well enough for demonstration.
> 
> So I figured I'd ask wether the feature is desired upstream
> before investing more effort into it.

It really depends on:

1) what memory savings and/or speedups can be achieved

2) what support can we expect from you (and other users) for
   this feature and potential regressions going forward?

I don't have the free time nor mental capacity to deal with
fallout from major new features such as this, myself.

> For this to be used in production without too much integration effort
> I think automatic promotion based on some criteria like number of
> request or process lifetime would be needed.
> 
> Ideally a hook interface to programatically trigger promotion would
> be very valuable as I'd likely want to trigger promotion
> based on memory metrics read from `/proc/<pid>/smaps_rollup`.

The hook interface would be yet another documentation+support
burden I'm worried about (and also more unicorn-specific stuff
to lock people in :<).


A completely different idea which should achieve the same goal,
completely independent of unicorn:

  App startup/loading can be made to trigger warmup paths which
  hit common endpoints.  IOW, app loading could run a small
  warmup suite which simulates common requests to heat up caches
  and trigger JIT.

  That would be completely self-contained in each app and work
  on every single app server; not just ones with special
  provisions to deal with this.  Of course, CoW-aware setups
  (preload_app) will still have the advantage, here.

  Best of all, it could be deterministic, too, instead of
  waiting and hoping 1k random requests will be sufficient
  warmup, developers can tune and mock exactly the requests
  required for warmup.  Of course, a lazy developer replay 1k
  requests from an old log, too.


Ultimately (long-term for ruby-core), it would be better to make
JIT (and perhaps even VM cache) results persistent and shareable
across different processes, somehow...  That would help all Ruby
tools, even short-lived CLI tools.  Portable locking would be a
pain, I suppose, but proprietary OSes are always a pain :P

>  4 files changed, 134 insertions(+), 27 deletions(-)
>  create mode 100644 lib/unicorn/promoted_worker.rb

There's some line-wrapping issues caused by your MUA;
I got this from "git am":

  warning: Patch sent with format=flowed; space at the end of lines might be lost.
  Applying: Master promotion with SIGURG (CoW optimization)
  error: corrupt patch at line 14

The git-format-patch(1) manpage has a section for Thunderbird users
which may help.

> --- a/SIGNALS
> +++ b/SIGNALS
> @@ -39,6 +39,10 @@ https://yhbt.net/unicorn/examples/init.sh
>  * WINCH - gracefully stops workers but keep the master running.
>    This will only work for daemonized processes.
> 
> +* URG - promote one of the existing workers as a new master, and gracefully
> +  stop workers.
> +  This will only work for daemonized processes.

Perhaps documenting this as experimental and subject to removal
at any time would make the addition of a major new feature an
easier pill to swallow; but I still think this is better outside
of app servers.

^ permalink raw reply	[relevance 1%]

* [PATCH 5/6] worker_loop: get rid of select() avoidance hack
  @ 2021-10-01  3:09  5% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2021-10-01  3:09 UTC (permalink / raw)
  To: unicorn-public

It doesn't seem to do anything since commit 221340c4ebc15666
(prevent single listener from monopolizing a worker, 2020-04-16).
---
 lib/unicorn/http_server.rb | 25 +++++++------------------
 1 file changed, 7 insertions(+), 18 deletions(-)

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 09c5ec2..7f33f98 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -699,6 +699,7 @@ def reopen_worker_logs(worker_nr)
     logger.info "worker=#{worker_nr} reopening logs..."
     Unicorn::Util.reopen_logs
     logger.info "worker=#{worker_nr} done reopening logs"
+    false
   rescue => e
     logger.error(e) rescue nil
     exit!(77) # EX_NOPERM in sysexits.h
@@ -709,19 +710,17 @@ def reopen_worker_logs(worker_nr)
   # given a INT, QUIT, or TERM signal)
   def worker_loop(worker)
     readers = init_worker_process(worker)
-    nr = 0 # this becomes negative if we need to reopen logs
+    reopen = false
 
     # this only works immediately if the master sent us the signal
     # (which is the normal case)
-    trap(:USR1) { nr = -65536 }
+    trap(:USR1) { reopen = true }
 
     ready = readers.dup
-    nr_listeners = readers.size
     @after_worker_ready.call(self, worker)
 
     begin
-      nr < 0 and reopen_worker_logs(worker.nr)
-      nr = 0
+      reopen = reopen_worker_logs(worker.nr) if reopen
       worker.tick = time_now.to_i
       tmp = ready.dup
       while sock = tmp.shift
@@ -729,26 +728,16 @@ def worker_loop(worker)
         # but that will return false
         if client = sock.kgio_tryaccept
           process_client(client)
-          nr += 1
           worker.tick = time_now.to_i
         end
-        break if nr < 0
+        break if reopen
       end
 
-      # 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().
-      if nr == nr_listeners
-        tmp = ready.dup
-        redo
-      end
-
-      # timeout used so we can detect parent death:
+      # timeout so we can .tick and keep parent from SIGKILL-ing us
       worker.tick = time_now.to_i
       ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
     rescue => e
-      redo if nr < 0 && readers[0]
+      redo if reopen && readers[0]
       Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
     end while readers[0]
   end

^ permalink raw reply related	[relevance 5%]

* Bus Error with Unicorn 6.0 on OpenBSD-current (6.9) with Ruby 3.0 in minimal rails application
  2021-04-04 20:03  0%         ` Eric Wong
@ 2021-04-07 21:19  0%           ` vtamara
  0 siblings, 0 replies; 200+ results
From: vtamara @ 2021-04-07 21:19 UTC (permalink / raw)
  To: Eric Wong; +Cc: Jeremy Evans, unicorn-public, ports

Good evening

I found that the problem happens also:

1. In OpenBSD-current (what will be 6.9 soon) --for that reason I copy 
to ports-openbsd
2. With what I consider the minimal rails application,  started with the 
flag --minimal, running with sqlite, i.e:

rails new --minimal ap61

The configuration of gems in /var/www/bundler and the configuration of 
nginx is as I explained in the previous email  
https://yhbt.net/unicorn-public/20210404200358.GA32719@dcvr/T/#mcf469c2eb443db6c9e4fee55afa3b252606c27f2 
but  changing cor1440 with ap61.

In my previous email I forgot some details, like how to create self 
signed certificate and how to install nginx and ruby, here they go:

doas pkg_add nginx
doas pkg_add ruby-3.0.0p0
  ln -sf /usr/local/bin/ruby30 /usr/local/bin/ruby
  ln -sf /usr/local/bin/erb30 /usr/local/bin/erb
  ln -sf /usr/local/bin/irb30 /usr/local/bin/irb
  ln -sf /usr/local/bin/rdoc30 /usr/local/bin/racc
  ln -sf /usr/local/bin/rdoc30 /usr/local/bin/rdoc
  ln -sf /usr/local/bin/rdoc30 /usr/local/bin/rbs
  ln -sf /usr/local/bin/ri30 /usr/local/bin/ri
  ln -sf /usr/local/bin/rake30 /usr/local/bin/rake
  ln -sf /usr/local/bin/gem30 /usr/local/bin/gem
  ln -sf /usr/local/bin/bundle30 /usr/local/bin/bundle
  ln -sf /usr/local/bin/bundler30 /usr/local/bin/bundler
  ln -sf /usr/local/bin/typeprof30 /usr/local/bin/typeprof


# Creating self signed certificate
doas openssl genrsa -out /etc/ssl/private/server.key 2048
openssl req -new -key /etc/ssl/private/server.key \
   -out /etc/ssl/private/server.csr
openssl x509 -req -days 3650 -in /etc/ssl/private/server.csr \
   -signkey /etc/ssl/private/server.key -out /etc/ssl/server.crt

# Installing sqlite3
doas pkg_add sqlite3
doas gem install --install-dir /var/www/bundler/ruby/3.0/ sqlite3

# Installing rails globally
doas gem install rails
doas ln -s /usr/local/bin/rails30 /usr/local/bin/rails

rails new --minimal ap61
mv ap61 /var/www/htdocs/
cd /var/www/htdocs/ap61

# Configuring unicorn
cat <<EOF >> Gemfile
group :production do
   gem 'unicorn'
end
EOF
doas gem install --install-dir /var/www/bundler/ruby/3.0 unicorn
bundle
cat <<EOF > config/unicorn.rb
listen 2011
APP_PATH="/var/www/htdocs/ap61"
working_directory APP_PATH
worker_processes 2
pid APP_PATH + "/tmp/pids/unicorn.pid"
stderr_path APP_PATH + "/log/unicorn.log"
stdout_path APP_PATH + "/log/unicorn.log"
EOF

# Start the application
bundle exec /usr/local/bin/unicorn_rails -c config/unicorn.rb -E 
production -D


# Try to see the initial rails screen
doas pkg_add w3m
w3m https://127.0.0.1/ap61

It will not present the presentation screen, stopping w3m and checking 
the logfile I see the samen Segmentation faults:

I, [2021-04-07T15:27:34.289298 #75098]  INFO -- : listening on 
addr=0.0.0.0:2011 fd=12
I, [2021-04-07T15:27:34.290752 #75098]  INFO -- : worker=0 spawning...
I, [2021-04-07T15:27:34.303205 #75098]  INFO -- : worker=1 spawning...
I, [2021-04-07T15:27:34.308574 #98078]  INFO -- : worker=0 spawned 
pid=98078
I, [2021-04-07T15:27:34.310157 #98078]  INFO -- : Refreshing Gem list
I, [2021-04-07T15:27:34.329730 #75098]  INFO -- : master process ready
I, [2021-04-07T15:27:34.390398 #14713]  INFO -- : worker=1 spawned 
pid=14713
I, [2021-04-07T15:27:34.391984 #14713]  INFO -- : Refreshing Gem list
/var/www/bundler/ruby/3.0/gems/unicorn-6.0.0/lib/unicorn.rb:86: [BUG] 
Segmentation fault at 0x0000000000000000
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-openbsd]



-- Control frame information 
-----------------------------------------------
/var/www/bundler/ruby/3.0/gems/unicorn-6.0.0/lib/unicorn.rb:80: [BUG] 
Segmentation fault at 0x0000000000000001
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-openbsd]


-- Control frame information 
-----------------------------------------------
E, [2021-04-07T15:27:50.430649 #75098] ERROR -- : reaped 
#<Process::Status: pid 98078 SIGSEGV (signal 11) (core dumped)> worker=0
I, [2021-04-07T15:27:50.430976 #75098]  INFO -- : worker=0 spawning...
I, [2021-04-07T15:27:50.439973 #19064]  INFO -- : worker=0 spawned 
pid=19064
I, [2021-04-07T15:27:50.441607 #19064]  INFO -- : Refreshing Gem list
E, [2021-04-07T15:27:51.490681 #75098] ERROR -- : reaped 
#<Process::Status: pid 14713 SIGSEGV (signal 11) (core dumped)> worker=1
I, [2021-04-07T15:27:51.491040 #75098]  INFO -- : worker=1 spawning...
I, [2021-04-07T15:27:51.500370 #66270]  INFO -- : worker=1 spawned 
pid=66270
I, [2021-04-07T15:27:51.501983 #66270]  INFO -- : Refreshing Gem list
/var/www/bundler/ruby/3.0/gems/unicorn-6.0.0/lib/unicorn.rb:80: [BUG] 
Segmentation fault at 0x0000030bbfc8d6f5
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-openbsd]



-- Control frame information 
-----------------------------------------------
/var/www/bundler/ruby/3.0/gems/unicorn-6.0.0/lib/unicorn.rb:80: [BUG] 
Segmentation fault at 0x0000000000000001
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-openbsd]


-- Control frame information 
-----------------------------------------------
E, [2021-04-07T15:28:05.676716 #75098] ERROR -- : reaped 
#<Process::Status: pid 19064 SIGSEGV (signal 11) (core dumped)> worker=0
I, [2021-04-07T15:28:05.677110 #75098]  INFO -- : worker=0 spawning...
I, [2021-04-07T15:28:05.685956 #32548]  INFO -- : worker=0 spawned 
pid=32548
I, [2021-04-07T15:28:05.687658 #32548]  INFO -- : Refreshing Gem list
E, [2021-04-07T15:28:06.272044 #75098] ERROR -- : reaped 
#<Process::Status: pid 66270 SIGSEGV (signal 11) (core dumped)> worker=1
I, [2021-04-07T15:28:06.272459 #75098]  INFO -- : worker=1 spawning...
I, [2021-04-07T15:28:06.281695 #65711]  INFO -- : worker=1 spawned 
pid=65711
I, [2021-04-07T15:28:06.283312 #65711]  INFO -- : Refreshing Gem list



--------

A core file is generated:
$ ls -l ruby30.core
-rw-------  1 servidor  servidor  44485672 Apr  7 15:41 ruby30.core


Checking the trace

$ doas pkg_add gdb
$ egdb /usr/local/bin/ruby30 ruby30.core
...
  [New process 601176]
[New process 412220]
[New process 332011]
Core was generated by `ruby30'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000030bbfc70af8 in rb_vm_frame_method_entry ()
    from /usr/local/lib/libruby30.so
[Current thread is 1 (process 601176)]
(gdb) bt
#0  0x0000030bbfc70af8 in rb_vm_frame_method_entry ()
    from /usr/local/lib/libruby30.so
#1  0x0000030bbfca04cb in rb_vmdebug_stack_dump_raw ()
    from /usr/local/lib/libruby30.so
#2  0x0000030bbfca118a in rb_vm_bugreport () from 
/usr/local/lib/libruby30.so
#3  0x0000030bbfabd021 in rb_bug_for_fatal_signal ()
    from /usr/local/lib/libruby30.so
#4  0x0000030bbfbf4470 in sigsegv () from /usr/local/lib/libruby30.so
#5  <signal handler called>
#6  0x0000030bbfc78bb8 in vm_exec_core () from 
/usr/local/lib/libruby30.so
#7  0x0000030bbfc89d95 in rb_vm_exec () from /usr/local/lib/libruby30.so
#8  0x0000030bbfb20b0b in load_iseq_eval () from 
/usr/local/lib/libruby30.so
#9  0x0000030bbfb1ebaf in rb_load_internal () from 
/usr/local/lib/libruby30.so
#10 0x0000030bbfb20216 in rb_f_load () from /usr/local/lib/libruby30.so
#11 0x0000030bbfc9717e in vm_call_cfunc_with_frame ()
    from /usr/local/lib/libruby30.so
#12 0x0000030bbfc8e955 in vm_sendish () from /usr/local/lib/libruby30.so
#13 0x0000030bbfc77292 in vm_exec_core () from 
/usr/local/lib/libruby30.so
#14 0x0000030bbfc89d95 in rb_vm_exec () from /usr/local/lib/libruby30.so
#15 0x0000030bbfb20b0b in load_iseq_eval () from 
/usr/local/lib/libruby30.so
#16 0x0000030bbfb1ebaf in rb_load_internal () from 
/usr/local/lib/libruby30.so
#17 0x0000030bbfb20216 in rb_f_load () from /usr/local/lib/libruby30.so
#18 0x0000030bbfc9717e in vm_call_cfunc_with_frame ()
    from /usr/local/lib/libruby30.so
#19 0x0000030bbfc8e955 in vm_sendish () from /usr/local/lib/libruby30.so
#20 0x0000030bbfc77292 in vm_exec_core () from 
/usr/local/lib/libruby30.so
#21 0x0000030bbfc89d95 in rb_vm_exec () from /usr/local/lib/libruby30.so
#22 0x0000030bbfb20b0b in load_iseq_eval () from 
/usr/local/lib/libruby30.so
#23 0x0000030bbfb1ebaf in rb_load_internal () from 
/usr/local/lib/libruby30.so
#24 0x0000030bbfb20216 in rb_f_load () from /usr/local/lib/libruby30.so
#25 0x0000030bbfc9717e in vm_call_cfunc_with_frame ()
    from /usr/local/lib/libruby30.so
#26 0x0000030bbfc8e955 in vm_sendish () from /usr/local/lib/libruby30.so
#27 0x0000030bbfc77292 in vm_exec_core () from 
/usr/local/lib/libruby30.so
#28 0x0000030bbfc89d95 in rb_vm_exec () from /usr/local/lib/libruby30.so
#29 0x0000030bbfac5044 in rb_ec_exec_node () from 
/usr/local/lib/libruby30.so
#30 0x0000030bbfac4eca in ruby_run_node () from 
/usr/local/lib/libruby30.so
#31 0x0000030997a42cec in main ()


I still have not tried to use the .gdbinit file, but hope to do it soon.


Blessings.

El 2021-04-04 16:03, Eric Wong escribió:
> Jeremy Evans <code@jeremyevans.net> wrote:
>> Unfortunately, given the required amount of setup, I don't think I'll
>> have time to look into this.  The best way to debug this is to start
>> removing code/dependencies piece by piece until the problem goes away.
>> Once the problem goes away, add that code back, and make sure it fails
>> again, then start removing other code/dependencies.  Repeat this 
>> process
>> until you have the most minimal example, where removing any additional
>> code/dependencies will stop the code from failing.
> 
> The above is exactly what I would do, too.
> 
> I would also try to correlate the time of Bus Errors with logs
> of when + which requests are failing, and try replaying only
> those requests to narrow things down.
> 
> Also, do you have core dumps enabled?  Enable those
> ("ulimit -c unlimited" in sh), and run gdb on them as you get
> them.  The .gdbinit in the source tree of the Ruby version
> you're using is helpful not just for Ruby itself, but
> also C extensions, too.  I'm no a gdb expert, but it's
> well-documented and has built-in help.

-- 
Dios, gracias por tu amor infinito.
--
   Vladimir Támara Patiño.  http://vtamara.pasosdeJesus.org/
   http://www.pasosdejesus.org/dominio_publico_colombia.html

^ permalink raw reply	[relevance 0%]

* Re: Bus Error with Unicorn 6.0 on OpenBSD/adJ 6.8 with Ruby 3.0
  2021-04-04 16:43  2%       ` Jeremy Evans
@ 2021-04-04 20:03  0%         ` Eric Wong
  2021-04-07 21:19  0%           ` Bus Error with Unicorn 6.0 on OpenBSD-current (6.9) with Ruby 3.0 in minimal rails application vtamara
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2021-04-04 20:03 UTC (permalink / raw)
  To: vtamara; +Cc: Jeremy Evans, unicorn-public

Jeremy Evans <code@jeremyevans.net> wrote:
> Unfortunately, given the required amount of setup, I don't think I'll
> have time to look into this.  The best way to debug this is to start
> removing code/dependencies piece by piece until the problem goes away.
> Once the problem goes away, add that code back, and make sure it fails
> again, then start removing other code/dependencies.  Repeat this process
> until you have the most minimal example, where removing any additional
> code/dependencies will stop the code from failing.

The above is exactly what I would do, too.

I would also try to correlate the time of Bus Errors with logs
of when + which requests are failing, and try replaying only
those requests to narrow things down.

Also, do you have core dumps enabled?  Enable those
("ulimit -c unlimited" in sh), and run gdb on them as you get
them.  The .gdbinit in the source tree of the Ruby version
you're using is helpful not just for Ruby itself, but
also C extensions, too.  I'm no a gdb expert, but it's
well-documented and has built-in help.

^ permalink raw reply	[relevance 0%]

* Re: Bus Error with Unicorn 6.0 on OpenBSD/adJ 6.8 with Ruby 3.0
  @ 2021-04-04 16:43  2%       ` Jeremy Evans
  2021-04-04 20:03  0%         ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jeremy Evans @ 2021-04-04 16:43 UTC (permalink / raw)
  To: vtamara; +Cc: Eric Wong, unicorn-public

On 04/04 07:06, vtamara wrote:
> Hi
> 
> Thank you very much for the reply and the interest in helping.
> 
> > vtamara: did previous versions of Ruby, unicorn, or OpenBSD work
> > for your app?
> 
> The application was working withouth issue on OpenBSD/adJ amd64 6.7, ruby
> 2.7 and unicorn 6.0.  So there is something in conflict betweeen Unicorn 6.0
> with OpenBSD/adJ 6.8 and ruby 3.0.
> 
> > Is preload_app in use?  If so, does the problem manifest without
> > preload_app?
> 
> No
> 
> > Are you using Ractor or any other new/experimental stuff
> > in Ruby?
> 
> No
> 
> > Can you reproduce the problem with a simple "hello world"-style app?
> 
> I don't have a hello world example at hand, but some projects that present
> this problem when I run them on OpenBSD/adJ amd64 6.8, nginx 1.18.0, node
> 12.16.1, PostgreSQL 13.2, Ruby 3.0, Rails 6.1 and Unicorn 6.0 are:
> 
> https://github.com/pasosdeJesus/cor1440
> https://github.com/pasosdeJesus/cor1440_pdJ
> https://github.com/pasosdeJesus/sivel2
> https://github.com/pasosdeJesus/si_anzorc
> https://github.com/pasosdeJesus/si_asom
> https://github.com/pasosdeJesus/si_codacop
> https://github.com/pasosdeJesus/sivel2_mujeresindigenas/

Unfortunately, given the required amount of setup, I don't think I'll
have time to look into this.  The best way to debug this is to start
removing code/dependencies piece by piece until the problem goes away.
Once the problem goes away, add that code back, and make sure it fails
again, then start removing other code/dependencies.  Repeat this process
until you have the most minimal example, where removing any additional
code/dependencies will stop the code from failing.

Sorry,
Jeremy

> 
> The simplest of them is https://github.com/pasosdeJesus/cor1440, althoug it
> requires:
> * PostgreSQL
> * nginx
> * nodejs
> * yarn. I install it with: doas pkg_add bash; ftp -o-
> https://yarnpkg.com/install.sh | bash
> * A directory /var/www/bundler/ruby/3.0 where I install the gems with the
> permissions of a user (not root) that runs the application, and better
> running:
>   bundle config path /var/www/bundler/.
> * unicorn installed globally with:
>   doas gem install unicorn
>   doas ln -s /usr/local/bin/unicorn_rails30 /usr/local/bin/unicorn
> 
> To create the PostgreSQL user (in the following example sipdes) and a
> database for the production environment  (in the following example
> cor1440gen_pro):
> 
>   doas su - _postgresql
>   createuser -h /var/www/var/run/postgresql/ -U postgres -s sipdes
>   createdb -h /var/www/var/run/postgresql/ -U postgres -O sipdes
> cor1440gen_pro
>   psql -h /var/www/var/run/postgresql/ -U postgres
>   postgres=# alter user sipdes with password 'xyz';
>   postgres=# \q
>   exit
> 
> With nginx suppossng I will run the application in localhost over SSL, what
> I do is to choose an unused port (let's say 2009) and add to the
> /etc/nginx/nginx.conf configuration in its http block:
> 
>  upstream unicorncor1440 {
> 	  server 127.0.0.1:2009 fail_timeout=0;
>   }
> 
> I also add a server block inside the http block with:
> 
>   server {
>       listen 443 ssl;
>       ssl_certificate /etc/ssl/server.crt;
>       ssl_certificate_key /etc/ssl/private/server.key;
>       root /var/www/htdocs/cor1440/;
>       server_name 127.0.0.1;
>       error_log logs/cor1440.rror.log;
> 
>       location /cor1440 {
>         try_files $uri @unicorncor1440;
>       }
> 
>       location @unicorncor1440 {
>         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
>         proxy_set_header X-Forwarded-Proto $scheme;
>         proxy_set_header Host $http_host;
>         proxy_redirect off;
>         proxy_pass http://unicorncor1440;
>         error_page 500 502 503 504 /500.html;
>         keepalive_timeout 10;
>       }
> 
>       location ^~ /cor1440/assets/ {
>         gzip_static on;
>         expires max;
>         add_header Cache-Control public;
>         root /var/www/htdocs/cor1440/public/;
>       }
> 
>       location ^~ /cor1440/images/ {
>         gzip_static on;
>         expires max;
>         add_header Cache-Control public;
>         root /var/www/htdocs/cor1440/public/;
>       }
> 
>       location ^~ /cor1440/packs/ {
>         gzip_static on;
>         add_header Cache-Control public;
>         root /var/www/htdocs/cor1440/public/;
>       }
>     }
> 
> And restarted nginx:
> 
>   doas rcctl restart nginx
> 
> Then to configure:
> 
>   cd /tmp
>   git clone git@github.com:pasosdeJesus/cor1440
>   doas mv cor1440 /var/www/htdocs/
>   doas chown -R $USER:$USER /var/www/htdocs/cor1440
>   cd /var/www/htdocs/cor1440
>   mkdir .bundle
>   echo '---' > .bundle/config
>   echo 'BUNDLE_PATH: "/var/www/bundler"' >> .bundle/config
>   echo 'BUNDLE_DISABLE_SHARED_GEMS: "true"' >> .bundle/config
>   bundle
> 
> Here some gems will require installing with special permissions for example:
> 
>   doas gem install --install-dir /var/www/bundler/ruby/3.0 unicorn
> 
> Other gems that will require this kind of installation will be:
>   racc nio4r websocket-driver bcrypt bindex msgpack ffi redcarpet kgio
> libxml-ruby pg
>   nokogiri bootsnap puma sassc
> 
> Once bundle runs completely:
> 
>   cp .env.plantilla .env
>   vi .env
> 
> There I set the variables:
> * BD_USUARIO with the database user,
> * BD_CLAVE with the password
> * BD_PRO with the name of the production database
> * CONFIG_HOSTS=127.0.0.1
> * DIRAP with the path to the application
> * RAILS_ENV=production
> * PUERTOUNICORN with the port I choosed and set in nginx.conf
> * USUARIO_AP with the operating system user who will run the application
> 
> Prepare credentials and database:
> 
>   bin/rails credentials:edit
>   RAILS_ENV=production bin/rails credentials:edit
>   bin/railsp db:setup  db:migrate sip:indices
> 
> Precompile assets:
> 
>   CXX=c++ yarn
>   doas ln -s /usr/local/bin/node /tmp  # To overcome bug of node with rails
> on OpenBSD
>   bin/railsp assets:precompile
> 
> Run the application with:
>   SECRET_KEY_BASE=cf46 DIRAP=/var/www/htdocs/cor1440/ bin/u.sh
> 
> Trying to see the application running with a browser in
> https://127.0.0.1/cor1440 will produce the errors described in
> log/unicorn.log.
> 
> Blessings. Thank you.
> 
> El 2021-03-30 00:00, Jeremy Evans escribi??:
> > On 03/30 01:58, Eric Wong wrote:
> > > vtamara <vtamara@pasosdeJesus.org> wrote:
> > > > Hi
> > > >
> > > > Launching a rails application with Ruby 3, Unicorn 6 on OpenBSD/adJ 6.8, I'm
> > > > experiencing a lot of "Bus Error" and "Segmentation Faults" during
> > > > aproximately half hour.
> > > 
> > > +Cc: Jeremy Evans, who is more familiar with OpenBSD and Ruby 3.0
> > > than I
> > > cf. https://yhbt.net/unicorn-public/6acb1c84c7392d7b4a64572b20498549@pasosdeJesus.org/
> > 
> > I'll be happy to look into this if a minimal self-contained reproducible
> > example is posted.  Personally, I run my Ruby apps on OpenBSD amd64 with
> > Unicorn, and haven't seen similar errors.  However, I don't use
> > preload_app and use fairly minimal dependencies.
> > 
> > Thanks,
> > Jeremy
> 
> -- 
> Dios, gracias por tu amor infinito.
> --
>   Vladimir T??mara Pati??o.  http://vtamara.pasosdeJesus.org/
>   http://www.pasosdejesus.org/dominio_publico_colombia.html

^ permalink raw reply	[relevance 2%]

* Re: Potential Unicorn vulnerability
       [not found]       ` <7F6FD017-7802-4871-88A3-1E03D26D967C@github.com>
@ 2021-03-12  9:41  1%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2021-03-12  9:41 UTC (permalink / raw)
  To: Dirkjan Bussink; +Cc: John Crepezzi, Kevin Sawicki, unicorn-public

[-- Attachment #1: Type: text/plain, Size: 6397 bytes --]

Dirkjan Bussink <dbussink@github.com> wrote:
> Hello Eric,
> 
> > On 11 Mar 2021, at 04:02, Eric Wong <normalperson@yhbt.net> wrote:
> > 
> > Thanks for reaching out.  Fwiw, I prefer if everything were made
> > public right away, but I'll leave it up to you if you're not
> > comfortable with it.
> > 
> > I don't know much about security, anwyays; and don't like
> > classifying bugs (or classifying anything)...
> 
> We reached out privately first out of care and to follow best practices
> around coordinated disclosure, in case you considered this a security
> vulnerability. We have no objections to moving this to the public mailing
> list. We are viewing this patch as a proactive hardening against race
> conditions more so than a vulnerability. 

OK, I'm adding unicorn-public@yhbt.net to the Cc: of this mail.

Personally, I prefer everything be reported publicly ASAP.
There's a constant threat of power/network failures from
disasters and such that could cause messages to be delayed
too long or indefinitely.

> We are also reaching out to a private Rails Security coordination channel
> that we’re a part of to raise awareness of this behavior so other Unicorn
> users in this group can look for similar issues in their code. 

OK.

> To move this discussion to the public list, would you prefer that you move
> this thread publicly, or that we resend the message or forward it to the
> public mailing list? 

Attached are the initial two (previously private) emails in this
thread, so no need to resend.  They have the correct
message/rfc822 MIME type set so they should be readable from
most MUAs and mail archives.

> > Ouch, so the hijack check we had in HttpParser_clear didn't fire...
> 
> Yes, to our understanding it only would fire if explicitly set and that
> doesn’t happen here.
> 
> >> While we understand and appreciate that Unicorn is not a multi-threaded web
> >> server, it feels like using the same `Hash` object between requests
> >> introduces the chance that a dependency like an external gem may use threads
> >> and thus potentially leak information between requests.
> > 
> > Yes, there's likely problems in trying to use threads with a
> > codebase that was only intended to be single-threaded.  And
> > using Thread.current[...] here wouldn't have made a. difference.
> 
> For us it was also the difference between “requests are handled single threaded”
> vs “you can’t use threads for anything else either.” We were totally aware of the
> first, but the latter seems more accidental and has a much broader impact. 

Agreed.

> > I worry some endpoints out there will suffer performance
> > degradation.  Expensive endpoints probably won't notice,
> > but maybe the fastest ones will...
> 
> That is a good point, but I think in practice most users do enough in most
> requests that the trade off is totally worth it. At least for us it definitely
> is. Maybe it would be an option to make the sharing somehow opt-in instead of
> default behavior? So that by default the safe behavior is used, but for those
> that want to, they can opt into the sharing if they know their app is safe
> enough to work with that. 

I'm not in favor of new options since they add support costs
and increase the learning/maintenance curve.

What I've been thinking about is bumping the major version to 6.0

Although our internals are technically not supported stable API,
there may be odd stuff out there similar to OobGC that uses
instance_variable_get or similar things to reach into internals.
Added with the fact our internals haven't changed in many years;
I'm inclined to believe there are other OobGC-like things out
there that can break.

Also, with 6.0; users who completely avoid Threads can keep
using 5.x, while others can use 6.x

> >> For the sake of transparency to our users, we plan on publishing a public
> >> post next week on how this was part of the larger series of bugs that had a
> >> security impact at GitHub. We've also attached a suggested patch that removes
> >> the environment sharing, which is what we're running right now to reduce the
> >> risk of this ever happening again.
> > 
> > Did you measure performance degradations in any endpoints you have?
> 
> We did measure and there were zero noticeable performance degradations. That’s
> also because all our requests do a bunch of work and are not direct Rack apps,
> and use stuff like Rails or Sinatra. Those all usually wrap the `env` hash
> anyway with their own per request object and there’s a lot of other allocations
> happening anyway.
> 
> In microbenchmarks we could see a difference, but even there, at least for us,
> we’d gladly pay the performance price for the safety if we’d have any endpoints
> where it would be measurable. 

OK.  Fwiw, there's still some stones left unturned that could
recover the lost speed if somebody _really_ cares for it(*)

(*) String#clear on response header buffer, swapping Ragel
    for a faster HTTP parser, ...

> All in all, I think here that a safe default would be helpful for users, as
> mentioned earlier, and that maybe for those cases where the latest performance
> bit matters, the existing behavior could be opted into. Whether this option is
> worth it from a maintenance standpoint is something you’re better able to answer
> than we are though.

It's probably OK and I'm inclined to accept your patch for 6.0.

At this point, I'm more worried about potential breakage of some
3rd-party OobGC-like thing that reaches deep into our internals.

Btw, did you consider replacing the @request HttpRequest object
entirely instead of the env and buf elements?
I suppose that's more allocations, still; but could've
been a smaller change.

> > I'll see about finding a less-noisy/overloaded system to run
> > benchmarks against...
> > 
> > I noticed some of the OobGC tests in t/ were failing (patch below),
> > but few users use the that version of OobGC.
> 
> I wasn’t easily able to run the entire suite, but only parts of it which is why
> I didn’t have a complete fix there. I can add this to the patch then.

Oops, was that the integration tests in t/* ?
They can run separately via "make test-integration"
(I never trusted some Ruby behaviors to remain stable,
 so I started writing tests in Bourne shell).

<snip> I have nothing to add to the rest of the mail

unicorn-public readers: see attachments

[-- Attachment #2: 0001-potential-unicorn-vulnerability.eml --]
[-- Type: message/rfc822, Size: 12105 bytes --]

[-- Attachment #2.1.1: Type: text/plain, Size: 2813 bytes --]

Hello Eric,

We're reaching out privately first on what we think could be classified as a
security issue in Unicorn. Since there may be other similarly impacted users,
this is out of an abundance of caution before sending it to the public
Unicorn mailing list.

The issue at hand is that the environment sharing and reuse between requests
in Unicorn, combined with other non thread safe code, resulted in a security
vulnerability where a very small number of user sessions tracked through
cookies got misrouted. For this reason, we logged everyone out of GitHub, see
also https://github.blog/2021-03-08-github-security-update-a-bug-related-to-handling-of-authenticated-sessions/. 

The unsafe background thread code we had ended up triggering an exception at
rare times and the exception tracking logic was using a deferred block
executed through a Ruby Proc to gather information, including things from the
request at the time the logic started.

That thread captured something from the cookie jar among other things, and
the following code in Rack memoized that into the (shared) environment.

From https://github.com/rack/rack/blob/2.1.4/lib/rack/request.rb#L219-L229

def cookies
  hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |k|
    set_header(k, {})
  end
  string = get_header HTTP_COOKIE

  return hash if string == get_header(RACK_REQUEST_COOKIE_STRING)
  hash.replace Utils.parse_cookies_header string
  set_header(RACK_REQUEST_COOKIE_STRING, string)
  hash
end

Because of the environment sharing, the memoization ended up overriding what
would be memoized (with the RACK_REQUEST_COOKIE_STRING key) for the new
request and because a specific session cookie was bumped then with a new
timeout on each request, the wrong session cookie was serialized.

While we understand and appreciate that Unicorn is not a multi-threaded web
server, it feels like using the same `Hash` object between requests
introduces the chance that a dependency like an external gem may use threads
and thus potentially leak information between requests.

For the sake of transparency to our users, we plan on publishing a public
post next week on how this was part of the larger series of bugs that had a
security impact at GitHub. We've also attached a suggested patch that removes
the environment sharing, which is what we're running right now to reduce the
risk of this ever happening again.

We hope you're open to collaborating on a fix prior to any public detailed
disclosure of how this request environment sharing could lead to security
issues, if you feel that is desired. 

I’ve added John & Kevin here on the CC since they’ve also worked on this and
that way we have some better timezone spread on our side if needed. 

Cheers,

Dirkjan Bussink


[-- Attachment #2.1.2: 0001-Drop-reuse-of-Ruby-level-objects.patch --]
[-- Type: application/octet-stream, Size: 4067 bytes --]

From 44aa6b056e1b24d42ab3efba738b38f4cd54a068 Mon Sep 17 00:00:00 2001
From: Dirkjan Bussink <d.bussink@gmail.com>
Date: Mon, 8 Mar 2021 09:51:09 +0100
Subject: [PATCH] Drop reuse of Ruby level objects

Remove the reuse of environment and the buffer as Ruby level objects.
Reusing these is very risky in the context of running any other threads
within the unicorn process, also for threads that run background tasks.

This lead to a significant security incident where the problems would
not have happened if there was no reuse of the `env` object. From a
safety perspective, this also removes reuse of the buffer object as it's
also a Ruby object and could be grabbed and retained outside of the http
parsing logic.

The downside here is that we allocate two extra objects on each request,
but that is worth the trade off here and the security risk we otherwise
would carry to leaking wrong and incorrect data.
---
 ext/unicorn_http/extconf.rb      |  1 -
 ext/unicorn_http/unicorn_http.rl | 30 +++---------------------------
 test/unit/test_http_parser.rb    |  3 +++
 3 files changed, 6 insertions(+), 28 deletions(-)

diff --git a/ext/unicorn_http/extconf.rb b/ext/unicorn_http/extconf.rb
index 95514bc..7e4775c 100644
--- a/ext/unicorn_http/extconf.rb
+++ b/ext/unicorn_http/extconf.rb
@@ -10,7 +10,6 @@
 have_macro("SIZEOF_SIZE_T", "ruby.h") or check_sizeof("size_t", "sys/types.h")
 have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
 have_func("rb_str_set_len", "ruby.h") or abort 'Ruby 1.9.3+ required'
-have_func("rb_hash_clear", "ruby.h") # Ruby 2.0+
 have_func("gmtime_r", "time.h")
 
 message('checking if String#-@ (str_uminus) dedupes... ')
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 21e09d6..9904d85 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -65,18 +65,6 @@ struct http_parser {
 static ID id_set_backtrace, id_is_chunked_p;
 static VALUE cHttpParser;
 
-#ifdef HAVE_RB_HASH_CLEAR /* Ruby >= 2.0 */
-#  define my_hash_clear(h) (void)rb_hash_clear(h)
-#else /* !HAVE_RB_HASH_CLEAR - Ruby <= 1.9.3 */
-
-static ID id_clear;
-
-static void my_hash_clear(VALUE h)
-{
-  rb_funcall(h, id_clear, 0);
-}
-#endif /* HAVE_RB_HASH_CLEAR */
-
 static void finalize_header(struct http_parser *hp);
 
 static void parser_raise(VALUE klass, const char *msg)
@@ -445,6 +433,8 @@ static void http_parser_init(struct http_parser *hp)
   hp->cont = Qfalse; /* zero on MRI, should be optimized away by above */
   %% write init;
   hp->cs = cs;
+  hp->buf = rb_str_new(NULL, 0);
+  hp->env = rb_hash_new();
 }
 
 /** exec **/
@@ -628,8 +618,6 @@ static VALUE HttpParser_init(VALUE self)
   struct http_parser *hp = data_get(self);
 
   http_parser_init(hp);
-  hp->buf = rb_str_new(NULL, 0);
-  hp->env = rb_hash_new();
 
   return self;
 }
@@ -643,16 +631,7 @@ static VALUE HttpParser_init(VALUE self)
  */
 static VALUE HttpParser_clear(VALUE self)
 {
-  struct http_parser *hp = data_get(self);
-
-  /* we can't safely reuse .buf and .env if hijacked */
-  if (HP_FL_TEST(hp, HIJACK))
-    return HttpParser_init(self);
-
-  http_parser_init(hp);
-  my_hash_clear(hp->env);
-
-  return self;
+  return HttpParser_init(self);
 }
 
 static void advance_str(VALUE str, off_t nr)
@@ -1025,9 +1004,6 @@ void Init_unicorn_http(void)
   id_set_backtrace = rb_intern("set_backtrace");
   init_unicorn_httpdate();
 
-#ifndef HAVE_RB_HASH_CLEAR
-  id_clear = rb_intern("clear");
-#endif
   id_is_chunked_p = rb_intern("is_chunked?");
 }
 #undef SET_GLOBAL
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index 697af44..d3f9ae7 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -33,6 +33,9 @@ def test_parse_simple
     parser.clear
     req.clear
 
+    req = parser.env
+    http = parser.buf
+
     http << "G"
     assert_nil parser.parse
     assert_equal "G", http
-- 
2.30.1


[-- Attachment #3: 0002-re-potential-unicorn-vulnerability.eml --]
[-- Type: message/rfc822, Size: 5635 bytes --]

From: Eric Wong <normalperson@yhbt.net>
To: Dirkjan Bussink <dbussink@github.com>
Cc: John Crepezzi <seejohnrun@github.com>, Kevin Sawicki <kevinsawicki@github.com>
Subject: Re: Potential Unicorn vulnerability
Date: Thu, 11 Mar 2021 03:02:50 +0000
Message-ID: <20210311030250.GA1266@dcvr>

Dirkjan Bussink <dbussink@github.com> wrote:
> Hello Eric,
> 
> We're reaching out privately first on what we think could be classified as a
> security issue in Unicorn. Since there may be other similarly impacted users,
> this is out of an abundance of caution before sending it to the public
> Unicorn mailing list.

Thanks for reaching out.  Fwiw, I prefer if everything were made
public right away, but I'll leave it up to you if you're not
comfortable with it.

I don't know much about security, anwyays; and don't like
classifying bugs (or classifying anything)...

<snip>

> That thread captured something from the cookie jar among other things, and
> the following code in Rack memoized that into the (shared) environment.

Ouch, so the hijack check we had in HttpParser_clear didn't fire...

<snip>

> While we understand and appreciate that Unicorn is not a multi-threaded web
> server, it feels like using the same `Hash` object between requests
> introduces the chance that a dependency like an external gem may use threads
> and thus potentially leak information between requests.

Yes, there's likely problems in trying to use threads with a
codebase that was only intended to be single-threaded.  And
using Thread.current[...] here wouldn't have made a. difference.

I worry some endpoints out there will suffer performance
degradation.  Expensive endpoints probably won't notice,
but maybe the fastest ones will...

`buf' is particularly worrying to me since it's a 16384-byte
allocation for a socket read on headers that could amount
to lots of GC pressure and hurt locality all around.

env['rack.input'] may also hold onto `buf', too, since
input is lazily-consumed (though it may be possible to
workaround that...).

Losing `env' is probably less worrying since keys are all
fstrings in modern Rubies.  Though losing the backing store (and
not having rb_hash_new_with_size in the C API) would mean more
rehashing with many headers.

The simple Rack apps I worked on back in-the-day which
benefitted from these object-reuse optimizations are unlikely to
be upgraded to newer versions of unicorn.  Of course, there
may be other similar Rack apps out there that depend on these...

> For the sake of transparency to our users, we plan on publishing a public
> post next week on how this was part of the larger series of bugs that had a
> security impact at GitHub. We've also attached a suggested patch that removes
> the environment sharing, which is what we're running right now to reduce the
> risk of this ever happening again.

Did you measure performance degradations in any endpoints you have?

I'll see about finding a less-noisy/overloaded system to run
benchmarks against...

I noticed some of the OobGC tests in t/ were failing (patch below),
but few users use the that version of OobGC.

Also, t/t0200-rack-hijack.sh would go away.

> We hope you're open to collaborating on a fix prior to any public detailed
> disclosure of how this request environment sharing could lead to security
> issues, if you feel that is desired. 

Sure, as long as everything is done via plain-text email so
I won't need working graphics drivers or modern hardware :>

Along the same lines, would you be OK with this entire mail thread
(including all mail headers) being eventually published in the
public mailbox at http://ou63pmih66umazou.onion/unicorn-public/
and https://yhbt.net/unicorn-public/ ?

> I’ve added John & Kevin here on the CC since they’ve also worked on this and
> that way we have some better timezone spread on our side if needed. 

OK, I'm around/awake at pretty random times.

Aforementioned OomGC change:
-------8<-------
diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb
index 3b2f488..91a8e51 100644
--- a/lib/unicorn/oob_gc.rb
+++ b/lib/unicorn/oob_gc.rb
@@ -60,7 +60,6 @@ def self.new(app, interval = 5, path = %r{\A/})
     self.const_set :OOBGC_INTERVAL, interval
     ObjectSpace.each_object(Unicorn::HttpServer) do |s|
       s.extend(self)
-      self.const_set :OOBGC_ENV, s.instance_variable_get(:@request).env
     end
     app # pretend to be Rack middleware since it was in the past
   end
@@ -68,9 +67,10 @@ def self.new(app, interval = 5, path = %r{\A/})
   #:stopdoc:
   def process_client(client)
     super(client) # Unicorn::HttpServer#process_client
-    if OOBGC_PATH =~ OOBGC_ENV['PATH_INFO'] && ((@@nr -= 1) <= 0)
+    env = instance_variable_get(:@request).env
+    if OOBGC_PATH =~ env['PATH_INFO'] && ((@@nr -= 1) <= 0)
       @@nr = OOBGC_INTERVAL
-      OOBGC_ENV.clear
+      env.clear
       disabled = GC.enable
       GC.start
       GC.disable if disabled

^ permalink raw reply related	[relevance 1%]

* Re: [RFC] http_response: ignore invalid header response characters
    2021-01-06 17:53  0% ` Eric Wong
@ 2021-02-26 11:15  3% ` Eric Wong
  1 sibling, 0 replies; 200+ results
From: Eric Wong @ 2021-02-26 11:15 UTC (permalink / raw)
  To: Sam Sanoop; +Cc: unicorn-public

cf. https://yhbt.net/unicorn-public/20201126115902.GA8883@dcvr/T/

This RFC patch isn't necessary and it's a waste of CPU cycles
for existing users returning valid responses.

I've confirmed nginx will return 502 (Bad Gateway) errors if
either the header key OR header value have "\r" (CR) in it.  The
client never sees the bad response through nginx, only the 502
from nginx.

For anybody unfamiliar with unicorn: unicorn was always designed
to have nginx in front of it, otherwise clients can trivially
DoS it.

I tested with nginx/1.14.2 on Debian stable (10.x).  Also,
varnish/6.1.1 (again on Debian stable) will fail with a 503
error when it sees "\r" in either the header key or value
(nowadays I rely on varnish quite a bit)

Again, thanks for the concern, but no action is necessary for
correctly configured deployments using nginx with or w/o varnish.

The following config.ru can be edited by uncommenting either
of the "cr*" lines to trigger the 502 from nginx.  Leaving
both lines commented shows a 200 "HI\n" response as expected.

==> config.ru <==
#\-E none
use Rack::CommonLogger
run lambda { |env|
  [ 200, {
     'Content-Type' => 'text/plain',
     'Content-Length' => '3',
     # uncommenting either of the following lines causes nginx to 502
     # varnish will 503
     #  "cr\rkey" => "cr-key",
     #  "cr-val" => "cr\rkey",
    },
    [ "HI\n" ]
  ]
}

^ permalink raw reply	[relevance 3%]

* Re: [RFC] http_response: ignore invalid header response characters
  2021-01-06 17:53  0% ` Eric Wong
@ 2021-01-13 23:20  1%   ` Sam Sanoop
  0 siblings, 0 replies; 200+ results
From: Sam Sanoop @ 2021-01-13 23:20 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Hi, The researcher didn’t test it with Ngnix, his configuration
according to the report was

server: unicorn (<= latest)
Rails version: 6.0.0 < rails < 6.0.3.2
RAILS_ENV: production


But reading http://blog.volema.com/nginx-insecurities.html, CRLF can
be allowed if a user is using $uri variables within their NGINX
config. So conditions within NGNIX would need to be met.

Regarding the hackerone report, here is a txt version of the report:


ooooooo_q-----------------

Hello,
I was looking at the change log
(https://github.com/rails/rails/commit/2121b9d20b60ed503aa041ef7b926d331ed79fc2)
for CVE-2020-8185 and found another problem existed.

https://github.com/rails/rails/blob/v6.0.3.1/actionpack/lib/action_dispatch/middleware/actionable_exceptions.rb#L21

  redirect_to request.params[:location]
end

private
  def actionable_request?(request)
    request.show_exceptions? && request.post? && request.path == endpoint
  end

  def redirect_to(location)
    body = "<html><body>You are being <a
href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"

    [302, {
      "Content-Type" => "text/html; charset=#{Response.default_charset}",
      "Content-Length" => body.bytesize.to_s,
      "Location" => location,
    }, [body]]
  end
There was an open redirect issue because the request parameter
location was not validated.
In 6.0.3.2, since the condition of actionable_request? has changed,
this problem is less likely to occur.

PoC
1. Prepare server
Prepare an attackable 6.0.3.1 version of Rails server

❯ rails -v
Rails 6.0.3.1

❯ RAILS_ENV=production rails s
...
* Environment: production
* Listening on tcp://0.0.0.0:3000
2. Attack server
Prepare the server for attack on another port.

<form method="post"
action="http://localhost:3000/rails/actions?error=ActiveRecord::PendingMigrationError&action=Run%20pending%20migrations&location=https://www.hackerone.com/">
    <button type="submit">click!</button>
</form>
python3 -m http.server 8000
3. Open browser
Open the http://localhost:8000/attack.html url in your browser and
click the button.
Redirect to https://www.hackerone.com/ url.



Impact
It will be fixed with 6.0.3.2 as with
CVE-2020-8185(https://groups.google.com/g/rubyonrails-security/c/pAe9EV8gbM0),
but I think it is necessary to announce it again because the range of
influence is different.

This open redirect changes from POST method to Get Method, so it may
be difficult to use for phishing.On the other hand, it may affect
bypass of referrer check or SSRF.



ooooooo_q------

I thought about it after submitting the report, but even in 6.0.3.2,
/rails/actions is available in developer mode.
If it was started in development mode, the request will be accepted by
CSRF, so the same as CVE-2020-8185 still exists.
I think it's better to take CSRF measures in /rails/actions.

Vulnerabilities, versions and modes
6.0.3.1 (production, development)
run pending migrations (CVE-2020-8185)
open redirect
6.0.3.2 (development)
run pending migrations (by CSRF)
open redirect (by CSRF)
6.0.3.2 (production)
no problem


tenderloveRuby on Rails staff -------

This seems like a good improvement, but I don't think we need to treat
it as a security issue. If you agree, would you mind filing an issue
on the Rails GitHub issues?
Thank you!


ooooooo_q------

@tenderlove
I'm sorry to reply late.

While researching unicorn, I found this report to lead to other vulnerabilities.

Open Redirect to HTTP header injection
Response header injection vulnerability exists in versions of puma below 4.3.2.
https://github.com/puma/puma/security/advisories/GHSA-84j7-475p-hp8v
I have confirmed that unicorn is also enable of response header injection.

HTTP header injection is possible by including \r in the redirect URL
using these.

PoC
> escape("\rSet-cookie:a=a")
"%0DSet-cookie%3Aa%3Da"
This is the html used on the attack server.

<form method="post"
action="http://localhost:3000/rails/actions?error=ActiveRecord::PendingMigrationError&action=Run%20pending%20migrations&location=%0DSet-cookie%3Aa%3Da">
  <button type="submit">set cookie</button>
</form>
When click this button, the response header will be as follows.

Location:
Set-cookie: a=a
If you open the developer tools in your browser, you'll see that the
cookie value is set.

When I tried it, puma and unicorn can insert only the character of \r,
and it seems that \n cannot be inserted.
Therefore, it seems that response body injection that leads to XSS
cannot be performed.

On the other hand, in passenger, it became an error when \r was in the
value of the header.

XSS trick
While trying out \r on the response header, I noticed a strange thing.
If \r is at the beginning, it means that the browser is not redirected
and javascript: can be used in the URL of the response link.
When specify \rjavascript:alert(location) as the redirect destination,
the HTML of the response will be as follows.

<html><body>You are being <a href="
javascript:alert(location)">redirected</a>.</body></html>
The \r character is ignored in html, so javascript:alert(location) is
executed when the user clicks the link.

This is a separate issue from HTTP header injection and depends on how
the server handles the value of \r.
In puma and unicorn below 4.3.2, \r is used for HTTP header injection,
so no error occurs.
With puma 4.3.3 or later, if there is a line containing \r, it does
not become an error and it can be executed because that line is
excluded.
On the other hand, the error occurred in passenger.

As a further issue, the headers in this response are
middleware-specific and therefore do not include the security headers
Rails is outputting.
Since X-Frame-Options is not included, click jacking is possible.
No output even if CSP is set in the application.

By using click jacking in combination with these, it is easy to
generate XSS that requires user click.

PoC
Inserting the execution code from another site using the name of the iframe.

This PoC will also run in production mode if 6.0.0 < rails < 6.0.3.2.
It can be run with the latest puma and unicorn.

If it is development mode, it can be executed even after 6.0.3.2

child.html
<form method="post"
action="http://localhost:3000/rails/actions?error=ActiveRecord::PendingMigrationError&action=Run%20pending%20migrations&location=%0Djavascript%3Aeval%28name%29">
    <button type="submit">location is escape("\rjavascript:eval(name)")</button>
</form>
<script type="text/javascript">
    document.querySelector("button").click();
</script>
click_jacking.html
<html>
<style>
iframe{
    position: absolute;
    z-index: 1;
    opacity: 0.3;
}
div{
    position: absolute;
    top: 20px;
    left: 130px;
}
button {
    width: 80px;
    height: 26px;
    cursor: pointer;
}
</style>
<body>
    <iframe src=./child.html name="alert(location)" height=40></iframe>
    <div>
        <button>click!!</button>
    </div>
</body>
</html>



XSS to RCE
When XSS exists in development mode, I confirmed that calling the
method of web-cosole leads to RCE.
RCE is possible by inducing users who are developing Rails
applications to click on the trap site.

PoC
var iframe = document.createElement("iframe");
iframe.src = "/not_found";
document.body.appendChild(iframe);
setTimeout(()=>fetch("/__web_console/repl_sessions/" +
iframe.contentDocument.querySelector("#console").dataset.sessionId, {
    method: "PUT",
    headers: {
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest"
    },
    body: JSON.stringify({
        input: "`touch from_web_console`"
    })
}), 2000)
<iframe src=./child.html name='var iframe =
document.createElement("iframe");iframe.src =
"/not_found";document.body.appendChild(iframe);setTimeout(()=>
fetch("/__web_console/repl_sessions/"+iframe.contentDocument.querySelector("#console").dataset.sessionId,
{method: "PUT",headers: {"Content-Type":
"application/json","X-Requested-With": "XMLHttpRequest"},body:
JSON.stringify({input: "`touch from_web_console`"})}),2000)'/>
When this is run, a file from from_web_console will be generated.

Vulnerabilities and conditions
Run pending migrations (CVE-2020-8185)
server: any

Rails version: 6.0.0 < rails < 6.0.3.2
RAILS_ENV: production

Run pending migrations by CSRF
server: any

Rails version: 6.0.0 < (not fixed)
RAILS_ENV: development

Open redirect (from POST method)
server: any

Rails version: 6.0.0 < rails < 6.0.3.2
RAILS_ENV: production

or

Rails version: 6.0.0 < (not fixed)
RAILS_ENV: development

HTTP header injection
server: unicorn (<= latest) or puma (< 4.3.2)

Rails version: 6.0.0 < rails < 6.0.3.2
RAILS_ENV: production

or

Rails version: 6.0.0 < (not fixed)
RAILS_ENV: development

XSS (need user click)
server: unicorn (<= latest) or puma (<= latest)

Railse version: 6.0.0 < rails < 6.0.3.2
RAILS_ENV: production

or

Railse version: 6.0.0 < (not fixed)
RAILS_ENV: development

RCE (from XSS)
server: unicorn (<= latest) or puma (<= latest)
Railse version: 6.0.0 < (not fixed)
RAILS_ENV: development



ooooooo_q------

When I tried it, puma and unicorn can insert only the character of \r,
and it seems that \n cannot be inserted.
Makes sense. This seems like a security vulnerability in Puma / Unicorn.
This is a separate issue from HTTP header injection and depends on how
the server handles the value of \r.
I'm not sure exactly which servers are vulnerable (this is too
confusing for me 😔), but whatever handles generating the response
page shouldn't allow \r at the beginning of the href like that.
So this needs to check that location is a url (http or https).
I don't think we need to set the security policy for the redirect page
if we prevent the javascript: location from the href.
How does this patch look?

On Wed, Jan 6, 2021 at 5:53 PM Eric Wong <bofh@yhbt.net> wrote:
>
> Sam Sanoop <sams@snyk.io> wrote:
> > Hi Eric,
>
> cf. https://yhbt.net/unicorn-public/20201126115902.GA8883@dcvr/
> (private followup <CAEQPCYJJdriMQyDNXimCS_kBrc_=DxhxJ66YdJmCe7ZXr-Zbvg@mail.gmail.com>)
>
> Hi Sam, I'm moving this to the public list.  Please keep
> unicorn-public@yhbt.net Cc-ed (no need to quote, archives are
> public and mirrored to several places).
>
> > I wanted to bring this up again. I spoke with the researcher
> > (@ooooooo_q) who disclosed this issue to us. He released the full
> > details including the HackerOne report pubclily which provided more
> > clarity about this issue. That can be read here:
> > https://hackerone.com/reports/904059#activity-8945588
>
> Note: I can't read anything on hackerone due to the JavaScript
> requirement.  If somebody can copy the text here, that'd be
> great.
>
> > That report was initially disclosed to the Ruby on Rails maintainers.
> > In certain conditions, he was also able to leverage Puma and Unicorn
> > to exploit the issues mentioned on the report. The issues which
> > related to Unicorn were:
> >
> > * Open Redirect to HTTP header injection - (including \r in the redirect URL)
> > * A user interaction XSS  - Which leverages
> > \rjavascript:alert(location) as the redirect destination
>
> Does this happen when unicorn is behind nginx?
>
> unicorn was always designed to run behind nginx and falls over badly
> without it (trivially DoS-ed via slowloris)
>
> > Reading that advisory, I see this more of an issue now. He has
> > leveraged Unicorn and Puma along with Rails to demonstrate some of the
> > proof of concepts. Under the section Vulnerabilities and conditions of
> > the report, he has specified different conditions and configurations
> > which allow for this vector.
> >
> > I agree with Aaron Patterson's (Rails Staff) decision on that report,
> > this should be fixed in Unicorn and Puma directly, and Puma has
> > already fixed this and issued an advisory:
> > https://github.com/puma/puma/security/advisories/GHSA-84j7-475p-hp8v.
> >
> > I believe there is enough of a risk to fix this issue. What do you think?
>
> While we follow puma on some things (as in recent non-rack
> features), I'm not sure if this affects unicorn the same way it
> affects puma and other servers (that are supported without nginx).
>
> Fwiw, I've been against client-side JavaScript for a decade, now.
> Libre license or not; the complexity of JS gives us a never-ending
> stream of vulnerabilities, wasted RAM, CPU cycles, and bandwidth
> use from constant software updates needed to continue the game of
> whack-a-mole.
>
> rest of thread below (top-posting corrected):
>
> > > On Sun, Jan 3, 2021 at 10:20 PM Eric Wong <bofh@yhbt.net> wrote:
> > > >
> > > > Sam Sanoop <sams@snyk.io> wrote:
> > > > > Hey Eric,
> > > > >
> > > > > Happy New Year. I want to follow up on the [RFC] http_response: ignore
> > > > > invalid header response character RFC for the CRLF injection we spoke
> > > > > about previously. I wanted to know what would be the timeline for your
> > > > > patch to get merged and what additional steps there are before that
> > > > > can happen.
> > > >
> > > > Hi Sam, honestly I have no idea if it's even necessary...
> > > > I've had no feedback since 2020-11-26:
> > > >   https://yhbt.net/unicorn-public/20201126115902.GA8883@dcvr/
> > > >
> > > > Meanwhile 5.8.0 was released 2020-12-24 with a feature somebody
> > > > actually cared about.
> > > >
> > > > Again, unicorn falls over without nginx in front of it anyways,
> > > > so maybe nginx already guards against this and extra code is
> > > > unnecessary on my end.
> >
> > On Sun, Jan 3, 2021 at 11:03 PM Sam Sanoop <sams@snyk.io> wrote:
> > >
> > > Hey Eric,
> > >
> > > No problem, I understand. Since the injection here is happening within
> > > the response, I am not convinced if this is exploitable as well. I
> > > have let the reporter of this issue know what your stance is. I
> > > mentioned if he can provide a better Proof of Concept where this is
> > > exploitable in the context of a http request, and look into this a bit
> > > further, we can open up discussion again, or else, this is not worth
> > > fixing.



-- 

Sam Sanoop
Security Analyst

^ permalink raw reply	[relevance 1%]

* Re: [RFC] http_response: ignore invalid header response characters
  @ 2021-01-06 17:53  0% ` Eric Wong
  2021-01-13 23:20  1%   ` Sam Sanoop
  2021-02-26 11:15  3% ` Eric Wong
  1 sibling, 1 reply; 200+ results
From: Eric Wong @ 2021-01-06 17:53 UTC (permalink / raw)
  To: Sam Sanoop; +Cc: unicorn-public

Sam Sanoop <sams@snyk.io> wrote:
> Hi Eric,

cf. https://yhbt.net/unicorn-public/20201126115902.GA8883@dcvr/
(private followup <CAEQPCYJJdriMQyDNXimCS_kBrc_=DxhxJ66YdJmCe7ZXr-Zbvg@mail.gmail.com>)

Hi Sam, I'm moving this to the public list.  Please keep
unicorn-public@yhbt.net Cc-ed (no need to quote, archives are
public and mirrored to several places).

> I wanted to bring this up again. I spoke with the researcher
> (@ooooooo_q) who disclosed this issue to us. He released the full
> details including the HackerOne report pubclily which provided more
> clarity about this issue. That can be read here:
> https://hackerone.com/reports/904059#activity-8945588

Note: I can't read anything on hackerone due to the JavaScript
requirement.  If somebody can copy the text here, that'd be
great.

> That report was initially disclosed to the Ruby on Rails maintainers.
> In certain conditions, he was also able to leverage Puma and Unicorn
> to exploit the issues mentioned on the report. The issues which
> related to Unicorn were:
> 
> * Open Redirect to HTTP header injection - (including \r in the redirect URL)
> * A user interaction XSS  - Which leverages
> \rjavascript:alert(location) as the redirect destination

Does this happen when unicorn is behind nginx?

unicorn was always designed to run behind nginx and falls over badly
without it (trivially DoS-ed via slowloris)

> Reading that advisory, I see this more of an issue now. He has
> leveraged Unicorn and Puma along with Rails to demonstrate some of the
> proof of concepts. Under the section Vulnerabilities and conditions of
> the report, he has specified different conditions and configurations
> which allow for this vector.
> 
> I agree with Aaron Patterson's (Rails Staff) decision on that report,
> this should be fixed in Unicorn and Puma directly, and Puma has
> already fixed this and issued an advisory:
> https://github.com/puma/puma/security/advisories/GHSA-84j7-475p-hp8v.
> 
> I believe there is enough of a risk to fix this issue. What do you think?

While we follow puma on some things (as in recent non-rack
features), I'm not sure if this affects unicorn the same way it
affects puma and other servers (that are supported without nginx).

Fwiw, I've been against client-side JavaScript for a decade, now.
Libre license or not; the complexity of JS gives us a never-ending
stream of vulnerabilities, wasted RAM, CPU cycles, and bandwidth
use from constant software updates needed to continue the game of
whack-a-mole.

rest of thread below (top-posting corrected):

> > On Sun, Jan 3, 2021 at 10:20 PM Eric Wong <bofh@yhbt.net> wrote:
> > >
> > > Sam Sanoop <sams@snyk.io> wrote:
> > > > Hey Eric,
> > > >
> > > > Happy New Year. I want to follow up on the [RFC] http_response: ignore
> > > > invalid header response character RFC for the CRLF injection we spoke
> > > > about previously. I wanted to know what would be the timeline for your
> > > > patch to get merged and what additional steps there are before that
> > > > can happen.
> > >
> > > Hi Sam, honestly I have no idea if it's even necessary...
> > > I've had no feedback since 2020-11-26:
> > >   https://yhbt.net/unicorn-public/20201126115902.GA8883@dcvr/
> > >
> > > Meanwhile 5.8.0 was released 2020-12-24 with a feature somebody
> > > actually cared about.
> > >
> > > Again, unicorn falls over without nginx in front of it anyways,
> > > so maybe nginx already guards against this and extra code is
> > > unnecessary on my end.
>
> On Sun, Jan 3, 2021 at 11:03 PM Sam Sanoop <sams@snyk.io> wrote:
> >
> > Hey Eric,
> >
> > No problem, I understand. Since the injection here is happening within
> > the response, I am not convinced if this is exploitable as well. I
> > have let the reporter of this issue know what your stance is. I
> > mentioned if he can provide a better Proof of Concept where this is
> > exploitable in the context of a http request, and look into this a bit
> > further, we can open up discussion again, or else, this is not worth
> > fixing.

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] Add early hints support
  @ 2020-07-16 12:16  2%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2020-07-16 12:16 UTC (permalink / raw)
  To: Jean Boussier; +Cc: unicorn-public

Jean Boussier <jean.boussier@shopify.com> wrote:
> Thanks for the very timely response.
> 
> > Since this RDoc ends up on the website, links to any relevant
> > Rails documentation and RFC 8297 would also be appropriate.
> > Otherwise non-Rails users like me might have no clue what
> > it's for.
> 
> I updated the documentation, let me know what you think of it.

It's fine, pushed for now.  Will release in a few days or a week
in case there's comments from others.

> > Are the method calls for .to_s necessary?
> 
> I don't think they are, I mostly took inspiration from the puma implementation
> that does all this defensive checks. Based on how that interface is
> used by Rails, we could assume both keys and values are strings already.
> 
> I simplified the implementation.

OK.

> > Eep, extra branch...  What's the performance impact for existing
> > users when not activated? (on Unix sockets).
> 
> Extremely small.

Alright, numbers would've been helpful but I'll take your word
for it.

If we get more branches in the main loop for other new features;
I wonder if generating the code for process_client dynamically
at initialize-time is worth it to avoid branches in the main
loop... (or if there's some way to hint MJIT to do that).

> > Perhaps bypassing the method and accessing the @early_hints ivar
> > directly can be slightly faster w/o method dispatch.  That
> > should also allow using attr_writer instead of attr_accessor,
> > I think.
> 
>  attr_reader is very optimized in MRI, it's barely slower than @early_hints.
> Also it ensure that it doesn't emit a warning in verbose mode if the variable
> isn't initialized.

Thanks again.

^ permalink raw reply	[relevance 2%]

* [PATCH] prevent single listener from monopolizing a worker
  @ 2020-04-16  9:24  4%         ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2020-04-16  9:24 UTC (permalink / raw)
  To: Stan Hu; +Cc: unicorn-public

Stan Hu <stanhu@gmail.com> wrote:
> That seems to work, thanks!

Thanks for confirming.  I'll push the patch below out.
(ugh, dealing with crazy packet loss all around)

Expect a v5.6.0 release within a few days or week at most.
(hopefully no regressions).

And... I wonder, are most deployments nowadays single listener?

I don't think I've used multiple listeners for this aside from
experiments in the early days.

---------8<----------
Subject: [PATCH] prevent single listener from monopolizing a worker

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
single listener.

Thanks for Stan Hu for reporting and testing.

Reported-by: Stan Hu <stanhu@gmail.com>
Tested-by: Stan Hu <stanhu@gmail.com>
Link: https://yhbt.net/unicorn-public/CAMBWrQ=Yh42MPtzJCEO7XryVknDNetRMuA87irWfqVuLdJmiBQ@mail.gmail.com/
---
 lib/unicorn/http_server.rb | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index a52931a..45a2e97 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -686,6 +686,7 @@ def worker_loop(worker)
     trap(:USR1) { nr = -65536 }
 
     ready = readers.dup
+    nr_listeners = readers.size
     @after_worker_ready.call(self, worker)
 
     begin
@@ -708,7 +709,7 @@ def worker_loop(worker)
       # 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
+      if nr == nr_listeners
         tmp = ready.dup
         redo
       end

^ permalink raw reply related	[relevance 4%]

* Re: Sustained queuing on one listener can block requests from other listeners
  2020-04-16  5:46  5%   ` Stan Hu
@ 2020-04-16  6:59  5%     ` Eric Wong
    0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2020-04-16  6:59 UTC (permalink / raw)
  To: Stan Hu; +Cc: unicorn-public

Stan Hu <stanhu@gmail.com> wrote:
> Thanks, Eric. That patch didn't work; it spun the CPU. I think this worked?

Oops, sorry.  I was too eager to drop `nr += 1' :x

Btw, please don't top post.  Fwiw, I wouldn't mind if we stopped
quoting at all on publically-archived lists (saves space and
bandwidth).

> +++ b/lib/unicorn/http_server.rb
> @@ -708,7 +708,7 @@ def worker_loop(worker)
>        # 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
> +      if nr == readers.size
>          tmp = ready.dup
>          redo
>        end

Your patch looks close.  However the `readers' array gets
dropped on SIGQUIT with `nuke_listeners!', so `readers.size'
is unstable.

How about this?

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index a52931a..45a2e97 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -686,6 +686,7 @@ def worker_loop(worker)
     trap(:USR1) { nr = -65536 }
 
     ready = readers.dup
+    nr_listeners = readers.size
     @after_worker_ready.call(self, worker)
 
     begin
@@ -708,7 +709,7 @@ def worker_loop(worker)
       # 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
+      if nr == nr_listeners
         tmp = ready.dup
         redo
       end

Thanks

^ permalink raw reply related	[relevance 5%]

* Re: Sustained queuing on one listener can block requests from other listeners
  2020-04-15  5:26  4% ` Eric Wong
@ 2020-04-16  5:46  5%   ` Stan Hu
  2020-04-16  6:59  5%     ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Stan Hu @ 2020-04-16  5:46 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Thanks, Eric. That patch didn't work; it spun the CPU. I think this worked?

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index a52931a..aaa4955 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -708,7 +708,7 @@ def worker_loop(worker)
       # 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
+      if nr == readers.size
         tmp = ready.dup
         redo
       end

On Tue, Apr 14, 2020 at 10:26 PM Eric Wong <e@yhbt.net> wrote:
>
> Stan Hu <stanhu@gmail.com> wrote:
> > My unicorn.rb has two listeners:
> >
> > listen "127.0.0.1:8080", :tcp_nopush => false
> > listen "/var/run/unicorn.socket", :backlog => 1024
>
> Fwiw, lowering :backlog may make sense if you got other
> hosts/instances.  More below..
>
> > We found that because of the greedy attempt to accept new connections
> > before calling select() in
> > https://github.com/defunkt/unicorn/blob/981f561a726bb4307d01e4a09a308edba8d69fe3/lib/unicorn/http_server.rb#L707-L714,
> > listeners on another socket stall out until the first listener is
> > drained. We would expect Unicorn to round-robin between the two
> > listeners, but that doesn't happen as long as there is work to be done
> > for the first listener. We've verified that deleting that `redo` block
> > fixes the problem.
> >
> > What do you think about the various options?
> >
> > 1. Only running that redo block if there is one listener
>
> That seems reasonable, or if ready.size == nr_listeners
> (proposed patch below)
>
> > 2. Removing the redo block entirely
>
> From what I recall ages ago, select() entry cost is pretty high
> and I remember that redo helping a fair bit even in 2009 with
> simple apps.  Syscall cost is even higher now with CPU
> vulnerability mitigations, and Ruby 1.9+ GVL release+reacquire
> is also a penalty I didn't have when developing this on 1.8.
>
> Do you have time+hardware to benchmark either approach on a
> simple app?  I no longer have stable/reliable hardware for
> benchmarking.  Thanks.
>
> Totally untested patch to try approach #1
>
> diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
> index a52931a..69f1f60 100644
> --- a/lib/unicorn/http_server.rb
> +++ b/lib/unicorn/http_server.rb
> @@ -686,6 +686,7 @@ def worker_loop(worker)
>      trap(:USR1) { nr = -65536 }
>
>      ready = readers.dup
> +    nr_listeners = readers.size
>      @after_worker_ready.call(self, worker)
>
>      begin
> @@ -698,7 +699,6 @@ def worker_loop(worker)
>          # but that will return false
>          if client = sock.kgio_tryaccept
>            process_client(client)
> -          nr += 1
>            worker.tick = time_now.to_i
>          end
>          break if nr < 0
> @@ -708,7 +708,7 @@ def worker_loop(worker)
>        # 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
> +      if ready.size == nr_listeners
>          tmp = ready.dup
>          redo
>        end
>
>
>
> And `nr' can probably just be a boolean `reopen' flag if we're
> not overloading it as a counter.

^ permalink raw reply related	[relevance 5%]

* Re: Sustained queuing on one listener can block requests from other listeners
  @ 2020-04-15  5:26  4% ` Eric Wong
  2020-04-16  5:46  5%   ` Stan Hu
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2020-04-15  5:26 UTC (permalink / raw)
  To: Stan Hu; +Cc: unicorn-public

Stan Hu <stanhu@gmail.com> wrote:
> My unicorn.rb has two listeners:
> 
> listen "127.0.0.1:8080", :tcp_nopush => false
> listen "/var/run/unicorn.socket", :backlog => 1024

Fwiw, lowering :backlog may make sense if you got other
hosts/instances.  More below..

> We found that because of the greedy attempt to accept new connections
> before calling select() in
> https://github.com/defunkt/unicorn/blob/981f561a726bb4307d01e4a09a308edba8d69fe3/lib/unicorn/http_server.rb#L707-L714,
> listeners on another socket stall out until the first listener is
> drained. We would expect Unicorn to round-robin between the two
> listeners, but that doesn't happen as long as there is work to be done
> for the first listener. We've verified that deleting that `redo` block
> fixes the problem.
> 
> What do you think about the various options?
> 
> 1. Only running that redo block if there is one listener

That seems reasonable, or if ready.size == nr_listeners
(proposed patch below)

> 2. Removing the redo block entirely

From what I recall ages ago, select() entry cost is pretty high
and I remember that redo helping a fair bit even in 2009 with
simple apps.  Syscall cost is even higher now with CPU
vulnerability mitigations, and Ruby 1.9+ GVL release+reacquire
is also a penalty I didn't have when developing this on 1.8.

Do you have time+hardware to benchmark either approach on a
simple app?  I no longer have stable/reliable hardware for
benchmarking.  Thanks.

Totally untested patch to try approach #1

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index a52931a..69f1f60 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -686,6 +686,7 @@ def worker_loop(worker)
     trap(:USR1) { nr = -65536 }
 
     ready = readers.dup
+    nr_listeners = readers.size
     @after_worker_ready.call(self, worker)
 
     begin
@@ -698,7 +699,6 @@ def worker_loop(worker)
         # but that will return false
         if client = sock.kgio_tryaccept
           process_client(client)
-          nr += 1
           worker.tick = time_now.to_i
         end
         break if nr < 0
@@ -708,7 +708,7 @@ def worker_loop(worker)
       # 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
+      if ready.size == nr_listeners
         tmp = ready.dup
         redo
       end



And `nr' can probably just be a boolean `reopen' flag if we're
not overloading it as a counter.

^ permalink raw reply related	[relevance 4%]

* [PATCH] doc: s/bogomips.org/yhbt.net/g
  2020-01-14  7:46  1% [PATCH] doc: s/bogomips.org/yhbt.net/g Eric Wong
@ 2020-01-14  7:46  1% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2020-01-14  7:46 UTC (permalink / raw)
  To: unicorn-public

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).
---
 .olddoc.yml                      | 19 ++++++++++++-------
 Documentation/unicorn.1          |  8 ++++----
 Documentation/unicorn_rails.1    |  8 ++++----
 FAQ                              |  2 +-
 GNUmakefile                      |  4 ++--
 HACKING                          |  2 +-
 ISSUES                           | 24 ++++++++++++------------
 KNOWN_ISSUES                     |  4 ++--
 Links                            | 10 +++++-----
 README                           | 12 ++++++------
 SIGNALS                          |  2 +-
 Sandbox                          |  4 ++--
 archive/slrnpull.conf            |  2 +-
 examples/big_app_gc.rb           |  2 +-
 examples/logrotate.conf          |  4 ++--
 examples/nginx.conf              |  2 +-
 examples/unicorn.conf.minimal.rb |  4 ++--
 examples/unicorn.conf.rb         |  4 ++--
 ext/unicorn_http/unicorn_http.rl |  2 +-
 lib/unicorn.rb                   |  2 +-
 lib/unicorn/configurator.rb      |  6 +++---
 lib/unicorn/http_server.rb       |  2 +-
 lib/unicorn/oob_gc.rb            |  4 ++--
 unicorn.gemspec                  |  4 ++--
 24 files changed, 71 insertions(+), 66 deletions(-)

diff --git a/.olddoc.yml b/.olddoc.yml
index d2d340f..0609bdb 100644
--- a/.olddoc.yml
+++ b/.olddoc.yml
@@ -1,8 +1,9 @@
 ---
-cgit_url: https://bogomips.org/unicorn.git
-git_url: https://bogomips.org/unicorn.git
-rdoc_url: https://bogomips.org/unicorn/
-ml_url: https://bogomips.org/unicorn-public/
+cgit_url: https://yhbt.net/unicorn.git
+rdoc_url: https://yhbt.net/unicorn/
+ml_url:
+- https://yhbt.net/unicorn-public/
+- http://ou63pmih66umazou.onion/unicorn-public/
 merge_html:
   unicorn_1: Documentation/unicorn.1.html
   unicorn_rails_1: Documentation/unicorn_rails.1.html
@@ -11,7 +12,11 @@ noindex:
 - LATEST
 - TODO
 - unicorn_rails_1
-public_email: unicorn-public@bogomips.org
+public_email: unicorn-public@yhbt.net
 nntp_url:
-  - nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
-  - nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+- nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
+- nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.unicorn
+- nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
+source_code:
+- git clone https://yhbt.net/unicorn.git
+- torsocks git clone http://ou63pmih66umazou.onion/unicorn.git
diff --git a/Documentation/unicorn.1 b/Documentation/unicorn.1
index 3f8cb96..d76d40f 100644
--- a/Documentation/unicorn.1
+++ b/Documentation/unicorn.1
@@ -154,7 +154,7 @@ TTIN \- increment the number of worker processes by one
 .IP \[bu] 2
 TTOU \- decrement the number of worker processes by one
 .PP
-See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
+See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
 full description of all signals used by Unicorn.
 .SH RACK ENVIRONMENT
 .PP
@@ -204,11 +204,11 @@ the unicorn config file.
 \f[I]Rack::Builder\f[] ri/RDoc
 .IP \[bu] 2
 \f[I]Unicorn::Configurator\f[] ri/RDoc
-.UR https://bogomips.org/unicorn/Unicorn/Configurator.html
+.UR https://yhbt.net/unicorn/Unicorn/Configurator.html
 .UE
 .IP \[bu] 2
 unicorn RDoc
-.UR https://bogomips.org/unicorn/
+.UR https://yhbt.net/unicorn/
 .UE
 .IP \[bu] 2
 Rack RDoc
@@ -219,4 +219,4 @@ Rackup HowTo
 .UR https://github.com/rack/rack/wiki/(tutorial)-rackup-howto
 .UE
 .SH AUTHORS
-The Unicorn Community <unicorn-public@bogomips.org>.
+The Unicorn Community <unicorn-public@yhbt.net>.
diff --git a/Documentation/unicorn_rails.1 b/Documentation/unicorn_rails.1
index 71c80be..fec0a2a 100644
--- a/Documentation/unicorn_rails.1
+++ b/Documentation/unicorn_rails.1
@@ -180,7 +180,7 @@ TTIN \- increment the number of worker processes by one
 .IP \[bu] 2
 TTOU \- decrement the number of worker processes by one
 .PP
-See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
+See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
 full description of all signals used by Unicorn.
 .SH SEE ALSO
 .IP \[bu] 2
@@ -189,11 +189,11 @@ unicorn(1)
 \f[I]Rack::Builder\f[] ri/RDoc
 .IP \[bu] 2
 \f[I]Unicorn::Configurator\f[] ri/RDoc
-.UR https://bogomips.org/unicorn/Unicorn/Configurator.html
+.UR https://yhbt.net/unicorn/Unicorn/Configurator.html
 .UE
 .IP \[bu] 2
 unicorn RDoc
-.UR https://bogomips.org/unicorn/
+.UR https://yhbt.net/unicorn/
 .UE
 .IP \[bu] 2
 Rack RDoc
@@ -204,4 +204,4 @@ Rackup HowTo
 .UR https://github.com/rack/rack/wiki/(tutorial)-rackup-howto
 .UE
 .SH AUTHORS
-The Unicorn Community <unicorn-public@bogomips.org>.
+The Unicorn Community <unicorn-public@yhbt.net>.
diff --git a/FAQ b/FAQ
index 4ae2034..018ca92 100644
--- a/FAQ
+++ b/FAQ
@@ -7,7 +7,7 @@ drained entirely by the application.  This may happen when request
 bodies are gzipped, as unicorn reads request body data lazily to avoid
 overhead from bad requests.
 
-Ref: https://bogomips.org/unicorn-public/FC91211E-FD32-432C-92FC-0318714C2170@zendesk.com/
+Ref: https://yhbt.net/unicorn-public/FC91211E-FD32-432C-92FC-0318714C2170@zendesk.com/
 
 === Why aren't my Rails log files rotated when I use SIGUSR1?
 
diff --git a/GNUmakefile b/GNUmakefile
index 94c46ee..eac3473 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -193,13 +193,13 @@ doc: .document $(ext)/unicorn_http.c man html .olddoc.yml $(PLACEHOLDERS)
 	install -m644 $(man1_paths) doc/
 	tar cf - $$(git ls-files examples/) | (cd doc && tar xf -)
 
-# publishes docs to https://bogomips.org/unicorn/
+# publishes docs to https://yhbt.net/unicorn/
 publish_doc:
 	-git set-file-times
 	$(MAKE) doc
 	$(MAKE) doc_gz
 	chmod 644 $$(find doc -type f)
-	$(RSYNC) -av doc/ bogomips.org:/srv/bogomips/unicorn/
+	$(RSYNC) -av doc/ yhbt.net:/srv/yhbt/unicorn/
 	git ls-files | xargs touch
 
 # Create gzip variants of the same timestamp as the original so nginx
diff --git a/HACKING b/HACKING
index be1bb85..976bf92 100644
--- a/HACKING
+++ b/HACKING
@@ -57,7 +57,7 @@ Please wrap documentation at 72 characters-per-line or less (long URLs
 are exempt) so it is comfortably readable from terminals.
 
 When referencing mailing list posts, use
-<tt>https://bogomips.org/unicorn-public/$MESSAGE_ID/</tt> if possible
+<tt>https://yhbt.net/unicorn-public/$MESSAGE_ID/</tt> if possible
 since the Message-ID remains searchable even if a particular site
 becomes unavailable.
 
diff --git a/ISSUES b/ISSUES
index 473da2f..d11dc56 100644
--- a/ISSUES
+++ b/ISSUES
@@ -1,9 +1,9 @@
 = Issues
 
-mailto:unicorn-public@bogomips.org is the best place to report bugs,
+mailto:unicorn-public@yhbt.net is the best place to report bugs,
 submit patches and/or obtain support after you have searched the
-{email archives}[https://bogomips.org/unicorn-public/] and
-{documentation}[https://bogomips.org/unicorn/].
+{email archives}[https://yhbt.net/unicorn-public/] and
+{documentation}[https://yhbt.net/unicorn/].
 
 * No subscription will ever be required to email us
 * Cc: all participants in a thread or commit, as subscription is optional
@@ -12,12 +12,12 @@ submit patches and/or obtain support after you have searched the
 * Do not send HTML mail or images,
   they hurt reader privacy and will be flagged as spam
 * Anonymous and pseudonymous messages will ALWAYS be welcome
-* The email submission port (587) is enabled on the bogomips.org MX:
-  https://bogomips.org/unicorn-public/20141004232241.GA23908@dcvr.yhbt.net/t/
+* The email submission port (587) is enabled on the yhbt.net MX:
+  https://yhbt.net/unicorn-public/20141004232241.GA23908@dcvr.yhbt.net/t/
 
 We will never have a centralized or formal bug tracker.  Instead we
 can interoperate with any bug tracker which can Cc: us plain-text to
-mailto:unicorn-public@bogomips.org   This includes the Debian BTS
+mailto:unicorn-public@yhbt.net   This includes the Debian BTS
 at https://bugs.debian.org/unicorn and possibly others.
 
 If your issue is of a sensitive nature or you're just shy in public,
@@ -73,10 +73,10 @@ document distributed with git) on guidelines for patch submission.
 
 == Contact Info
 
-* public: mailto:unicorn-public@bogomips.org
-* nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+* public: mailto:unicorn-public@yhbt.net
+* nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
 * nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
-* https://bogomips.org/unicorn-public/
+* https://yhbt.net/unicorn-public/
 * http://ou63pmih66umazou.onion/unicorn-public/
 
 Mailing list subscription is optional, so Cc: all participants.
@@ -84,13 +84,13 @@ Mailing list subscription is optional, so Cc: all participants.
 You can follow along via NNTP (read-only):
 
 	nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
-	nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+	nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
 
 Or Atom feeds:
 
-	https://bogomips.org/unicorn-public/new.atom
+	https://yhbt.net/unicorn-public/new.atom
 	http://ou63pmih66umazou.onion/unicorn-public/new.atom
 
-	The HTML archives at https://bogomips.org/unicorn-public/
+	The HTML archives at https://yhbt.net/unicorn-public/
 	also has links to per-thread Atom feeds and downloadable
 	mboxes.
diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES
index ebd4822..0017f20 100644
--- a/KNOWN_ISSUES
+++ b/KNOWN_ISSUES
@@ -9,7 +9,7 @@ acceptable solution.  Those issues are documented here.
   handlers.
 
 * Issues with FreeBSD jails can be worked around as documented by Tatsuya Ono:
-  https://bogomips.org/unicorn-public/CAHBuKRj09FdxAgzsefJWotexw-7JYZGJMtgUp_dhjPz9VbKD6Q@mail.gmail.com/
+  https://yhbt.net/unicorn-public/CAHBuKRj09FdxAgzsefJWotexw-7JYZGJMtgUp_dhjPz9VbKD6Q@mail.gmail.com/
 
 * PRNGs (pseudo-random number generators) loaded before forking
   (e.g. "preload_app true") may need to have their internal state
@@ -60,7 +60,7 @@ acceptable solution.  Those issues are documented here.
   application to use Rails 2.3.2 and you have no other choice, then
   you may edit your unicorn gemspec and remove the Rack dependency.
 
-  ref: https://bogomips.org/unicorn-public/20091014221552.GA30624@dcvr.yhbt.net/
+  ref: https://yhbt.net/unicorn-public/20091014221552.GA30624@dcvr.yhbt.net/
   Note: the workaround described in the article above only made
   the issue more subtle and we didn't notice them immediately.
 
diff --git a/Links b/Links
index 10551a6..f81142d 100644
--- a/Links
+++ b/Links
@@ -2,7 +2,7 @@
 
 If you're interested in unicorn, you may be interested in some of the projects
 listed below.  If you have any links to add/change/remove, please tell us at
-mailto:unicorn-public@bogomips.org!
+mailto:unicorn-public@yhbt.net!
 
 == Disclaimer
 
@@ -23,10 +23,10 @@ or services behind them.
 * {golden_brindle}[https://github.com/simonoff/golden_brindle] - tool to
   manage multiple unicorn instances/applications on a single server
 
-* {raindrops}[https://bogomips.org/raindrops/] - real-time stats for
+* {raindrops}[https://yhbt.net/raindrops/] - real-time stats for
   preforking Rack servers
 
-* {UnXF}[https://bogomips.org/unxf/]  Un-X-Forward* the Rack environment,
+* {UnXF}[https://yhbt.net/unxf/]  Un-X-Forward* the Rack environment,
   useful since unicorn is designed to be deployed behind a reverse proxy.
 
 === unicorn is written to work with
@@ -52,7 +52,7 @@ or services behind them.
 * {Mongrel}[https://rubygems.org/gems/mongrel] - the awesome webserver
   unicorn is based on.  A historical archive of the mongrel dev list
   featuring early discussions of unicorn is available at:
-  https://bogomips.org/mongrel-devel/
+  https://yhbt.net/mongrel-devel/
 
-* {david}[https://bogomips.org/david.git] - a tool to explain why you need
+* {david}[https://yhbt.net/david.git] - a tool to explain why you need
   nginx in front of unicorn
diff --git a/README b/README
index 89467fc..0e95f48 100644
--- a/README
+++ b/README
@@ -80,12 +80,12 @@ You may install it via RubyGems on RubyGems.org:
 You can get the latest source via git from the following locations
 (these versions may not be stable):
 
-  https://bogomips.org/unicorn.git
+  https://yhbt.net/unicorn.git
   https://repo.or.cz/unicorn.git (mirror)
 
 You may browse the code from the web:
 
-* https://bogomips.org/unicorn.git
+* https://yhbt.net/unicorn.git
 * https://repo.or.cz/w/unicorn.git (gitweb)
 
 See the HACKING guide on how to contribute and build prerelease gems
@@ -133,13 +133,13 @@ and libraries which run on top of it.
 
 All feedback (bug reports, user/development dicussion, patches, pull
 requests) go to the mailing list/newsgroup.  See the ISSUES document for
-information on the {mailing list}[mailto:unicorn-public@bogomips.org].
+information on the {mailing list}[mailto:unicorn-public@yhbt.net].
 
-The mailing list is archived at https://bogomips.org/unicorn-public/
+The mailing list is archived at https://yhbt.net/unicorn-public/
 Read-only NNTP access is available at:
 nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn and
-nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
 
 For the latest on unicorn releases, you may also finger us at
-unicorn@bogomips.org or check our NEWS page (and subscribe to our Atom
+unicorn@yhbt.net or check our NEWS page (and subscribe to our Atom
 feed).
diff --git a/SIGNALS b/SIGNALS
index 1af851d..7321f2b 100644
--- a/SIGNALS
+++ b/SIGNALS
@@ -8,7 +8,7 @@ should be possible to easily share process management scripts between
 Unicorn and nginx.
 
 One example init script is distributed with unicorn:
-https://bogomips.org/unicorn/examples/init.sh
+https://yhbt.net/unicorn/examples/init.sh
 
 === Master Process
 
diff --git a/Sandbox b/Sandbox
index d0f915e..651e5cd 100644
--- a/Sandbox
+++ b/Sandbox
@@ -34,7 +34,7 @@ is the primary issue with sandboxing tools such as Bundler and Isolate.
 If you're bundling unicorn, use "bundle exec unicorn" (or "bundle exec
 unicorn_rails") to start unicorn with the correct environment variables
 
-ref: https://bogomips.org/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
+ref: https://yhbt.net/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
 
 Otherwise (if you choose to not sandbox your unicorn installation), we
 expect the tips for Isolate (below) apply, too.
@@ -44,7 +44,7 @@ expect the tips for Isolate (below) apply, too.
 This is no longer be an issue as of bundler 0.9.17
 
 ref:
-https://bogomips.org/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
+https://yhbt.net/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
 
 === BUNDLE_GEMFILE for Capistrano users
 
diff --git a/archive/slrnpull.conf b/archive/slrnpull.conf
index fcfcafe..fd04f97 100644
--- a/archive/slrnpull.conf
+++ b/archive/slrnpull.conf
@@ -1,4 +1,4 @@
 # group_name                         max        expire     headers_only
 gmane.comp.lang.ruby.unicorn.general 1000000000 1000000000 0
 
-# usage: slrnpull -d $PWD -h news.gmane.org --no-post
+# usage: slrnpull -d $PWD -h news.gmane.io --no-post
diff --git a/examples/big_app_gc.rb b/examples/big_app_gc.rb
index 9d05719..c1bae10 100644
--- a/examples/big_app_gc.rb
+++ b/examples/big_app_gc.rb
@@ -1,2 +1,2 @@
-# see {Unicorn::OobGC}[https://bogomips.org/unicorn/Unicorn/OobGC.html]
+# see {Unicorn::OobGC}[https://yhbt.net/unicorn/Unicorn/OobGC.html]
 # Unicorn::OobGC was broken in Unicorn v3.3.1 - v3.6.1 and fixed in v3.6.2
diff --git a/examples/logrotate.conf b/examples/logrotate.conf
index 77a01b5..c3aa40d 100644
--- a/examples/logrotate.conf
+++ b/examples/logrotate.conf
@@ -5,7 +5,7 @@
 #    https://linux.die.net/man/8/logrotate
 #
 # public logrotate-related discussion in our archives:
-#    https://bogomips.org/unicorn-public/?q=logrotate
+#    https://yhbt.net/unicorn-public/?q=logrotate
 
 # Modify the following glob to match the logfiles your app writes to:
 /var/log/unicorn_app/*.log {
@@ -33,7 +33,7 @@
 		systemctl kill -s SIGUSR1 unicorn@2.service
 
 		# Examples for other process management systems appreciated
-		# Mail us at unicorn-public@bogomips.org
+		# Mail us at unicorn-public@yhbt.net
 		# (see above for archives)
 
 		# If you use a pid file and assuming your pid file
diff --git a/examples/nginx.conf b/examples/nginx.conf
index b6b69c1..c5026f9 100644
--- a/examples/nginx.conf
+++ b/examples/nginx.conf
@@ -113,7 +113,7 @@ http {
     # try_files directive appeared in in nginx 0.7.27 and has stabilized
     # over time.  Older versions of nginx (e.g. 0.6.x) requires
     # "if (!-f $request_filename)" which was less efficient:
-    # https://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
+    # https://yhbt.net/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
     try_files $uri/index.html $uri.html $uri @app;
 
     location @app {
diff --git a/examples/unicorn.conf.minimal.rb b/examples/unicorn.conf.minimal.rb
index 2d1bf0a..46fd634 100644
--- a/examples/unicorn.conf.minimal.rb
+++ b/examples/unicorn.conf.minimal.rb
@@ -1,9 +1,9 @@
 # Minimal sample configuration file for Unicorn (not Rack) when used
 # with daemonization (unicorn -D) started in your working directory.
 #
-# See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
+# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
 # documentation.
-# See also https://bogomips.org/unicorn/examples/unicorn.conf.rb for
+# See also https://yhbt.net/unicorn/examples/unicorn.conf.rb for
 # a more verbose configuration using more features.
 
 listen 2007 # by default Unicorn listens on port 8080
diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index d2897ef..d90bdc4 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -2,10 +2,10 @@
 #
 # This configuration file documents many features of Unicorn
 # that may not be needed for some applications. See
-# https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
+# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
 # for a much simpler configuration file.
 #
-# See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
+# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
 # documentation.
 
 # Use at least one worker per core if you're on a dedicated server,
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 8ef23bc..dfe3a63 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -487,7 +487,7 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
      * and X-Forwarded-Proto handling from this parser?  We've had it
      * forever and nobody has said anything against it, either.
      * Anyways, please send comments to our public mailing list:
-     * unicorn-public@bogomips.org (no HTML mail, no subscription necessary)
+     * unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
      */
     scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
     if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index dd5dff4..d5991fe 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -96,7 +96,7 @@ def self.builder(ru, op)
 
   # returns an array of strings representing TCP listen socket addresses
   # and Unix domain socket paths.  This is useful for use with
-  # Raindrops::Middleware under Linux: https://bogomips.org/raindrops/
+  # Raindrops::Middleware under Linux: https://yhbt.net/raindrops/
   def self.listener_names
     Unicorn::HttpServer::LISTENERS.map do |io|
       Unicorn::SocketHelper.sock_name(io)
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index e8b76f5..c3a4f2d 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -3,11 +3,11 @@
 
 # Implements a simple DSL for configuring a unicorn server.
 #
-# See https://bogomips.org/unicorn/examples/unicorn.conf.rb and
-# https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
+# See https://yhbt.net/unicorn/examples/unicorn.conf.rb and
+# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
 # example configuration files.  An example config file for use with
 # nginx is also available at
-# https://bogomips.org/unicorn/examples/nginx.conf
+# https://yhbt.net/unicorn/examples/nginx.conf
 #
 # See the link:/TUNING.html document for more information on tuning unicorn.
 class Unicorn::Configurator
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 5334fa0..a52931a 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -6,7 +6,7 @@
 # forked worker children.
 #
 # Users do not need to know the internals of this class, but reading the
-# {source}[https://bogomips.org/unicorn.git/tree/lib/unicorn/http_server.rb]
+# {source}[https://yhbt.net/unicorn.git/tree/lib/unicorn/http_server.rb]
 # is education for programmers wishing to learn how unicorn works.
 # See Unicorn::Configurator for information on how to configure unicorn.
 class Unicorn::HttpServer
diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb
index c4741a0..3b2f488 100644
--- a/lib/unicorn/oob_gc.rb
+++ b/lib/unicorn/oob_gc.rb
@@ -43,8 +43,8 @@
 #     use Unicorn::OobGC, 2, %r{\A/(?:expensive/foo|more_expensive/foo)}
 #
 # Feedback from users of early implementations of this module:
-# * https://bogomips.org/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
-# * https://bogomips.org/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
+# * https://yhbt.net/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
+# * https://yhbt.net/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
 
 module Unicorn::OobGC
 
diff --git a/unicorn.gemspec b/unicorn.gemspec
index ceea831..a189f8d 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -15,14 +15,14 @@
   s.authors = ['unicorn hackers']
   s.summary = 'Rack HTTP server for fast clients and Unix'
   s.description = File.read('README').split("\n\n")[1]
-  s.email = %q{unicorn-public@bogomips.org}
+  s.email = %q{unicorn-public@yhbt.net}
   s.executables = %w(unicorn unicorn_rails)
   s.extensions = %w(ext/unicorn_http/extconf.rb)
   s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
     File.exist?(f)
   end
   s.files = manifest
-  s.homepage = 'https://bogomips.org/unicorn/'
+  s.homepage = 'https://yhbt.net/unicorn/'
   s.test_files = test_files
 
   # technically we need ">= 1.9.3", too, but avoid the array here since

^ permalink raw reply related	[relevance 1%]

* [PATCH] doc: s/bogomips.org/yhbt.net/g
@ 2020-01-14  7:46  1% Eric Wong
  2020-01-14  7:46  1% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2020-01-14  7:46 UTC (permalink / raw)
  To: unicorn-public

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).
---
 .olddoc.yml                      | 19 ++++++++++++-------
 Documentation/unicorn.1          |  8 ++++----
 Documentation/unicorn_rails.1    |  8 ++++----
 FAQ                              |  2 +-
 GNUmakefile                      |  4 ++--
 HACKING                          |  2 +-
 ISSUES                           | 24 ++++++++++++------------
 KNOWN_ISSUES                     |  4 ++--
 Links                            | 10 +++++-----
 README                           | 12 ++++++------
 SIGNALS                          |  2 +-
 Sandbox                          |  4 ++--
 archive/slrnpull.conf            |  2 +-
 examples/big_app_gc.rb           |  2 +-
 examples/logrotate.conf          |  4 ++--
 examples/nginx.conf              |  2 +-
 examples/unicorn.conf.minimal.rb |  4 ++--
 examples/unicorn.conf.rb         |  4 ++--
 ext/unicorn_http/unicorn_http.rl |  2 +-
 lib/unicorn.rb                   |  2 +-
 lib/unicorn/configurator.rb      |  6 +++---
 lib/unicorn/http_server.rb       |  2 +-
 lib/unicorn/oob_gc.rb            |  4 ++--
 unicorn.gemspec                  |  4 ++--
 24 files changed, 71 insertions(+), 66 deletions(-)

diff --git a/.olddoc.yml b/.olddoc.yml
index d2d340f..0609bdb 100644
--- a/.olddoc.yml
+++ b/.olddoc.yml
@@ -2,4 +2,5 @@
-cgit_url: https://bogomips.org/unicorn.git
-git_url: https://bogomips.org/unicorn.git
-rdoc_url: https://bogomips.org/unicorn/
-ml_url: https://bogomips.org/unicorn-public/
+cgit_url: https://yhbt.net/unicorn.git
+rdoc_url: https://yhbt.net/unicorn/
+ml_url:
+- https://yhbt.net/unicorn-public/
+- http://ou63pmih66umazou.onion/unicorn-public/
@@ -14 +15 @@ noindex:
-public_email: unicorn-public@bogomips.org
+public_email: unicorn-public@yhbt.net
@@ -16,2 +17,6 @@ nntp_url:
-  - nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
-  - nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+- nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
+- nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.unicorn
+- nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
+source_code:
+- git clone https://yhbt.net/unicorn.git
+- torsocks git clone http://ou63pmih66umazou.onion/unicorn.git
diff --git a/Documentation/unicorn.1 b/Documentation/unicorn.1
index 3f8cb96..d76d40f 100644
--- a/Documentation/unicorn.1
+++ b/Documentation/unicorn.1
@@ -157 +157 @@ TTOU \- decrement the number of worker processes by one
-See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
+See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
@@ -207 +207 @@ the unicorn config file.
-.UR https://bogomips.org/unicorn/Unicorn/Configurator.html
+.UR https://yhbt.net/unicorn/Unicorn/Configurator.html
@@ -211 +211 @@ unicorn RDoc
-.UR https://bogomips.org/unicorn/
+.UR https://yhbt.net/unicorn/
@@ -222 +222 @@ Rackup HowTo
-The Unicorn Community <unicorn-public@bogomips.org>.
+The Unicorn Community <unicorn-public@yhbt.net>.
diff --git a/Documentation/unicorn_rails.1 b/Documentation/unicorn_rails.1
index 71c80be..fec0a2a 100644
--- a/Documentation/unicorn_rails.1
+++ b/Documentation/unicorn_rails.1
@@ -183 +183 @@ TTOU \- decrement the number of worker processes by one
-See the SIGNALS (https://bogomips.org/unicorn/SIGNALS.html) document for
+See the SIGNALS (https://yhbt.net/unicorn/SIGNALS.html) document for
@@ -192 +192 @@ unicorn(1)
-.UR https://bogomips.org/unicorn/Unicorn/Configurator.html
+.UR https://yhbt.net/unicorn/Unicorn/Configurator.html
@@ -196 +196 @@ unicorn RDoc
-.UR https://bogomips.org/unicorn/
+.UR https://yhbt.net/unicorn/
@@ -207 +207 @@ Rackup HowTo
-The Unicorn Community <unicorn-public@bogomips.org>.
+The Unicorn Community <unicorn-public@yhbt.net>.
diff --git a/FAQ b/FAQ
index 4ae2034..018ca92 100644
--- a/FAQ
+++ b/FAQ
@@ -10 +10 @@ overhead from bad requests.
-Ref: https://bogomips.org/unicorn-public/FC91211E-FD32-432C-92FC-0318714C2170@zendesk.com/
+Ref: https://yhbt.net/unicorn-public/FC91211E-FD32-432C-92FC-0318714C2170@zendesk.com/
diff --git a/GNUmakefile b/GNUmakefile
index 94c46ee..eac3473 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -196 +196 @@ doc: .document $(ext)/unicorn_http.c man html .olddoc.yml $(PLACEHOLDERS)
-# publishes docs to https://bogomips.org/unicorn/
+# publishes docs to https://yhbt.net/unicorn/
@@ -202 +202 @@ publish_doc:
-	$(RSYNC) -av doc/ bogomips.org:/srv/bogomips/unicorn/
+	$(RSYNC) -av doc/ yhbt.net:/srv/yhbt/unicorn/
diff --git a/HACKING b/HACKING
index be1bb85..976bf92 100644
--- a/HACKING
+++ b/HACKING
@@ -60 +60 @@ When referencing mailing list posts, use
-<tt>https://bogomips.org/unicorn-public/$MESSAGE_ID/</tt> if possible
+<tt>https://yhbt.net/unicorn-public/$MESSAGE_ID/</tt> if possible
diff --git a/ISSUES b/ISSUES
index 473da2f..d11dc56 100644
--- a/ISSUES
+++ b/ISSUES
@@ -3 +3 @@
-mailto:unicorn-public@bogomips.org is the best place to report bugs,
+mailto:unicorn-public@yhbt.net is the best place to report bugs,
@@ -5,2 +5,2 @@ submit patches and/or obtain support after you have searched the
-{email archives}[https://bogomips.org/unicorn-public/] and
-{documentation}[https://bogomips.org/unicorn/].
+{email archives}[https://yhbt.net/unicorn-public/] and
+{documentation}[https://yhbt.net/unicorn/].
@@ -15,2 +15,2 @@ submit patches and/or obtain support after you have searched the
-* The email submission port (587) is enabled on the bogomips.org MX:
-  https://bogomips.org/unicorn-public/20141004232241.GA23908@dcvr.yhbt.net/t/
+* The email submission port (587) is enabled on the yhbt.net MX:
+  https://yhbt.net/unicorn-public/20141004232241.GA23908@dcvr.yhbt.net/t/
@@ -20 +20 @@ can interoperate with any bug tracker which can Cc: us plain-text to
-mailto:unicorn-public@bogomips.org   This includes the Debian BTS
+mailto:unicorn-public@yhbt.net   This includes the Debian BTS
@@ -76,2 +76,2 @@ document distributed with git) on guidelines for patch submission.
-* public: mailto:unicorn-public@bogomips.org
-* nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+* public: mailto:unicorn-public@yhbt.net
+* nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
@@ -79 +79 @@ document distributed with git) on guidelines for patch submission.
-* https://bogomips.org/unicorn-public/
+* https://yhbt.net/unicorn-public/
@@ -87 +87 @@ You can follow along via NNTP (read-only):
-	nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+	nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
@@ -91 +91 @@ Or Atom feeds:
-	https://bogomips.org/unicorn-public/new.atom
+	https://yhbt.net/unicorn-public/new.atom
@@ -94 +94 @@ Or Atom feeds:
-	The HTML archives at https://bogomips.org/unicorn-public/
+	The HTML archives at https://yhbt.net/unicorn-public/
diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES
index ebd4822..0017f20 100644
--- a/KNOWN_ISSUES
+++ b/KNOWN_ISSUES
@@ -12 +12 @@ acceptable solution.  Those issues are documented here.
-  https://bogomips.org/unicorn-public/CAHBuKRj09FdxAgzsefJWotexw-7JYZGJMtgUp_dhjPz9VbKD6Q@mail.gmail.com/
+  https://yhbt.net/unicorn-public/CAHBuKRj09FdxAgzsefJWotexw-7JYZGJMtgUp_dhjPz9VbKD6Q@mail.gmail.com/
@@ -63 +63 @@ acceptable solution.  Those issues are documented here.
-  ref: https://bogomips.org/unicorn-public/20091014221552.GA30624@dcvr.yhbt.net/
+  ref: https://yhbt.net/unicorn-public/20091014221552.GA30624@dcvr.yhbt.net/
diff --git a/Links b/Links
index 10551a6..f81142d 100644
--- a/Links
+++ b/Links
@@ -5 +5 @@ listed below.  If you have any links to add/change/remove, please tell us at
-mailto:unicorn-public@bogomips.org!
+mailto:unicorn-public@yhbt.net!
@@ -26 +26 @@ or services behind them.
-* {raindrops}[https://bogomips.org/raindrops/] - real-time stats for
+* {raindrops}[https://yhbt.net/raindrops/] - real-time stats for
@@ -29 +29 @@ or services behind them.
-* {UnXF}[https://bogomips.org/unxf/]  Un-X-Forward* the Rack environment,
+* {UnXF}[https://yhbt.net/unxf/]  Un-X-Forward* the Rack environment,
@@ -55 +55 @@ or services behind them.
-  https://bogomips.org/mongrel-devel/
+  https://yhbt.net/mongrel-devel/
@@ -57 +57 @@ or services behind them.
-* {david}[https://bogomips.org/david.git] - a tool to explain why you need
+* {david}[https://yhbt.net/david.git] - a tool to explain why you need
diff --git a/README b/README
index 89467fc..0e95f48 100644
--- a/README
+++ b/README
@@ -83 +83 @@ You can get the latest source via git from the following locations
-  https://bogomips.org/unicorn.git
+  https://yhbt.net/unicorn.git
@@ -88 +88 @@ You may browse the code from the web:
-* https://bogomips.org/unicorn.git
+* https://yhbt.net/unicorn.git
@@ -136 +136 @@ requests) go to the mailing list/newsgroup.  See the ISSUES document for
-information on the {mailing list}[mailto:unicorn-public@bogomips.org].
+information on the {mailing list}[mailto:unicorn-public@yhbt.net].
@@ -138 +138 @@ information on the {mailing list}[mailto:unicorn-public@bogomips.org].
-The mailing list is archived at https://bogomips.org/unicorn-public/
+The mailing list is archived at https://yhbt.net/unicorn-public/
@@ -141 +141 @@ nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn and
-nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
+nntp://news.gmane.io/gmane.comp.lang.ruby.unicorn.general
@@ -144 +144 @@ For the latest on unicorn releases, you may also finger us at
-unicorn@bogomips.org or check our NEWS page (and subscribe to our Atom
+unicorn@yhbt.net or check our NEWS page (and subscribe to our Atom
diff --git a/SIGNALS b/SIGNALS
index 1af851d..7321f2b 100644
--- a/SIGNALS
+++ b/SIGNALS
@@ -11 +11 @@ One example init script is distributed with unicorn:
-https://bogomips.org/unicorn/examples/init.sh
+https://yhbt.net/unicorn/examples/init.sh
diff --git a/Sandbox b/Sandbox
index d0f915e..651e5cd 100644
--- a/Sandbox
+++ b/Sandbox
@@ -37 +37 @@ unicorn_rails") to start unicorn with the correct environment variables
-ref: https://bogomips.org/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
+ref: https://yhbt.net/unicorn-public/9ECF07C4-5216-47BE-961D-AFC0F0C82060@internetfamo.us/
@@ -47 +47 @@ ref:
-https://bogomips.org/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
+https://yhbt.net/unicorn-public/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com/
diff --git a/archive/slrnpull.conf b/archive/slrnpull.conf
index fcfcafe..fd04f97 100644
--- a/archive/slrnpull.conf
+++ b/archive/slrnpull.conf
@@ -4 +4 @@ gmane.comp.lang.ruby.unicorn.general 1000000000 1000000000 0
-# usage: slrnpull -d $PWD -h news.gmane.org --no-post
+# usage: slrnpull -d $PWD -h news.gmane.io --no-post
diff --git a/examples/big_app_gc.rb b/examples/big_app_gc.rb
index 9d05719..c1bae10 100644
--- a/examples/big_app_gc.rb
+++ b/examples/big_app_gc.rb
@@ -1 +1 @@
-# see {Unicorn::OobGC}[https://bogomips.org/unicorn/Unicorn/OobGC.html]
+# see {Unicorn::OobGC}[https://yhbt.net/unicorn/Unicorn/OobGC.html]
diff --git a/examples/logrotate.conf b/examples/logrotate.conf
index 77a01b5..c3aa40d 100644
--- a/examples/logrotate.conf
+++ b/examples/logrotate.conf
@@ -8 +8 @@
-#    https://bogomips.org/unicorn-public/?q=logrotate
+#    https://yhbt.net/unicorn-public/?q=logrotate
@@ -36 +36 @@
-		# Mail us at unicorn-public@bogomips.org
+		# Mail us at unicorn-public@yhbt.net
diff --git a/examples/nginx.conf b/examples/nginx.conf
index b6b69c1..c5026f9 100644
--- a/examples/nginx.conf
+++ b/examples/nginx.conf
@@ -116 +116 @@ http {
-    # https://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
+    # https://yhbt.net/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
diff --git a/examples/unicorn.conf.minimal.rb b/examples/unicorn.conf.minimal.rb
index 2d1bf0a..46fd634 100644
--- a/examples/unicorn.conf.minimal.rb
+++ b/examples/unicorn.conf.minimal.rb
@@ -4 +4 @@
-# See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
+# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
@@ -6 +6 @@
-# See also https://bogomips.org/unicorn/examples/unicorn.conf.rb for
+# See also https://yhbt.net/unicorn/examples/unicorn.conf.rb for
diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index d2897ef..d90bdc4 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -5 +5 @@
-# https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
+# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
@@ -8 +8 @@
-# See https://bogomips.org/unicorn/Unicorn/Configurator.html for complete
+# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 8ef23bc..dfe3a63 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -490 +490 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
-     * unicorn-public@bogomips.org (no HTML mail, no subscription necessary)
+     * unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index dd5dff4..d5991fe 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -99 +99 @@ def self.builder(ru, op)
-  # Raindrops::Middleware under Linux: https://bogomips.org/raindrops/
+  # Raindrops::Middleware under Linux: https://yhbt.net/raindrops/
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index e8b76f5..c3a4f2d 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -6,2 +6,2 @@
-# See https://bogomips.org/unicorn/examples/unicorn.conf.rb and
-# https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
+# See https://yhbt.net/unicorn/examples/unicorn.conf.rb and
+# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
@@ -10 +10 @@
-# https://bogomips.org/unicorn/examples/nginx.conf
+# https://yhbt.net/unicorn/examples/nginx.conf
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 5334fa0..a52931a 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -9 +9 @@
-# {source}[https://bogomips.org/unicorn.git/tree/lib/unicorn/http_server.rb]
+# {source}[https://yhbt.net/unicorn.git/tree/lib/unicorn/http_server.rb]
diff --git a/lib/unicorn/oob_gc.rb b/lib/unicorn/oob_gc.rb
index c4741a0..3b2f488 100644
--- a/lib/unicorn/oob_gc.rb
+++ b/lib/unicorn/oob_gc.rb
@@ -46,2 +46,2 @@
-# * https://bogomips.org/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
-# * https://bogomips.org/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
+# * https://yhbt.net/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
+# * https://yhbt.net/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
diff --git a/unicorn.gemspec b/unicorn.gemspec
index ceea831..a189f8d 100644
--- a/unicorn.gemspec
+++ b/unicorn.gemspec
@@ -18 +18 @@
-  s.email = %q{unicorn-public@bogomips.org}
+  s.email = %q{unicorn-public@yhbt.net}
@@ -25 +25 @@
-  s.homepage = 'https://bogomips.org/unicorn/'
+  s.homepage = 'https://yhbt.net/unicorn/'

^ permalink raw reply related	[relevance 1%]

* Re: Traffic priority with Unicorn
  2019-12-17  5:12  3% ` Eric Wong
@ 2019-12-18 22:06  0%   ` Bertrand Paquet
  0 siblings, 0 replies; 200+ results
From: Bertrand Paquet @ 2019-12-18 22:06 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

On Tue, 17 Dec 2019 at 06:12, Eric Wong <bofh@yhbt.net> wrote:
>
> Bertrand Paquet <bertrand.paquet@doctolib.com> wrote:
> > Hello,
> >
> > I would like to introduce some traffic priority in Unicorn. The goal
> > is to keep critical endpoints online even if the application is
> > slowing down a lot.
> >
> > The idea is to classify the request at nginx level (by vhost, http
> > path, header or whatever), and send the queries to two different
> > unicorn sockets (opened by the same unicorn instance): one for high
> > priority request, one for low priority request.
> > I need to do some small modifications [1] in the unicorn worker loop
> > to process high priority requests first. It seems to work:
> > - I launch a first apache bench toward the low priority port
> > - I launch a second apache bench toward the high priority port
> > - Unicorn handles the queries only for that one, and stop answering to
> > the low priority traffic
>
> > [1] https://github.com/bpaquet/unicorn/commit/58d6ba2805d4399f680f97eefff82c407e0ed30f#
>
> Easier to view locally w/o JS/CSS using "git show -W" for context:
>
> $ git remote add bpaquet https://github.com/bpaquet/unicorn
> $ git fetch bpaquet
> $ git show -W 58d6ba28
>   <snip>
> diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
> index 5334fa0c..976b728e 100644
> --- a/lib/unicorn/http_server.rb
> +++ b/lib/unicorn/http_server.rb
> @@ -676,53 +676,56 @@ def reopen_worker_logs(worker_nr)
>    # runs inside each forked worker, this sits around and waits
>    # for connections and doesn't die until the parent dies (or is
>    # given a INT, QUIT, or TERM signal)
>    def worker_loop(worker)
>      ppid = @master_pid
>      readers = init_worker_process(worker)
>      nr = 0 # this becomes negative if we need to reopen logs
>
>      # this only works immediately if the master sent us the signal
>      # (which is the normal case)
>      trap(:USR1) { nr = -65536 }
>
>      ready = readers.dup
> +    high_priority_reader = readers.first
> +    last_processed_is_high_priority = false
>      @after_worker_ready.call(self, worker)
>
>      begin
>        nr < 0 and reopen_worker_logs(worker.nr)
>        nr = 0
>        worker.tick = time_now.to_i
>        tmp = ready.dup
>        while sock = tmp.shift
>          # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
>          # but that will return false
>          if client = sock.kgio_tryaccept
> +          last_processed_is_high_priority = sock == high_priority_reader
>            process_client(client)
>            nr += 1
>            worker.tick = time_now.to_i
>          end
> -        break if nr < 0
> +        break if nr < 0 || last_processed_is_high_priority
>        end
>
>        # 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
> +      unless nr == 0 || !last_processed_is_high_priority
>          tmp = ready.dup
>          redo
>        end
>
>        ppid == Process.ppid or return
>
>        # timeout used so we can detect parent death:
>        worker.tick = time_now.to_i
>        ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
>      rescue => e
>        redo if nr < 0 && readers[0]
>        Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
>      end while readers[0]
>    end
>
>    # delivers a signal to a worker and fails gracefully if the worker
>    # is no longer running.
>
> > The tradeoff are
> > - No more "bet"[2] on low priority traffic. This is probably slowing
> > down a little bit the low  priority traffic.
>
> Yeah, but low priority is low priority, so it's fine to slow
> them down, right? :>
>
> > - This approach is only low / high. Not sure if I can extend it for 3
> > (or more) level of priority without a non negligible performance
> > impact (because of the "bet" above).
>
> I don't think it makes sense to have more than two levels of
> priority (zero, one, two, infinity rule?)
> <https://en.wikipedia.org/wiki/Zero_One_Infinity>
>
> > Do you think this approach is correct?
>
> readers order isn't guaranteed, especially when inheriting
> sockets from systemd or similar launchers.

Interesting point.

>
> I think some sort order could be defined via listen option...
>
> I'm not sure if inheriting multiple sockets from systemd or
> similar launchers using LISTEN_FDS env can guarantee ordering
> (or IO.select in Ruby, for that matter).
>
> It seems OK otherwise, I think...  Have you tested in real world?

Not yet. But I will probably test it soon.

>
> > Do you have any better idea to have some traffic prioritization?
> > (Another idea is to have dedicated workers for each priority class.
> > This approach has other downsides, I would like to avoid it).
> > Is it something we can  introduce in Unicorn (not as default
> > behaviour, but as a configuration option)?
>
> If you're willing to drop some low-priority requests, using a
> small listen :backlog value for a low-priority listen may work.
>
> I'm hesitant to put extra code in worker_loop method since
> it can slow down current users who don't need the feature.
>
> Instead, perhaps try replacing the worker_loop method entirely
> (similar to how oob_gc.rb wraps process_client) so users who
> don't enable the feature won't be penalized with extra code.
> Users who opt into the feature can get an entirely different
> method.

Ok I will try this approach. I'm a little bit annoyed by the code duplication.

>
> > Thx for any opinion.
>
> The best option would be to never get yourself in a situation
> where you're never overloaded by making everything fast :>
> Anything else seems pretty ugly...

On a system which handle 10k QPS, it's really difficult to never have
an issue somewhere :)

Thx

Bertrand

^ permalink raw reply	[relevance 0%]

* Re: Traffic priority with Unicorn
  @ 2019-12-17  5:12  3% ` Eric Wong
  2019-12-18 22:06  0%   ` Bertrand Paquet
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2019-12-17  5:12 UTC (permalink / raw)
  To: Bertrand Paquet; +Cc: unicorn-public

Bertrand Paquet <bertrand.paquet@doctolib.com> wrote:
> Hello,
> 
> I would like to introduce some traffic priority in Unicorn. The goal
> is to keep critical endpoints online even if the application is
> slowing down a lot.
> 
> The idea is to classify the request at nginx level (by vhost, http
> path, header or whatever), and send the queries to two different
> unicorn sockets (opened by the same unicorn instance): one for high
> priority request, one for low priority request.
> I need to do some small modifications [1] in the unicorn worker loop
> to process high priority requests first. It seems to work:
> - I launch a first apache bench toward the low priority port
> - I launch a second apache bench toward the high priority port
> - Unicorn handles the queries only for that one, and stop answering to
> the low priority traffic

> [1] https://github.com/bpaquet/unicorn/commit/58d6ba2805d4399f680f97eefff82c407e0ed30f#

Easier to view locally w/o JS/CSS using "git show -W" for context:

$ git remote add bpaquet https://github.com/bpaquet/unicorn
$ git fetch bpaquet
$ git show -W 58d6ba28
  <snip>
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 5334fa0c..976b728e 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -676,53 +676,56 @@ def reopen_worker_logs(worker_nr)
   # runs inside each forked worker, this sits around and waits
   # for connections and doesn't die until the parent dies (or is
   # given a INT, QUIT, or TERM signal)
   def worker_loop(worker)
     ppid = @master_pid
     readers = init_worker_process(worker)
     nr = 0 # this becomes negative if we need to reopen logs
 
     # this only works immediately if the master sent us the signal
     # (which is the normal case)
     trap(:USR1) { nr = -65536 }
 
     ready = readers.dup
+    high_priority_reader = readers.first
+    last_processed_is_high_priority = false
     @after_worker_ready.call(self, worker)
 
     begin
       nr < 0 and reopen_worker_logs(worker.nr)
       nr = 0
       worker.tick = time_now.to_i
       tmp = ready.dup
       while sock = tmp.shift
         # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
         # but that will return false
         if client = sock.kgio_tryaccept
+          last_processed_is_high_priority = sock == high_priority_reader
           process_client(client)
           nr += 1
           worker.tick = time_now.to_i
         end
-        break if nr < 0
+        break if nr < 0 || last_processed_is_high_priority
       end
 
       # 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
+      unless nr == 0 || !last_processed_is_high_priority
         tmp = ready.dup
         redo
       end
 
       ppid == Process.ppid or return
 
       # timeout used so we can detect parent death:
       worker.tick = time_now.to_i
       ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
     rescue => e
       redo if nr < 0 && readers[0]
       Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
     end while readers[0]
   end
 
   # delivers a signal to a worker and fails gracefully if the worker
   # is no longer running.

> The tradeoff are
> - No more "bet"[2] on low priority traffic. This is probably slowing
> down a little bit the low  priority traffic.

Yeah, but low priority is low priority, so it's fine to slow
them down, right? :>

> - This approach is only low / high. Not sure if I can extend it for 3
> (or more) level of priority without a non negligible performance
> impact (because of the "bet" above).

I don't think it makes sense to have more than two levels of
priority (zero, one, two, infinity rule?)
<https://en.wikipedia.org/wiki/Zero_One_Infinity>

> Do you think this approach is correct?

readers order isn't guaranteed, especially when inheriting
sockets from systemd or similar launchers.

I think some sort order could be defined via listen option...

I'm not sure if inheriting multiple sockets from systemd or
similar launchers using LISTEN_FDS env can guarantee ordering
(or IO.select in Ruby, for that matter).

It seems OK otherwise, I think...  Have you tested in real world?

> Do you have any better idea to have some traffic prioritization?
> (Another idea is to have dedicated workers for each priority class.
> This approach has other downsides, I would like to avoid it).
> Is it something we can  introduce in Unicorn (not as default
> behaviour, but as a configuration option)?

If you're willing to drop some low-priority requests, using a
small listen :backlog value for a low-priority listen may work.

I'm hesitant to put extra code in worker_loop method since
it can slow down current users who don't need the feature.

Instead, perhaps try replacing the worker_loop method entirely
(similar to how oob_gc.rb wraps process_client) so users who
don't enable the feature won't be penalized with extra code.
Users who opt into the feature can get an entirely different
method.

> Thx for any opinion.

The best option would be to never get yourself in a situation
where you're never overloaded by making everything fast :>
Anything else seems pretty ugly...

^ permalink raw reply related	[relevance 3%]

* Re: tmpio.rb and taint mode
  @ 2019-12-11 23:16  2% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2019-12-11 23:16 UTC (permalink / raw)
  To: Terry Scheingeld; +Cc: unicorn-public

Terry Scheingeld <tscheingeld32@gmail.com> wrote:
> tmpio.rb causes an "insecure operation" error when being run in taint
> mode. This is due not to a problem in tmpio.rb but in Ruby's File
> class. Here are the details on the problem and a simple workaround for
> it.
> 
> I filed this bug report in February 2018:
> https://bugs.ruby-lang.org/issues/14485. The problem is that when a
> File object is created using an untainted string for the path, File
> nevertheless changes that path to tainted. It is agreed thatit's a
> bug: File should not taint an untainted path. However, efforts to fix
> the bug seem to have stalled out.
> 
> Now, in tmpio.rb, a random, untainted path is generated and stored in
> the Unicorn::TmpIO object. Then, a few lines later, the class attempts
> to unlink that file using the path stored in the object. Because of
> the bug in File, the path is now tainted, resulting in an insecure
> operation error.
> 
> I propose a simple workaround. Store the path in its own variable.
> Pass the variable to the Unicorn::TmpIO object, but use the original
> variable to unlink the file. This technique worked in experimentation
> for me. Here's a modified version of tmpio.rb.

Thanks for the analysis, explanation and fix.  I've made your
change into the patch + commit message below.

I had no idea unicorn or rack could work at all with tainting
enabled...  Was there anything else that was broken with
taint checks?

But AFAIK tainting is due to be removed in Ruby, soonish (I've
never used it in either Ruby or Perl5).

Anyways, I'll merge this into master soonish and hopefully get
some doc updates + release in a week or so...  Thanks again.

---------------8<-----------------
From: Terry Scheingeld <tscheingeld32@gmail.com>
Date: Wed, 11 Dec 2019 11:24:59 -0500
Subject: [PATCH] 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.

Link: https://bogomips.org/unicorn-public/CABg1sXrvGv9G6CDQxePDUqTe6N-5UpLXm7eG3YQO=dda-Cgg7A@mail.gmail.com/
---
 lib/unicorn/tmpio.rb | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/lib/unicorn/tmpio.rb b/lib/unicorn/tmpio.rb
index db88ed33..0bbf6ec5 100644
--- a/lib/unicorn/tmpio.rb
+++ b/lib/unicorn/tmpio.rb
@@ -11,12 +11,18 @@ class Unicorn::TmpIO < File
   # immediately, switched to binary mode, and userspace output
   # buffering is disabled
   def self.new
+    path = nil
+
+    # workaround File#path being tainted:
+    # https://bugs.ruby-lang.org/issues/14485
     fp = begin
-      super("#{Dir::tmpdir}/#{rand}", RDWR|CREAT|EXCL, 0600)
+      path = "#{Dir::tmpdir}/#{rand}"
+      super(path, RDWR|CREAT|EXCL, 0600)
     rescue Errno::EEXIST
       retry
     end
-    unlink(fp.path)
+
+    unlink(path)
     fp.binmode
     fp.sync = true
     fp


^ permalink raw reply related	[relevance 2%]

* yet-another-horribly-named-server as an nginx alternative
@ 2019-05-26  5:24  1% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2019-05-26  5:24 UTC (permalink / raw)
  To: unicorn-public

First off, it should come as no surprise to anybody nowadays
that I hate marketing :P  I also hate that anybody using unicorn
is also stuck with nginx as the only (well-known) proxy which
fully buffers both requests AND responses.

One of the major reasons I like *nix-like is interchangeable
parts, and the lack of proxies which can do what nginx does
always bothered me.  nginx has also continuously gotten more
enterprisey over the years, maybe a decidedly non-enterprisey
alternative is in order :>

Keep in mind: I am nothing more than an Internet loon with
no way to substantiate any claims I make!

Hypothetically, I've been using this server in production since
2013, and doing HTTPS termination since 2016.  It's handled
numerous hug-of-death events over the years sitting in front of
Varnish, unicorn, mod_perl stuff, PSGI stuff and also serving
static files on a cheap VPS.

Even if by some coincidence/luck unicorn somehow works well for
you, this alternative is the complete opposite in terms of
design.  I have no real production experience with epoll,
kqueue, threads or non-blocking I/O; all of which are (ab)used
by this alternative.

The yin to unicorn's yang, if you will.

Again, keep in mind that I'm an Internet loon.  I feel bad for
you if some "cool Internet companies" misled you into believing
unicorn is competently engineered.  This alternative is likely
worse given the effects of pollution and head trauma I've
suffered over the years.

I'm not going to mention this "nginx alternative" by name, here;
but it's been announced on the ruby-talk mailing list at least
(and it's mostly Ruby with some C, not that I know C).

So if anybody wants to update the unicorn docs to give this
alternative proxy equal mention with nginx, they're welcome to
send such as patch.  Please no superlatives, hype, or "marketing
speak", though.

I can't make such an update to unicorn docs myself, since I
would be abusing my position as the maintainer of this project
to market yet-another-horribly-named-server.

Thanks for understanding.


One notable difference from nginx ("nqinagntr bire atvak" vs V
jrer tbbq ng znexrgvat :P):

  Output buffering is lazy, similar to how Unicorn::TeeInput works
  (but for output, not input).  It matters for large responses,
  whereas nginx "proxy_buffering" is an on/off switch, this alternative
  only buffers response bodies when the slow client can't keep up
  with a backend (unicorn).

^ permalink raw reply	[relevance 1%]

* Re: Support default_middleware configurator method
  @ 2018-09-19  7:39  2%         ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2018-09-19  7:39 UTC (permalink / raw)
  To: Jeremy Evans; +Cc: unicorn-public

Jeremy Evans <code@jeremyevans.net> wrote:
> OK. To implement that, I modified the bin/unicorn file so -N
> is only respected while parsing ARGV, and not while parsing
> embedded configuration file options.

Thanks.

Unfortunately, -N on the command-line was broken by your patch.
I fixed configurator.rb ordering (below) to pass t0300
integration test.

Also, using a non-config.ru .rb file (TestHandler in
test/unit/test_server.rb) was broken because of missing
parentheses.

Will squash the following changes in before pushing:

diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index 9c36dfe..d426edf 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -88,6 +88,9 @@ def reload(merge_defaults = true) #:nodoc:
     RACKUP[:set_listener] and
       set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
 
+    RACKUP[:no_default_middleware] and
+      set[:default_middleware] = false
+
     # unicorn_rails creates dirs here after working_directory is bound
     after_reload.call if after_reload
 
@@ -714,9 +717,6 @@ def parse_rackup_file # :nodoc:
 
     /^#\\(.*)/ =~ File.read(ru) or return
     RACKUP[:optparse].parse!($1.split(/\s+/))
-    if RACKUP[:no_default_middleware]
-      set[:default_middleware] = false
-    end
 
     if RACKUP[:daemonize]
       # unicorn_rails wants a default pid path, (not plain 'unicorn')
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 7531886..62f6171 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -786,7 +786,7 @@ def listener_names(listeners = LISTENERS)
   end
 
   def build_app!
-    if app.respond_to?(:arity) && app.arity == 0 || app.arity == 2
+    if app.respond_to?(:arity) && (app.arity == 0 || app.arity == 2)
       if defined?(Gem) && Gem.respond_to?(:refresh)
         logger.info "Refreshing Gem list"
         Gem.refresh

But there's still no tests for the config file option...
I assume you tested that part locally?
Thanks again.

^ permalink raw reply related	[relevance 2%]

* Re: [PATCH] Send SIGTERM before SIGKILL on timeout
  2018-02-24 15:15  2% ` Eric Wong
@ 2018-02-27  4:03  0%   ` Fumiaki Matsushima
  0 siblings, 0 replies; 200+ results
From: Fumiaki Matsushima @ 2018-02-27  4:03 UTC (permalink / raw)
  To: unicorn-public; +Cc: Eric Wong, Fumiaki Matsushima

Thank you for your reply!

> Right, it's been rejected several times before:

Sorry, I missed these emails.

> I don't want to encourage lazy app development or Rack apps
written with only unicorn in mind

That's true.
I'll use rack-timeout to solve my timeout problem.

Thanks.

On Sun, Feb 25, 2018 at 12:15 AM, Eric Wong <e@80x24.org> wrote:
> Fumiaki MATSUSHIMA <mtsm.fm@gmail.com> wrote:
>> To output log / send error to error tracking service,
>> we need to receive a signal other than SIGKILL first.
>> ---
>> Hi Unicorn team,
>>
>> I'm not sure this change is accetable though,
>> I can find some articles and patches to prevent SIGKILL
>> on timeout.
>
> Right, it's been rejected several times before:
>
>  https://bogomips.org/unicorn-public/20140416084416.GA9709@dcvr.yhbt.net/t/#u
>  https://bogomips.org/unicorn-public/20180115015740.GA850@dcvr/t/#u
>
>> I think it's great if this feature is supported by unicorn itself.
>>
>> Could you give me your opinion?
>
> Again, I don't want to encourage lazy app development or Rack apps
> written with only unicorn in mind.
>
> Existing `timeout' feature of unicorn is already something I hate
> and don't want to encourage further reliance on:
>
>         https://bogomips.org/unicorn/Application_Timeouts.html
>
> Fwiw, I (with ruby-core) will try to make Timeout in ruby stdlib
> have less overhead for 2.6 (or 2.7) so it can benefit more users
> than just unicorn.  Ditto with various OobGC hacks over the years.
>
> Thanks.

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] Send SIGTERM before SIGKILL on timeout
  @ 2018-02-24 15:15  2% ` Eric Wong
  2018-02-27  4:03  0%   ` Fumiaki Matsushima
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2018-02-24 15:15 UTC (permalink / raw)
  To: Fumiaki MATSUSHIMA; +Cc: unicorn-public, mtsmfm

Fumiaki MATSUSHIMA <mtsm.fm@gmail.com> wrote:
> To output log / send error to error tracking service,
> we need to receive a signal other than SIGKILL first.
> ---
> Hi Unicorn team,
> 
> I'm not sure this change is accetable though,
> I can find some articles and patches to prevent SIGKILL
> on timeout.

Right, it's been rejected several times before:

 https://bogomips.org/unicorn-public/20140416084416.GA9709@dcvr.yhbt.net/t/#u
 https://bogomips.org/unicorn-public/20180115015740.GA850@dcvr/t/#u

> I think it's great if this feature is supported by unicorn itself.
> 
> Could you give me your opinion?

Again, I don't want to encourage lazy app development or Rack apps
written with only unicorn in mind.

Existing `timeout' feature of unicorn is already something I hate
and don't want to encourage further reliance on:

	https://bogomips.org/unicorn/Application_Timeouts.html

Fwiw, I (with ruby-core) will try to make Timeout in ruby stdlib
have less overhead for 2.6 (or 2.7) so it can benefit more users
than just unicorn.  Ditto with various OobGC hacks over the years.

Thanks.

^ permalink raw reply	[relevance 2%]

* Re: Auto scaling workers with unicorn
  @ 2017-12-05  1:51  2% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2017-12-05  1:51 UTC (permalink / raw)
  To: Sam Saffron; +Cc: unicorn-public

Sam Saffron <sam.saffron@gmail.com> wrote:
> I would like to amend Discourse so we "automatically" absorb certain
> traffic spikes. As it stands we can only configure unicorn with
> num_workers and use TTIN and TTOUT to tune the number on the fly.
> 
> I was wondering if you would be open to patching unicorn to allow it
> to perform auto-tuning based on raindrops info.

I'm no fan of this or auto-tuning systems in general.
More explanation below.

> How it could work
> 
> 1. configure unicorn with min_workers, max_workers, wince_delay, scale_up_delay
> 
> 2. If queued requests is over 0 for N samples over scale_up_delay, add
> a worker up until max_workers
> 
> 3. If queued requests is 0 for N samples over wince_delay scale down
> until you reach min workers

This adds more complexity to configuration: increasing the
likelyhood of getting these numbers completely wrong.  GC and
malloc tuning is tricky and error-prone enough, already.

Mainly, this tends to hide problems for later; instead of
forcing you to deal with your resource limitations up front.

It becomes more difficult to forsee resource limitations down
the line.  Before I worked on unicorn, I've seen auto-scaling
Apache workers mistuned far too often and running out of DB
connections or memory; and that happens at the worst time:
when your site is under heavy load (when you have the most to
lose (or gain)).

> Having this system in place can heavily optimize memory in large
> deployments and simplifies provisioning logic quite a lot.

My philosophy remains to tune for the worst case possible.

If you really need to do something like run an expensive
off-peak cronjob, maybe have it TTIN at the beginning and TTOU
again at the end.

Fwiw, the most useful thing I've found TTIN/TTOU for is cutting
down to one worker so I know which one to strace when tracking
down a problem; not auto-scaling.

> Wondering what you think about this and if you think unicorn should
> provide this option?

Fwiw, my position has been consistent on this throughout the years.

Also, digging through the archives, Ben Somers came up with
alicorn a while back and it might be up your alley:

https://bogomips.org/unicorn-public/CAO1NZApo0TLJY2KgSg+Fjt1jEcuPfq=UCC0SCvvnuGDnr39w8w@mail.gmail.com/

^ permalink raw reply	[relevance 2%]

* Re: Bug, probably related to Unicoen
  2017-09-14  8:25  2% Bug, probably related to Unicoen Felix Yasnopolski
@ 2017-09-14  9:15  2% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2017-09-14  9:15 UTC (permalink / raw)
  To: Felix Yasnopolski; +Cc: unicorn-public

Felix Yasnopolski <felix.yasnopolski@gmail.com> wrote:
> We use Ruby v2.2.3, Rails v4.2.4, Unicorn v5.1.0 in production.
> 
> ActiveRecord validation rules and callbacks for all models of app has
> been disabled after unicorn restart (from log rotation script). Here
> it is

Wow, that's a new one... I haven't used Rails in years, but it's
certainly not intentional behavior on the part of unicorn.

Hopefully somebody else with Rails knowledge can chime in.

Some questions:

Do you have anything which overrides USR1?

Are the logs getting rotated correctly?

(more below ...)

> /var/www/fitness_crm/shared/log/production.log
> /var/www/fitness_crm/shared/log/unicorn.stderr.log
> /var/www/fitness_crm/shared/log/unicorn.stdout.log {
>         daily
>         missingok
>         rotate 30
>         compress
>         notifempty
>         create 640 deploy deploy
>         sharedscripts
>         postrotate
>                 kill -s USR2 `cat
> /var/www/fitness_crm/shared/tmp/pids/unicorn.pid`
>         endscript
> }
> 
> We've changed the script to send USR1 instead of USR2, but nothing has
> changed. When USR1 is sent validations/callbacks simply keep being
> disabled. Here is our unicorn configuration file
> 
> working_directory "/var/www/fitness_crm/current"
> pid "/var/www/fitness_crm/shared/tmp/pids/unicorn.pid"
> stdout_path "/var/www/fitness_crm/shared/log/unicorn.stdout.log"
> stderr_path "/var/www/fitness_crm/shared/log/unicorn.stderr.log"

OK, that looks right.

Do your logs show the log rotations as they're happening, at least?

Anything else pop up in the logs which might help us figure out
what's wrong?  (for example, do you have dying workers)

> listen "/tmp/unicorn.fitness_crm_production.sock"
> 
> worker_processes 8
> timeout 30
> 
> preload_app true

Can you try omitting preload_app and see if you can reproduce
the problem?  The big reason preload_app is `false' by default
is it could break some code...

And probably use only 1 worker to avoid memory
overhead/excessive log spew during debugging.

> before_exec do |server|
>   ENV["BUNDLE_GEMFILE"] = "/var/www/fitness_crm/current/Gemfile"
> end
> 
> before_fork do |server, worker|
>   # Disconnect since the database connection will not carry over
>   if defined? ActiveRecord::Base
>     ActiveRecord::Base.connection.disconnect!
>   end
> 
>   # Quit the old unicorn process
>   old_pid = "#{server.config[:pid]}.oldbin"
>   if File.exists?(old_pid) && server.pid != old_pid
>     begin
>       Process.kill("QUIT", File.read(old_pid).to_i)
>     rescue Errno::ENOENT, Errno::ESRCH
>       # someone else did our job for us
>     end
>   end
> 
>   if defined?(Resque)
>     Resque.redis.quit
>   end
> 
>   sleep 1
> end
> 
> after_fork do |server, worker|
>   # Start up the database connection again in the worker
>   if defined?(ActiveRecord::Base)
>     ActiveRecord::Base.establish_connection
>   end
> 
>   if defined?(Resque)
>     Resque.redis = 'localhost:6379'
>   end
> end

If log rotations (via USR1) fails (e.g. due to permission
error), the workers die and get replaced, instead.  This allows
the log files to be released and rotated in "brute force"
fashion...

Can you reproduce the problem by sending a QUIT, TERM, or KILL
signal directly to a worker process?  (Also, note the time
interval between initial master startup and your test)

A later-spawned worker could behave differently/strangely
from an originally-spawned one...

Finally, can you reproduce your original problem by sending USR1
to the master immediately after a fresh start?

With logrotate (which only runs intermittently), the problem
could be manifesting from an app timeout or time skew of some
sort interacting poorly with preload_app and respawned workers.

> After this we kill unicorn and start it manually with command:
> 
> bundle exec unicorn -D -c
> /var/www/fitness_crm/shared/config/unicorn.rb -E production
> 
> After this everything is good and validations and callbacks are
> enabled again. Please, help to find out what is the reason of such a
> behavior and how to fix it.

This is why I gave the time skew/timeout hypothesis above.

Otherwise; I'm out of ideas, for now (and falling asleep).
Hoping somebody more awake with modern Rails knowledge can chime
in and help with things I'm missing.


Again; one of the first unicorn-specific steps for
troubleshooting is to disable preload_app and use a single
worker process which you can trace more easily.

^ permalink raw reply	[relevance 2%]

* Bug, probably related to Unicoen
@ 2017-09-14  8:25  2% Felix Yasnopolski
  2017-09-14  9:15  2% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Felix Yasnopolski @ 2017-09-14  8:25 UTC (permalink / raw)
  To: unicorn-public

We use Ruby v2.2.3, Rails v4.2.4, Unicorn v5.1.0 in production.

ActiveRecord validation rules and callbacks for all models of app has
been disabled after unicorn restart (from log rotation script). Here
it is

/var/www/fitness_crm/shared/log/production.log
/var/www/fitness_crm/shared/log/unicorn.stderr.log
/var/www/fitness_crm/shared/log/unicorn.stdout.log {
        daily
        missingok
        rotate 30
        compress
        notifempty
        create 640 deploy deploy
        sharedscripts
        postrotate
                kill -s USR2 `cat
/var/www/fitness_crm/shared/tmp/pids/unicorn.pid`
        endscript
}

We've changed the script to send USR1 instead of USR2, but nothing has
changed. When USR1 is sent validations/callbacks simply keep being
disabled. Here is our unicorn configuration file

working_directory "/var/www/fitness_crm/current"
pid "/var/www/fitness_crm/shared/tmp/pids/unicorn.pid"
stdout_path "/var/www/fitness_crm/shared/log/unicorn.stdout.log"
stderr_path "/var/www/fitness_crm/shared/log/unicorn.stderr.log"

listen "/tmp/unicorn.fitness_crm_production.sock"

worker_processes 8
timeout 30

preload_app true

before_exec do |server|
  ENV["BUNDLE_GEMFILE"] = "/var/www/fitness_crm/current/Gemfile"
end

before_fork do |server, worker|
  # Disconnect since the database connection will not carry over
  if defined? ActiveRecord::Base
    ActiveRecord::Base.connection.disconnect!
  end

  # Quit the old unicorn process
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end

  if defined?(Resque)
    Resque.redis.quit
  end

  sleep 1
end

after_fork do |server, worker|
  # Start up the database connection again in the worker
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end

  if defined?(Resque)
    Resque.redis = 'localhost:6379'
  end
end

After this we kill unicorn and start it manually with command:

bundle exec unicorn -D -c
/var/www/fitness_crm/shared/config/unicorn.rb -E production

After this everything is good and validations and callbacks are
enabled again. Please, help to find out what is the reason of such a
behavior and how to fix it.

^ permalink raw reply	[relevance 2%]

* Re: Master Process Reaping Worker with No message
@ 2017-05-23 19:29  2% Aakash Gupta
  0 siblings, 0 replies; 200+ results
From: Aakash Gupta @ 2017-05-23 19:29 UTC (permalink / raw)
  To: unicorn-public@bogomips.org; +Cc: unicorn-public@bogomips.org

Thank a lot Eric. 
I am using Ruby 2.3.3 and Unicorn 5.3. 

"It sounds like somebody else is sending SIGKILL to a worker..." This line of yours saved my server. 

Actually when I uploads a file, it first goes to /tmp directory and then one of the worker processes of unicorn uploads it to S3 from there. So, earlier when I was trying to fix timeout connection error of unicorn, I increased it to 20 minutes and the problem was solved temporarily. 

But the problem was that the files that were uploaded temporarily to /tmp directory, those files do not get deleted from /tmp after they are uploaded to S3. So, I wrote a script using tmpreaper that cleans the /tmp directory recursively and removes the files that are 30 min older and added that to cron service. 

After this, as a result of this script, all the files that were required to be removed, were removed from /tmp directory but again a problem occurred. When I used "df -h" command to check whether the files are actually deleted or not, it showed that the space that was taken by uploaded files on my disk has not been freed :( . So, I searched internet and got to know that any other process was still using those files, that was why the space on the disk was not freed. 

I used ' lsof +L1 | grep "deleted" ' command and found that a process named 'bundle' is still using these deleted files. So, I again wrote a script that was killing the process that was using a deleted file.  Script was using this command

" lsof +L1 | grep 'deleted' | awk '{print $2}' | xargs kill -9 "

Today, after reading your words, I finally realised that my script was killing the unicorn worker itself because the command that I was using was killing the parent of "bundle" process. As "bundle" was a child of Unicorn worker, unicorn worker was being killed.

So, thanks a lot Eric :) 

I was finally able to solve this error because of the direction and hint that you gave me. But the only thing that I need to find is that what is causing uploaded files to persist in /tmp directory.   


^ permalink raw reply	[relevance 2%]

* Re: after_worker_exit on murder
  2017-04-05 10:55  2%   ` Simon Eskildsen
@ 2017-04-05 18:33  2%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2017-04-05 18:33 UTC (permalink / raw)
  To: Simon Eskildsen; +Cc: unicorn-public, Jeremy Evans

Simon Eskildsen <simon.eskildsen@shopify.com> wrote:

Thank you for your reply.  It is a good reminder of how far away
I am from the rest of the web development world :>

<snip>

> It becomes difficult, because sometimes you have legitimate requests
> that take 10-20s, because the merchant's data set is so large that it
> exposes anomalies. Again, with the size of our code-base, we need this
> wiggle room in the global timeout to not just error on users. You can
> have endpoints that do 4 HTTP requests, 5 RPC requests, 4 MySQL
> queries, and 30 calls to Memcached. In that case, your worst case is
> the timeout of all of those actions, which easily exceeds the Unicorn
> timeout.

Wow, that is frightening... People will actually wait for a web
page to load in that case?  I guess that's why ccc exists :>

> We've debated having "budgets" and "shitlisting"
> (http://sirupsen.com/shitlists/) paths that obviously take longer than
> the budget for a single resource. The probability of more than one
> resource being very slow at once, is quite low (and if it is, again,
> we rely on the Unicorn timeout).

Interesting.  I guess for now, you can use nginx or similar to
route to differently-configured unicorns with different timeouts
(or even other servers)?

Anyways, I'm coming around to reconciling the two mindsets of
"typical" code running on unicorn ("it's alright to crash")
and the "no room for error: nuclear war starts if you screw up"
mindset I adopt for other projects.

> Some of these bugs are even deep in Ruby, Jean B, one of my co-workers
> submitted a bug about there being no write_timeout in Net::HTTP (you
> even replied!): https://bugs.ruby-lang.org/issues/13396

Yeah, that got me thinking of improving core timeouts again...

One big problem is the lack of a portable standard asynchronous
name resolution mechanism in the C standard library.  I'm not
sure how well resolv.rb/resolv-replace.rb holds up in real-world
usage, nor if pulling in something like ares2 would be an
acceptable dependency for ruby-core...

> BTW we deployed 5.3.0 and replaced our `before_murder` hook with
> `after_worker_exit`. Everything works perfectly and we finally are not
> using a forked version of Unicorn anymore. Thanks for the release!

Cool, good to know.

^ permalink raw reply	[relevance 2%]

* Re: after_worker_exit on murder
  @ 2017-04-05 10:55  2%   ` Simon Eskildsen
  2017-04-05 18:33  2%     ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Simon Eskildsen @ 2017-04-05 10:55 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public, Jeremy Evans

I agree with you in principle, absolutely. However, when you have a
code-base the size of ours (100Ks of lines of Ruby) with 100s of
developers, and new ones coming on every month with no prior Ruby or
Rails experience, we can't rely on everyone doing the right thing all
the time. With a surface area of that size, there will be things that
are missed, especially when you run gems like Liquid that have
billions of different ways of composing templates—some of these paths,
unfortunately, are going to be slow. We definitely chase down all the
worst offenders, but when new ones creep up, we do our best to chase
them down when time allows. Using this hook, allows us to monitor when
that happens, and how often it happens, and for which endpoints. With
100Ks of lines of code, 100s of developers, and 10s of thousands of
requests per second—once in a million happens every couple of seconds.
Multiply that with the size of the code-base, and Unicorn timeouts due
to the conditions below will happen somewhat often.

It becomes difficult, because sometimes you have legitimate requests
that take 10-20s, because the merchant's data set is so large that it
exposes anomalies. Again, with the size of our code-base, we need this
wiggle room in the global timeout to not just error on users. You can
have endpoints that do 4 HTTP requests, 5 RPC requests, 4 MySQL
queries, and 30 calls to Memcached. In that case, your worst case is
the timeout of all of those actions, which easily exceeds the Unicorn
timeout. We've debated having "budgets" and "shitlisting"
(http://sirupsen.com/shitlists/) paths that obviously take longer than
the budget for a single resource. The probability of more than one
resource being very slow at once, is quite low (and if it is, again,
we rely on the Unicorn timeout).

In other words, the Unicorn timeout is not a crutch for timeouts in
the application, but a global timeout to as a last line of defense
against many timeouts, or some bug we didn't foresee. This seems
unavoidable in my eyes, unless you have very aggressive timeouts and
meticulously keep track of the budgets in a testing environment and
raise if the budget is exceeded. When we hit many timeouts, we use
Semian (http://github.com/shopify/semian) to trigger circuit
breakers—so the reliance on Unicorn should be brief.

Some of these bugs are even deep in Ruby, Jean B, one of my co-workers
submitted a bug about there being no write_timeout in Net::HTTP (you
even replied!): https://bugs.ruby-lang.org/issues/13396

BTW we deployed 5.3.0 and replaced our `before_murder` hook with
`after_worker_exit`. Everything works perfectly and we finally are not
using a forked version of Unicorn anymore. Thanks for the release!

^ permalink raw reply	[relevance 2%]

* [PATCH] doc: remove private email support address
@ 2017-03-20 20:32  4% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2017-03-20 20:32 UTC (permalink / raw)
  To: unicorn-public

Email was never private, and won't further burden myself or
any future maintainers with trying to maintain someone elses'
privacy.

Offering private support is also unfair to readers on public
lists who may get a watered down or improperly translated
summary (if at all).

Instead, encourage the use of anonymity tools and scrubbing of
sensitive information when the sender deems necessary.
---
 No, I don't do GPG, as I have no real identity :P

 .olddoc.yml |  1 -
 ISSUES      | 11 ++++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/.olddoc.yml b/.olddoc.yml
index ee2d306..cacc0ab 100644
--- a/.olddoc.yml
+++ b/.olddoc.yml
@@ -12,7 +12,6 @@ noindex:
 - TODO
 - unicorn_rails_1
 public_email: unicorn-public@bogomips.org
-private_email: unicorn@bogomips.org
 nntp_url:
   - nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
   - nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
diff --git a/ISSUES b/ISSUES
index 291441a..e06f9cd 100644
--- a/ISSUES
+++ b/ISSUES
@@ -9,14 +9,16 @@ submit patches and/or obtain support after you have searched the
 * Cc: all participants in a thread or commit, as subscription is optional
 * Do not {top post}[http://catb.org/jargon/html/T/top-post.html] in replies
 * Quote as little as possible of the message you're replying to
-* Do not send HTML mail or images, it will be flagged as spam
-* Anonymous and pseudonymous messages will always be welcome.
+* Do not send HTML mail or images,
+  they hurt reader privacy and will be flagged as spam
+* Anonymous and pseudonymous messages will ALWAYS be welcome
 * The email submission port (587) is enabled on the bogomips.org MX:
   https://bogomips.org/unicorn-public/20141004232241.GA23908@dcvr.yhbt.net/t/
 
 If your issue is of a sensitive nature or you're just shy in public,
-then feel free to email us privately at mailto:unicorn@bogomips.org
-instead and your issue will be handled discreetly.
+use anonymity tools such as Tor or Mixmaster; and rely on the public
+mail archives for responses.  Be sure to scrub sensitive log messages
+and such.
 
 If you don't get a response within a few days, we may have forgotten
 about it so feel free to ask again.
@@ -64,7 +66,6 @@ document distributed with git) on guidelines for patch submission.
 == Contact Info
 
 * public: mailto:unicorn-public@bogomips.org
-* private: mailto:unicorn@bogomips.org
 * nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
 * nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn
 * https://bogomips.org/unicorn-public/
-- 
EW


^ permalink raw reply related	[relevance 4%]

* Re: [PATCH] Add worker_exec configuration option V2
  @ 2017-03-11  7:18  2%         ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2017-03-11  7:18 UTC (permalink / raw)
  To: Jeremy Evans; +Cc: unicorn-public

Jeremy Evans <code@jeremyevans.net> wrote:
> On 03/10 09:19, Eric Wong wrote:
> > tests, later, or you can.  I also had some FreeBSD test fixes
> > (which might apply to OpenBSD) on a VM somewhere which I'll Cc:
> > you on: there was also just SO_KEEPALIVE fix I posted:
> > 
> >   https://bogomips.org/unicorn-public/20170310203431.28067-1-e@80x24.org/raw
> 
> The C test code also returns 8 on OpenBSD, FWIW.  I'm happy to test any
> test fixes on OpenBSD, just let me know.

Thanks. I'll be happy to help fix any OpenBSD failures you see
from "gmake check"  I don't think the accept_filter fixes apply
to OpenBSD, but I guess the expr(1) fix did.

Thank you.

Same goes for NetBSD, DragonflyBSD or any other completely
Free/Open Source OSes anybody else here uses.

> > Jeremy Evans <code@jeremyevans.net> wrote:
> > > -      if pid = fork
> > > -        @workers[pid] = worker
> > > -        worker.atfork_parent
> > > +
> > > +      pid = if @worker_exec
> > > +        worker_spawn(worker)
> > >        else
> > > -        after_fork_internal
> > > -        worker_loop(worker)
> > > -        exit
> > > +        fork do
> > > +          after_fork_internal
> > > +          worker_loop(worker)
> > > +          exit
> > > +        end
> > 
> > I prefer to avoid the block with fork.  The block deepens the
> > stack for the running app, so it can affect GC efficiency.
> > 
> > Can be fixed in a separate patch...
> 
> That makes sense.  If you would like me to send a separate patch to fix
> it, I can do that.

Yes, please.

Not sure if we should automate the stack depth test...
I resisted it in the past since it might be too fragile
w.r.t changes to Ruby (even using "unicorn" via the
RubyGems-generated wrapper deepens it by 2).

Thanks again.

^ permalink raw reply	[relevance 2%]

* Re: [PATCH] Add worker_exec configuration option
  @ 2017-03-08 20:02  2% ` Eric Wong
    0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2017-03-08 20:02 UTC (permalink / raw)
  To: Jeremy Evans; +Cc: unicorn-public

Jeremy Evans <code@jeremyevans.net> wrote:
> The worker_exec configuration option makes all worker processes
> exec after forking.  This initializes the worker processes with
> separate memory layouts, defeating address space discovery
> attacks on operating systems supporting address space layout
> randomization, such as Linux, MacOS X, NetBSD, OpenBSD, and
> Solaris.
> 
> Support for execing workers is very similar to support for reexecing
> the master process.  The main difference is the worker's to_i and
> master pipes also need to be inherited after worker exec just as the
> listening sockets need to be inherited after reexec.

Thanks, this seems like an acceptable feature.
Some comments below....

> +++ b/lib/unicorn/http_server.rb
> @@ -105,6 +105,14 @@ def initialize(app, options = {})
>      # list of signals we care about and trap in master.
>      @queue_sigs = [
>        :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
> +
> +    if worker_nr = ENV['UNICORN_WORKER_NR']
> +      @worker_nr = worker_nr.to_i
> +      @master_pid = Process.ppid
> +    else
> +      @master_pid = $$
> +    end
> +
>    end

We should nil @worker_nr in the normal case to avoid introducing
new warnings on uninitialized ivars.

>    # Runs the thing.  Returns self so you can run join on it
> @@ -113,7 +121,6 @@ def start
>      # this pipe is used to wake us up from select(2) in #join when signals
>      # are trapped.  See trap_deferred.
>      @self_pipe.replace(Unicorn.pipe)
> -    @master_pid = $$

Any particular reason setting @master_pid was moved to initialize?

Maybe we (or anybody else) has tests or other code which do:

	  foo = Unicorn::HttpServer.new ...
	  pid = fork do
	    foo.start.join
	  end

Which would be broken by the movement from start => initialize

Again, I don't consider this a stable API and don't want people
depending on our internals; but I will also avoid breaking
existing behavior whenever possible.

> @@ -459,6 +466,39 @@ def reexec
>      proc_name 'master (old)'
>    end
>  
> +  def worker_exec(worker)
> +    listener_fds = {}
> +    LISTENERS.each do |sock|
> +      sock.close_on_exec = false
> +      listener_fds[sock.fileno] = sock
> +    end
> +    ENV['UNICORN_FD'] = listener_fds.keys.join(',')
> +    ENV['UNICORN_WORKER_NR'] = worker.nr.to_s

> +    ENV['UNICORN_WORKER_TO_IO'] = worker.to_io.fileno.to_s
> +    ENV['UNICORN_WORKER_MASTER'] = worker.master.fileno.to_s

We can cram these two FDs into one UNICORN_WORKER_FD
comma-delimited variable like UNICORN_FD.  The underlying
getenv/setenv/putenv functions are all O(n), so keeping env
smaller would be nice.

And we can even avoid modifying ENV on Ruby 1.9+ by
using exec(env, cmd, options...) or even Process.spawn (see below)

> +    Dir.chdir(START_CTX[:cwd])
> +    cmd = [ START_CTX[0] ].concat(START_CTX[:argv])
> +
> +    listener_fds[worker.to_io.fileno] = worker.to_io
> +    listener_fds[worker.master.fileno] = worker.master
> +
> +    # avoid leaking FDs we don't know about, but let before_exec
> +    # unset FD_CLOEXEC, if anything else in the app eventually
> +    # relies on FD inheritence.

Comment seems blindly copy+pasted, since before_exec does not
seem to exist, for this...

> +    (3..1024).each do |io|
> +      next if listener_fds.include?(io)
> +      io = IO.for_fd(io) rescue next
> +      io.autoclose = false
> +      io.close_on_exec = true
> +    end

I suppose we should probably use Process.getrlimit, here,
maybe skip the loop entirely for Ruby 2.0.0+ (which made
close-on-exec the default).

This will be a separate patch...


Since there's no before_exec hook as with SIGUSR2, I think we
can do all of this setup in master and use Process.spawn instead
of fork+exec explicitly.  Process.spawn transparently allows the use
of vfork+exec to avoid fork overhead on some systems.

> +    # exec(command, hash) works in at least 1.9.1+, but will only be
> +    # required in 1.9.4/2.0.0 at earliest.

I guess it's time to update that comment, since 1.9.4 never
happened :)

> +    cmd << listener_fds
> +    logger.info "executing worker #{cmd.inspect} (in #{Dir.pwd})"
> +    exec(*cmd)
> +  end
> +
>    # forcibly terminate all workers that haven't checked in in timeout seconds.  The timeout is implemented using an unlinked File
>    def murder_lazy_workers
>      next_sleep = @timeout - 1
> @@ -495,6 +535,12 @@ def after_fork_internal
>    end
>  
>    def spawn_missing_workers
> +    if @worker_nr
> +      worker = Unicorn::Worker.new(@worker_nr, [ENV["UNICORN_WORKER_TO_IO"], ENV["UNICORN_WORKER_MASTER"]].map{|i| Kgio::Pipe.for_fd(i.to_i)})

Please keep new code wrapped to 80 chars or less.  I'm still
using hardware that predates this project and my eyes will
only get worse with age.

> @@ -505,7 +551,11 @@ def spawn_missing_workers
>          worker.atfork_parent
>        else
>          after_fork_internal
> -        worker_loop(worker)
> +        if @worker_exec
> +          worker_exec(worker)
> +        else
> +          worker_loop(worker)
> +        end

I prefer the ternary operator for simple cases like this.  It is
more compact in terms of screen space and VM bytecode.  But this
may not be changed at all if we switch to using Process.spawn,
instead.

The rest looks fine.  Thanks.

^ permalink raw reply	[relevance 2%]

* [PATCH] t0011-active-unix-socket.sh: fix race condition in test
@ 2017-02-28 23:00  7% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2017-02-28 23:00 UTC (permalink / raw)
  To: unicorn-public

Killing the master process may lead to the worker dying on its
own (as designed); before kill(1) has had an opportunity to send
the second kill(2) syscall on the worker process.

Killing the worker before the master might also lead to a
needless respawn, so merely kill the master and let the worker
follow it in death.

This race condition occasionally caused test failures on slow,
uniprocessor hardware.
---
 t/t0011-active-unix-socket.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t0011-active-unix-socket.sh b/t/t0011-active-unix-socket.sh
index d256f5c..fae0b6c 100755
--- a/t/t0011-active-unix-socket.sh
+++ b/t/t0011-active-unix-socket.sh
@@ -52,7 +52,7 @@ t_begin "worker pid unchanged (again)" && {
 }
 
 t_begin "nuking the existing Unicorn succeeds" && {
-	kill -9 $unicorn_pid $worker_pid
+	kill -9 $unicorn_pid
 	while kill -0 $unicorn_pid
 	do
 		sleep 1
-- 
EW


^ permalink raw reply related	[relevance 7%]

* Re: check_client_connection using getsockopt(2)
  2017-02-23  1:42  2%     ` Eric Wong
@ 2017-02-23  2:42  0%       ` Simon Eskildsen
  0 siblings, 0 replies; 200+ results
From: Simon Eskildsen @ 2017-02-23  2:42 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public, raindrops-public

On Wed, Feb 22, 2017 at 8:42 PM, Eric Wong <e@80x24.org> wrote:
> Simon Eskildsen <simon.eskildsen@shopify.com> wrote:
>
> <snip> Thanks for the writeup!
>
> Another sidenote: It seems nginx <-> unicorn is a bit odd
> for deployment in a containerized environment(*).
>
>> I meant to ask, in Raindrops why do you use the netlink API to get the
>> socket backlog instead of `getsockopt(2)` with `TCP_INFO` to get
>> `tcpi_unacked`? (as described in
>> http://www.ryanfrantz.com/posts/apache-tcp-backlog/) We use this to
>> monitor socket backlogs with a sidekick Ruby daemon. Although we're
>> looking to replace it with a middleware to simplify for Kubernetes.
>> It's one of our main metrics for monitoring performance, especially
>> around deploys.
>
> The netlink API allows independently-spawned processes to
> monitor others; so it can be system-wide.  TCP_INFO requires the
> process doing the checking to also have the socket open.
>
> I guess this reasoning for using netlink is invalid for containers,
> though...

If you namespace the network it's problematic, yeah. I'm considering
right now putting Raindrops in a middleware with the netlink API
inside the container, but it feels weird. That said, if you consider
the alternative of using `getsockopt(2)` on the listening socket, I
don't know how you'd get access to the Unicorn listening socket from a
middleware. Would it be nuts to expose a hook in Unicorn that allows
periodic execution for monitoring listening stats from Raindrops on
the listening socket? It seems somewhat of a narrow use-case, but on
the other hand I'm also not a fan of doing
`Raindrops::Linux.tcp_listener_stats("localhost:#{ENV["PORT"}")`, but
that might be less ugly.

>
>> I was going to use `env["unicorn.socket"]`/`env["puma.socket"]`, but
>> you could also do `env.delete("hijack_io")` after hijacking to allow
>> Unicorn to still render the response. Unfortunately the
>> `<webserver>.socket` key is not part of the Rack standard, so I'm
>> hesitant to use it. When this gets into Unicorn I'm planning to
>> propose it upstream to Puma as well.
>
> I was going to say env.delete("rack.hijack_io") is dangerous
> (since env could be copied by middleware); but apparently not:
> rack.hijack won't work with a copied env, either.
> You only need to delete with the same env object you call
> rack.hijack with.
>
> But calling rack.hijack followed by env.delete may still
> have unintended side-effects in other servers; so I guess
> we (again) cannot rely on hijack working portably.

Exactly, it gives the illusion of portability but e.g. Puma stores an
instance variable to check whether a middleware hijacked, rendering
the `env#delete` option useless.

>
>> Cool. How would you suggest I check for TCP_INFO compatible platforms
>> in Unicorn? Is `RUBY_PLATFORM.ends_with?("linux".freeze)` sufficient
>> or do you prefer another mechanism? I agree that we should fall back
>> to the write hack on other platforms.
>
> The Raindrops::TCP_Info class should be undefined on unsupported
> platforms, so I think you can check for that, instead.  Then it
> should be transparent to add FreeBSD support from unicorn's
> perspective.

Perfect. I'll start working on a patch.

>
>
> (*) So I've been wondering if adding a "unicorn-mode" to an
>     existing C10K, slow-client-capable Ruby/Rack server +
>     reverse proxy makes sense for containerized deploys.
>     Maybe...

I'd love to hear more about this idea. What are you contemplating?

^ permalink raw reply	[relevance 0%]

* Re: check_client_connection using getsockopt(2)
  @ 2017-02-23  1:42  2%     ` Eric Wong
  2017-02-23  2:42  0%       ` Simon Eskildsen
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2017-02-23  1:42 UTC (permalink / raw)
  To: Simon Eskildsen; +Cc: unicorn-public, raindrops-public

Simon Eskildsen <simon.eskildsen@shopify.com> wrote:

<snip> Thanks for the writeup!

Another sidenote: It seems nginx <-> unicorn is a bit odd
for deployment in a containerized environment(*).

> I meant to ask, in Raindrops why do you use the netlink API to get the
> socket backlog instead of `getsockopt(2)` with `TCP_INFO` to get
> `tcpi_unacked`? (as described in
> http://www.ryanfrantz.com/posts/apache-tcp-backlog/) We use this to
> monitor socket backlogs with a sidekick Ruby daemon. Although we're
> looking to replace it with a middleware to simplify for Kubernetes.
> It's one of our main metrics for monitoring performance, especially
> around deploys.

The netlink API allows independently-spawned processes to
monitor others; so it can be system-wide.  TCP_INFO requires the
process doing the checking to also have the socket open.

I guess this reasoning for using netlink is invalid for containers,
though...

> I was going to use `env["unicorn.socket"]`/`env["puma.socket"]`, but
> you could also do `env.delete("hijack_io")` after hijacking to allow
> Unicorn to still render the response. Unfortunately the
> `<webserver>.socket` key is not part of the Rack standard, so I'm
> hesitant to use it. When this gets into Unicorn I'm planning to
> propose it upstream to Puma as well.

I was going to say env.delete("rack.hijack_io") is dangerous
(since env could be copied by middleware); but apparently not:
rack.hijack won't work with a copied env, either.
You only need to delete with the same env object you call
rack.hijack with.

But calling rack.hijack followed by env.delete may still
have unintended side-effects in other servers; so I guess
we (again) cannot rely on hijack working portably.

> Cool. How would you suggest I check for TCP_INFO compatible platforms
> in Unicorn? Is `RUBY_PLATFORM.ends_with?("linux".freeze)` sufficient
> or do you prefer another mechanism? I agree that we should fall back
> to the write hack on other platforms.

The Raindrops::TCP_Info class should be undefined on unsupported
platforms, so I think you can check for that, instead.  Then it
should be transparent to add FreeBSD support from unicorn's
perspective.


(*) So I've been wondering if adding a "unicorn-mode" to an
    existing C10K, slow-client-capable Ruby/Rack server +
    reverse proxy makes sense for containerized deploys.
    Maybe...

^ permalink raw reply	[relevance 2%]

* Re: [PATCH] `unicorn upgrade` script resilience against exceptions
  2016-06-07 13:41  0% ` [PATCH] `unicorn upgrade` script resilience against exceptions Eric Wong
@ 2016-06-07 15:17  0%   ` Jesper Rønn-Jensen
  0 siblings, 0 replies; 200+ results
From: Jesper Rønn-Jensen @ 2016-06-07 15:17 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Thanks for input.

I will close this issue and wait for your patch later, then.

Will also close pull-request I put at github with reference to this thread.

PS. Thanks for tip on HTML vs Plain text mails. Didn't know Gmail
could send plain text :)



On Tue, Jun 7, 2016 at 3:41 PM, Eric Wong <e@80x24.org> wrote:
> Jesper Rønn-Jensen <jesperrr@gmail.com> wrote:
>> This is actually a change proposal "by accident". But first, some
>> background:
>
>         Please don't send HTML mail.  I guess the bounce message didn't
>         include the reason for the bounce, but should be fixed; now;
>         and the bounce will tell you to not send HTML.
>
>         Anyways if you stick to old-school Usenet/mailing list posting
>         conventions, I'm more than happy to help you :)
>
>> My server uses a standard version of this `init.sh` script. I found that
>> executing `unicorn upgrade` often gives me problems.
>
> Hm... I haven't looked at that script in a while given all
> the new-fangled init replacements...
>
>> For example, it breaks my Capistrano deploy script to use `unicorn:upgrade`
>> to deploy. This is a shame, since it's the correct
>> command for Unicorn to pick up any code changes. But unicorn upgrade in the
>> script fails with messages like:
>>
>> ```
>> sudo /etc/init.d/unicorn upgrade
>> Couldn't upgrade, starting 'export HOME=/home/deploy ; cd
>> /var/www/my_app/current && /home/deploy/.rvm/wrappers/my_app/bundle exec
>> unicorn -D -c /var/www/my_app/shared/config/unicorn.rb -E production'
>> instead
>> master failed to start, check stderr log for details
>> ```
>>
>> And the stderror log:
>> ```
>> E, [2016-06-07T09:03:27.517267 #27583] ERROR -- : reaped #<Process::Status:
>> pid 27621 exit 1> exec()-ed
>> /var/www/my_app/shared/vendor/bundle/ruby/2.3.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:195:in
>>     `pid=': Already running on PID:27583 (or
>> pid=/var/www/my_app/shared/tmp/pids/unicorn.pid is stale)
>>     (ArgumentError)
>> ```
>>
>> I am proposing this change because I misread the documentation for the
>> SIGNALS!
>> I thought that Unicorn itself sends a QUIT after all subprocesses has ended
>> via SIG USR2.
>>
>> The change is to remove the QUIT signal to the PID.oldbin process, and that
>> apparently stops all the failures I ran into. So I made this change on a
>> server, and it works!
>>
>> I just don't understand why, since reading the documentation again it says:
>> https://github.com/defunkt/unicorn/blob/d23d4713dc9ab9732c574f5aa34a4b6740b43164/SIGNALS#L35
>>
>> > * USR2 - reexecute the running binary.  A separate QUIT
>> >  should be sent to the original process once the child is verified to
>> >  be up and running.
>>
>> So clearly, I should send the QUIT signal.
>
> Yes.
>
>> My goal is to have a resilient, robust deployment, where unicorn picks up
>> any code changes.
>
> Unfortunately, PID files have always been racy with USR2.
> Nowadays systemd is fairly standardized and seems to work
> pretty well for managing sockets and services.
>
> I'm still not a fan of some systemd things, but I think the socket
> activation part is very nice.  Example @.service and .socket files
> are distributed nowadays:
>
>         https://unicorn.bogomips.org/examples/unicorn@.service
>         https://unicorn.bogomips.org/examples/unicorn.socket
>
>> Can you help me and enlighten me as to why this proposed change works?
>
> Anyways, things are racy in that script and increasing the
> "sleep 2" to a higher number will help if your system is really
> overloaded.
>
>> ---
>>  examples/init.sh | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/examples/init.sh b/examples/init.sh
>> index 1f0e035..cbadc11 100644
>> --- a/examples/init.sh
>> +++ b/examples/init.sh
>> @@ -45,7 +45,7 @@ restart|reload)
>>   $CMD
>>   ;;
>>  upgrade)
>> - if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
>> + if sig USR2 && sleep 2 && sig 0
>>   then
>>   n=$TIMEOUT
>>   while test -s $old_pid && test $n -ge 0
>> --
>
> I actually wrote a better init script for a similar server,
> patch coming in a bit (or later, close to falling asleep).



-- 

Jesper Rønn-Jensen
Nine A/S
Mobile: +45 2989 1822
Blog http://justaddwater.dk/
jesperrr@gmail.com (Private e-mail and Google Talk IM)

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] `unicorn upgrade` script resilience against exceptions
       [not found]     <CAL-rKu6CZ731c=uHyZ8+Fg2fhC30e-3-J26XODacUK=YrfX+5Q@mail.gmail.com>
@ 2016-06-07 13:41  0% ` Eric Wong
  2016-06-07 15:17  0%   ` Jesper Rønn-Jensen
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2016-06-07 13:41 UTC (permalink / raw)
  To: Jesper Rønn-Jensen; +Cc: unicorn-public

Jesper Rønn-Jensen <jesperrr@gmail.com> wrote:
> This is actually a change proposal "by accident". But first, some
> background:

	Please don't send HTML mail.  I guess the bounce message didn't
	include the reason for the bounce, but should be fixed; now;
	and the bounce will tell you to not send HTML.

	Anyways if you stick to old-school Usenet/mailing list posting
	conventions, I'm more than happy to help you :)

> My server uses a standard version of this `init.sh` script. I found that
> executing `unicorn upgrade` often gives me problems.

Hm... I haven't looked at that script in a while given all
the new-fangled init replacements...

> For example, it breaks my Capistrano deploy script to use `unicorn:upgrade`
> to deploy. This is a shame, since it's the correct
> command for Unicorn to pick up any code changes. But unicorn upgrade in the
> script fails with messages like:
> 
> ```
> sudo /etc/init.d/unicorn upgrade
> Couldn't upgrade, starting 'export HOME=/home/deploy ; cd
> /var/www/my_app/current && /home/deploy/.rvm/wrappers/my_app/bundle exec
> unicorn -D -c /var/www/my_app/shared/config/unicorn.rb -E production'
> instead
> master failed to start, check stderr log for details
> ```
> 
> And the stderror log:
> ```
> E, [2016-06-07T09:03:27.517267 #27583] ERROR -- : reaped #<Process::Status:
> pid 27621 exit 1> exec()-ed
> /var/www/my_app/shared/vendor/bundle/ruby/2.3.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:195:in
>     `pid=': Already running on PID:27583 (or
> pid=/var/www/my_app/shared/tmp/pids/unicorn.pid is stale)
>     (ArgumentError)
> ```
> 
> I am proposing this change because I misread the documentation for the
> SIGNALS!
> I thought that Unicorn itself sends a QUIT after all subprocesses has ended
> via SIG USR2.
> 
> The change is to remove the QUIT signal to the PID.oldbin process, and that
> apparently stops all the failures I ran into. So I made this change on a
> server, and it works!
> 
> I just don't understand why, since reading the documentation again it says:
> https://github.com/defunkt/unicorn/blob/d23d4713dc9ab9732c574f5aa34a4b6740b43164/SIGNALS#L35
> 
> > * USR2 - reexecute the running binary.  A separate QUIT
> >  should be sent to the original process once the child is verified to
> >  be up and running.
> 
> So clearly, I should send the QUIT signal.

Yes.

> My goal is to have a resilient, robust deployment, where unicorn picks up
> any code changes.

Unfortunately, PID files have always been racy with USR2.
Nowadays systemd is fairly standardized and seems to work
pretty well for managing sockets and services.

I'm still not a fan of some systemd things, but I think the socket
activation part is very nice.  Example @.service and .socket files
are distributed nowadays:

	https://unicorn.bogomips.org/examples/unicorn@.service
	https://unicorn.bogomips.org/examples/unicorn.socket

> Can you help me and enlighten me as to why this proposed change works?

Anyways, things are racy in that script and increasing the
"sleep 2" to a higher number will help if your system is really
overloaded.

> ---
>  examples/init.sh | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/examples/init.sh b/examples/init.sh
> index 1f0e035..cbadc11 100644
> --- a/examples/init.sh
> +++ b/examples/init.sh
> @@ -45,7 +45,7 @@ restart|reload)
>   $CMD
>   ;;
>  upgrade)
> - if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
> + if sig USR2 && sleep 2 && sig 0
>   then
>   n=$TIMEOUT
>   while test -s $old_pid && test $n -ge 0
> -- 

I actually wrote a better init script for a similar server,
patch coming in a bit (or later, close to falling asleep).

^ permalink raw reply	[relevance 0%]

* [ANN] unicorn 5.1.0 - Rack HTTP server for fast clients and *nix
@ 2016-04-01  0:43  2% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2016-04-01  0:43 UTC (permalink / raw)
  To: ruby-talk, unicorn-public; +Cc: Adam Duke, Aaron Patterson

Unicorn is an HTTP server for Rack applications designed to only serve
fast clients on low-latency, high-bandwidth connections and take
advantage of features in Unix/Unix-like kernels.  Slow clients should
only be served by placing a reverse proxy capable of fully buffering
both the the request and response in between unicorn and slow clients.

* http://unicorn.bogomips.org/
* public list: unicorn-public@bogomips.org
* mail archives: http://bogomips.org/unicorn-public/
* git clone git://bogomips.org/unicorn.git
* http://unicorn.bogomips.org/NEWS.atom.xml
* nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn

Changes:

    unicorn 5.1.0 - rack is optional, again

    Note: no code changes since 5.1.0.pre1 from January.^WNo, wait,
    last minute performance improvement added today.  See below.

    The big change is rack is not required (but still recommended).
    Applications are expected to depend on rack on their own so they can
    specify the version of rack they prefer without unicorn pulling
    in a newer, potentially incompatible version.

    unicorn will always attempt to work with multiple versions of rack
    as practical.

    The HTTP parser also switched to using the TypedData C-API for
    extra type safety and memory usage accounting support in the
    'objspace' extension.

    Thanks to Adam Duke to bringing the rack change to our attention
    and Aaron Patterson for helping with the matter.

    Last minute change: we now support the new leftpad() syscall under
    Linux for major performance and security improvements:

        http://mid.gmane.org/1459463613-32473-1-git-send-email-richard@nod.at

    8^H9 changes since 5.0.1:

          http: TypedData C-API conversion
          various documentation updates
          doc: bump olddoc to ~> 1.2 for extra NNTP URL
          rack is optional at runtime, required for dev
          doc update for ClientShutdown exceptions class
          unicorn 5.1.0.pre1 - rack is optional, again
          doc: reference --keep-file-descriptors for "bundle exec"
          doc: further trimming to reduce noise
          use leftpad Linux syscall for speed!

^ permalink raw reply	[relevance 2%]

* [PATCH] doc: reference --keep-file-descriptors for "bundle exec"
  2016-03-12 23:19  2%             ` Amir Yalon
@ 2016-03-17  0:27  0%               ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2016-03-17  0:27 UTC (permalink / raw)
  To: Amir Yalon; +Cc: Christos Trochalakis, unicorn-public

Amir Yalon <amiryal@yxejamir.net> wrote:
> It became obvious that RVM is not to blame, and the real culprit was
> Bundler’s `bundle exec` script.  From here it was just a matter of
> figuring out how to launch the unicorn script from within the bundle
> using the correct ruby without calling `bundle exec`.  There are a
> number of ways to do that, here is what worked for me:

Ahah, seems like an old problem with bundler defaults.

> ExecStart = /usr/local/rvm/gems/ruby-2.0.0-p645/wrappers/ruby -r
> bundler/setup vendor/bundle/ruby/2.0.0/bin/unicorn -c
> config/unicorn/unicorn.rb -E staging -s myapp@%i

Actually, "bundle exec --keep-file-descriptors" should work
nowadays.  See patch below.

> Thanks again to all participants, your input was invaluable.

No problem.

> P.S.  Notice how I abuse the -s no-op parameter to identify which
> template instance is running in systemd (in the output of ps, for
> example), maybe worth documenting it as a feature?

*shrug* I prefer to use full the full path to the config file
which would already include the app name in it:

	unicorn -c /path/to/my_app/unicorn.conf.rb ...

-------------------------8<-------------------------
Subject: [PATCH] doc: reference --keep-file-descriptors for "bundle exec"

"bundle exec" alone is not suitable for use with systemd-style
socket activation due to Ruby 2.0+ behavior of setting close-on-exec
for file descriptors above 2.  However, the "--keep-file-descriptors"
option was added to bundler 1.4.0 to workaround this Ruby 2.0 change
and may be used to prevent Ruby 2.0+ from closing file descriptors
on exec.

Thanks to Amir Yalon and Christos Trochalakis for bringing up
this issue on the mailing list:

http://bogomips.org/unicorn-public/1457824748.3666627.547425122.2A828B07@webmail.messagingengine.com/
---
 Sandbox                   | 6 +++---
 examples/unicorn@.service | 2 ++
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/Sandbox b/Sandbox
index 997b92f..0760065 100644
--- a/Sandbox
+++ b/Sandbox
@@ -63,9 +63,9 @@ before_exec hook as illustrated by https://gist.github.com/534668
 === Ruby 2.0.0 close-on-exec and SIGUSR2 incompatibility
 
 Ruby 2.0.0 enforces FD_CLOEXEC on file descriptors by default.  unicorn
-has been prepared for this behavior since unicorn 4.1.0, but we forgot
-to remind the Bundler developers.  This issue is being tracked here:
-https://github.com/bundler/bundler/issues/2628
+has been prepared for this behavior since unicorn 4.1.0, and bundler
+needs the "--keep-file-descriptors" option for "bundle exec":
+http://bundler.io/man/bundle-exec.1.html
 
 == Isolate
 
diff --git a/examples/unicorn@.service b/examples/unicorn@.service
index b058da5..5b16b5b 100644
--- a/examples/unicorn@.service
+++ b/examples/unicorn@.service
@@ -11,6 +11,8 @@ Wants = unicorn.socket
 After = unicorn.socket
 
 [Service]
+# bundler users must use the "--keep-file-descriptors" switch, here:
+# ExecStart = bundle exec --keep-file-descriptors /usr/bin/unicorn ...
 ExecStart = /usr/bin/unicorn -c /path/to/unicorn.conf.rb /path/to/config.ru
 Sockets = unicorn.socket
 KillSignal = SIGQUIT
-- 
EW


^ permalink raw reply related	[relevance 0%]

* Re: Systemd socket inheritance fails with “not a socket file descriptor”
  @ 2016-03-12 23:19  2%             ` Amir Yalon
  2016-03-17  0:27  0%               ` [PATCH] doc: reference --keep-file-descriptors for "bundle exec" Eric Wong
  0 siblings, 1 reply; 200+ results
From: Amir Yalon @ 2016-03-12 23:19 UTC (permalink / raw)
  To: Christos Trochalakis, Eric Wong; +Cc: unicorn-public

Hi all,
 
After isolating the problem with the following one-liner in bash:
 
$ /usr/local/rvm/bin/rvm default exec ruby -e 'puts
IO.for_fd(3).inspect' 3<<<test
#<IO:fd 3>
 
$ bundle exec ruby -e 'puts IO.for_fd(3).inspect' 3<<<test
-e:1:in `for_fd': The given fd is not accessible because RubyVM reserves
it (ArgumentError)
        from -e:1:in `<main>'
 
$ /usr/local/rvm/rubies/default/bin/ruby -r bundler/setup -e 'puts
IO.for_fd(3).inspect' 3<<<test
#<IO:fd 3>
 
It became obvious that RVM is not to blame, and the real culprit was
Bundler’s `bundle exec` script.  From here it was just a matter of
figuring out how to launch the unicorn script from within the bundle
using the correct ruby without calling `bundle exec`.  There are a
number of ways to do that, here is what worked for me:
 
ExecStart = /usr/local/rvm/gems/ruby-2.0.0-p645/wrappers/ruby -r
bundler/setup vendor/bundle/ruby/2.0.0/bin/unicorn -c
config/unicorn/unicorn.rb -E staging -s myapp@%i
 
Thanks again to all participants, your input was invaluable.
 
 
P.S.  Notice how I abuse the -s no-op parameter to identify which
template instance is running in systemd (in the output of ps, for
example), maybe worth documenting it as a feature?
 
Regards,
Amir

^ permalink raw reply	[relevance 2%]

* Re: Systemd socket inheritance fails with “not a socket file descriptor” [take2]
  2016-03-09  3:51  2%         ` Eric Wong
@ 2016-03-09 13:19  0%           ` Christos Trochalakis
    1 sibling, 0 replies; 200+ results
From: Christos Trochalakis @ 2016-03-09 13:19 UTC (permalink / raw)
  To: Eric Wong; +Cc: Amir Yalon, unicorn-public

On Wed, Mar 09, 2016 at 03:51:57AM +0000, Eric Wong wrote:
>Amir Yalon <amiryal@yxejamir.net> wrote:
>> Yes, everything looks as expected, one inherited socket starting at fd
>> number 3:
>>
>> I=[3] PID=4869 FDS=1
>>
>> Also tried with Ruby 2.2.1, same results.  FWIW, my rubies are compiled
>> by system-wide RVM with no special options given to it.
>
>Interesting.   Can you try using some other non-Ruby daemon
>which uses sd_listen_fds, too?   I can't think of any off
>the top of my head, but I'm sure there's some.
>
>It seems like systemd is mis-setting FD_CLOEXEC on socket,
>even.
>
>> Thanks for taking the time to investigate, really appreciated.
>
>No problem.  I wonder if something else is broken in your systemd
>config...
>
>Again, I'll take a harder look at newer systemd later this week
>unless somebody beats me to it.

I was also unable to reproduce it in:

* debian stretch, ruby2.2 / systemd 229
* ubuntu wily, ruby2.{1,2} / systemd 225

I used system rubies for all the experments, not rvm.

Amir, could you also send us for completeness your unicorn config and
the output of systemctl cat unicorn.socket & systemctl cat
unicorn@1.service ?

^ permalink raw reply	[relevance 0%]

* Re: Systemd socket inheritance fails with “not a socket file descriptor”
  @ 2016-03-09  3:51  2%         ` Eric Wong
  2016-03-09 13:19  0%           ` Systemd socket inheritance fails with “not a socket file descriptor” [take2] Christos Trochalakis
    0 siblings, 2 replies; 200+ results
From: Eric Wong @ 2016-03-09  3:51 UTC (permalink / raw)
  To: Amir Yalon; +Cc: unicorn-public

Amir Yalon <amiryal@yxejamir.net> wrote:
> Yes, everything looks as expected, one inherited socket starting at fd
> number 3:
> 
> I=[3] PID=4869 FDS=1
> 
> Also tried with Ruby 2.2.1, same results.  FWIW, my rubies are compiled
> by system-wide RVM with no special options given to it.

Interesting.   Can you try using some other non-Ruby daemon
which uses sd_listen_fds, too?   I can't think of any off
the top of my head, but I'm sure there's some.

It seems like systemd is mis-setting FD_CLOEXEC on socket,
even.

> Thanks for taking the time to investigate, really appreciated.

No problem.  I wonder if something else is broken in your systemd
config...

Again, I'll take a harder look at newer systemd later this week
unless somebody beats me to it.

^ permalink raw reply	[relevance 2%]

* [ANN] unicorn 5.1.0.pre1 - Rack HTTP server for fast clients and *nix
@ 2016-01-27 23:16  2% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2016-01-27 23:16 UTC (permalink / raw)
  To: ruby-talk, unicorn-public

Unicorn is an HTTP server for Rack applications designed to only serve
fast clients on low-latency, high-bandwidth connections and take
advantage of features in Unix/Unix-like kernels.  Slow clients should
only be served by placing a reverse proxy capable of fully buffering
both the the request and response in between unicorn and slow clients.

* http://unicorn.bogomips.org/
* public list: unicorn-public@bogomips.org
* mail archives: http://bogomips.org/unicorn-public/
* git clone git://bogomips.org/unicorn.git
* http://unicorn.bogomips.org/NEWS.atom.xml
* nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn

This is a pre-release, you will need to specify the version to
install explicitly when using RubyGems.

Changes:

    unicorn 5.1.0.pre1 - rack is optional, again

    The big change is rack is not required (but still recommended).
    Applications are expected to depend on rack on their own so they can
    specify the version of rack they prefer without unicorn pulling
    in a newer, potentially incompatible version.

    unicorn will always attempt to work with multiple versions of rack
    as practical.

    The HTTP parser also switched to using the TypedData C-API for
    extra type safety and memory usage accounting support in the
    'objspace' extension.

    Thanks to Adam Duke to bringing the rack change to our attention
    and Aaron Patterson for helping with the matter.

    There might be more documentation-related changes before 5.1.0
    final.  I am considering dropping pandoc from manpage generation
    and relying on pod2man (from Perl) because it has a wider install
    base.

    5 changes since v5.0.1:

          http: TypedData C-API conversion
          various documentation updates
          doc: bump olddoc to ~> 1.2 for extra NNTP URL
          rack is optional at runtime, required for dev
          doc update for ClientShutdown exceptions class

^ permalink raw reply	[relevance 2%]

* [ANN] unicorn 5.0.1 - Rack HTTP server for fast clients and *nix
       [not found]     <20151117-unicorn-5.0.1-rele@sed>
@ 2015-11-17 22:48  2% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2015-11-17 22:48 UTC (permalink / raw)
  To: ruby-talk, unicorn-public; +Cc: Owen Ou, Christos Trochalakis

Unicorn is an HTTP server for Rack applications designed to only serve
fast clients on low-latency, high-bandwidth connections and take
advantage of features in Unix/Unix-like kernels.  Slow clients should
only be served by placing a reverse proxy capable of fully buffering
both the the request and response in between unicorn and slow clients.

* http://unicorn.bogomips.org/
* public list: unicorn-public@bogomips.org
* mail archives: http://bogomips.org/unicorn-public/
* git clone git://bogomips.org/unicorn.git
* http://unicorn.bogomips.org/NEWS.atom.xml
* nntp://news.public-inbox.org/inbox.comp.lang.ruby.unicorn

Changes since 5.0.0:

unicorn 5.0.1 - continuing to violate Rack SPEC

Once again, we allow nil values in response headers.  We've had
this bug since March 2009, and thus cannot expect existing
applications and middlewares running unicorn to fix this.

Unfortunately, supporting this bug contributes to application
server lock-in, but at least we'll document it as such.

Thanks to Owen Ou <o@heroku.com> for reporting this regression:

  http://bogomips.org/unicorn-public/CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com/

Additionally, systemd examples are now in the examples/ directory
based on a post by Christos Trochalakis <yatiohi@ideopolis.gr>:

  http://bogomips.org/unicorn-public/20150708130821.GA1361@luke.ws.skroutz.gr/

-- 
EW

^ permalink raw reply	[relevance 2%]

* Re: Request to follow SemVer/mention it in homepage
  2015-09-29 19:36  3%     ` Eric Wong
@ 2015-09-30 16:04  0%       ` Pirate Praveen
  0 siblings, 0 replies; 200+ results
From: Pirate Praveen @ 2015-09-30 16:04 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On Wednesday 30 September 2015 01:06 AM, Eric Wong wrote:
> Pirate Praveen <praveen@debian.org> wrote:
>> On २९ सप्टेंबर, २०१५ १:०६:५० [PM] IST, Eric Wong <e@80x24.org> 
>> wrote:
>>> Pirate Praveen <praveen@debian.org> wrote:
>>>> Do you follow Semantic Versioning as defined by semver.org? 
>>>> If yes, can you mention it on the homepage? If not, can you 
>>>> consider it?
> 
> <snip>
> 
>>> I have considered it in the past, but decided against it...
>>> 
>>> unicorn is on the verge of becoming 5.x:
>>> 
>>> This is for internal changes which some weirdo projects 
>>> (notably Rainbows!) rely on; but was never considered public 
>>> API or marketed as such.  I absolutely do not want people 
>>> building server-specific apps when Rack exists.
>> 
>> Can you consider SemVer from 5.x?
> 
> Again, the answer is "no".  I do not want people to depend on 
> unicorn providing a stable API of any sort.  Even if in practice 
> and history it does: the config file directives and internal 
> constant names haven't changed at all in 5-6 years and there are
> no plans to change it.

Can you mention the recommended way of adding unicorn in a Gemfile in
your home page?

gitlab and diaspora has unicorn in their Gemfile, for a user they want
gitlab or diaspora and they don't care if its using unicorn or puma or
passenger.

I need some kind of an official statement about compatibility which I
can show to projects like gitlab and diaspora, so they don't insist on
exact patch release of unicorn. I could patch the Gemfile, but that
adds extra burden on me as a maintainer which I'd like to avoid if
possible.

>>> Regardless of a formalities such as semver, I'll work to
>>> ensure unicorn can be compatible[1] with any Rack apps in
>>> Debian main. Heck, perhaps Rack 0.9.x still works (assuming
>>> the installed Ruby version supports it).
>>> 
>>> Just let us know what real problems you find, they will be 
>>> fixed.
>> 
>> If you can convince gitlab to declare ~> 4.8 and assure it will 
>> work with 4.9, that will solve the issue for now.
>> 
>> See https://gitlab.com/gitlab-org/gitlab-ce/issues/2820
> 
> That's not a real compatibility bug with unicorn itself.  Again, 
> it's wrong of them to have a dependency on any Rack server at all.
> 
>> Also if you can help convinse diaspora upstream to declare a 
>> looser dependency, that will also help.
>> 
>>> Fwiw, Debian is my preferred platform and I can stay 100% 
>>> within email with the Debian BTS + lists.
> 
> I am only doing plain-text email.  I'm done with websites requiring
> login + registration.
> 

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJWDAf9AAoJEM4fnGdFEsIqYV0P/AmIFATrnxSF4V5Q5v05LT5G
LcVkXCzMhQYnPaVYwiAEi5JLGkiFw2r2Y5kWgF3WLynXHqWmUpGoNLEsG5tZG38V
109RYYKG5VgsGw6HlIoOwpy61MF9/d29+CRDXvLs0q7wc1/CbDEYqnhwLvKKOT9o
xZBD3bXsLuXiRl/BR8xk1q5fy4oS0rNnTU4MWcc8pEKU3KFE0xkXqA5WVS3ENShg
WLfqlUnll2SnQwyR1FzqFJws5OzejKpdNRZK1IOD1ivMd8aBVY25+0u6Q3o/2h/J
dW4ID9QV6R1fCtLybKqkI/fwljlKGs+shY+5lvUqE2KSYe8gwyHm6r1DslprzwM6
QhzLXqBtvDxSxX41sbcr12SoEt7poRztr+8gFXqni+iTkoljgsmODQMWX/ed/9O8
AekqfSrA//K1JfUpGKWHr1+UXhijHggYcKi0eoCJA2GH5ijLu5DIglEuGCQtthl9
z4/aI/na1M7goqBKw1Qtuxc/AHlBAkDcsott5n+4FIQInc5ooiyj54+Sek1AZNWw
/pp5F6z3axPgH3+h26Jf4W8OHl58gUeht9G7sl2j5GwMDkVSOtwlgwhLEYWdZ8Mw
pZcDXl/0VieD6LBFEh3N5MfkP+n3aYHrenCbsxRBh/jn6/+wczoUruNYl0SbSgUh
62qZ2q/7NgvHBdVPtXaB
=F2uT
-----END PGP SIGNATURE-----

^ permalink raw reply	[relevance 0%]

* Re: Request to follow SemVer/mention it in homepage
  @ 2015-09-29 19:36  3%     ` Eric Wong
  2015-09-30 16:04  0%       ` Pirate Praveen
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2015-09-29 19:36 UTC (permalink / raw)
  To: Pirate Praveen; +Cc: unicorn-public

Pirate Praveen <praveen@debian.org> wrote:
> On २९ सप्टेंबर, २०१५ १:०६:५० [PM] IST, Eric Wong <e@80x24.org> wrote:
> >Pirate Praveen <praveen@debian.org> wrote:
> >> Do you follow Semantic Versioning as defined by semver.org? If yes,
> >> can you mention it on the homepage? If not, can you consider it?

<snip>

> >I have considered it in the past, but decided against it...
> >
> >unicorn is on the verge of becoming 5.x:
> >
> >This is for internal changes which some weirdo projects (notably
> >Rainbows!) rely on; but was never considered public API or marketed
> >as such.  I absolutely do not want people building server-specific
> >apps when Rack exists.
> 
> Can you consider SemVer from 5.x?

Again, the answer is "no".  I do not want people to depend on unicorn
providing a stable API of any sort.  Even if in practice and history
it does: the config file directives and internal constant names
haven't changed at all in 5-6 years and there are no plans to change it.

> >Regardless of a formalities such as semver, I'll work to ensure unicorn
> >can be compatible[1] with any Rack apps in Debian main.
> >Heck, perhaps Rack 0.9.x still works (assuming the installed Ruby
> >version supports it).
> >
> >Just let us know what real problems you find, they will be fixed.
> 
> If you can convince gitlab to declare ~> 4.8 and assure it will work
> with 4.9, that will solve the issue for now.
> 
> See https://gitlab.com/gitlab-org/gitlab-ce/issues/2820

That's not a real compatibility bug with unicorn itself.  Again, it's
wrong of them to have a dependency on any Rack server at all.

> Also if you can help convinse diaspora upstream to declare a looser
> dependency, that will also help.
> 
> >Fwiw, Debian is my preferred platform and I can stay 100% within email
> >with the Debian BTS + lists.

I am only doing plain-text email.  I'm done with websites requiring
login + registration.

^ permalink raw reply	[relevance 3%]

* Unicorn returns blank page after no use
@ 2015-07-01 16:08  1% Farjad Adamjee
  0 siblings, 0 replies; 200+ results
From: Farjad Adamjee @ 2015-07-01 16:08 UTC (permalink / raw)
  To: unicorn-public

Hello,

I am not sure if this is the correct place for this, I have googled 
around to see if anyone else has encountered such an issue, but I did 
not find anything.

I am having this issue where after a period of time of no use (on beta 
[production] environment), maybe 12 hours or so, the unicorn process 
sleeps. When I go and re-request the page, it returns blank.

I am using Apache proxy to Unicorn.

My unicorn.stderr.log states:
I, [2015-07-01T03:07:05.168511 #13258]  INFO -- : master done reopening logs
I, [2015-07-01T03:07:05.232546 #16222]  INFO -- : worker=2 done 
reopening logs
I, [2015-07-01T03:07:05.239934 #13308]  INFO -- : worker=0 done 
reopening logs
I, [2015-07-01T03:07:05.307616 #13311]  INFO -- : worker=1 done 
reopening logs


When I request the page again, it loads fine. It is only during the 
process of "reopening logs" when it returns a blank page.

My unicorn.rb file:

worker_processes 3

# Load app into the master before forking workers
# for super-fast worker spawn times
preload_app true

# Listen on
listen "127.0.0.1:8550", :backlog => 1024

# Restart any workers that haven't responded in 180 seconds
timeout 600

# PID for the Unicorn master
pid "/var/www/r/shared/pids/unicorn.pid"

# Unicorn Log paths
stderr_path "/var/www/r/shared/log/unicorn.stderr.log"
stdout_path "/var/www/r/shared/log/unicorn.stdout.log"

before_fork do |server, worker|
   ##
   # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
   # immediately start loading up a new version of itself (loaded with a new
   # version of our app). When this new Unicorn is completely loaded
   # it will begin spawning workers. The first worker spawned will check to
   # see if an .oldbin pidfile exists. If so, this means we've just 
booted up
   # a new Unicorn and need to tell the old one that it can now die. To 
do so
   # we send it a QUIT.
   #
   # Using this method we get 0 downtime deploys.
   defined?(ActiveRecord::Base) and 
ActiveRecord::Base.connection.disconnect!
   defined?(OathKeeper) and OathKeeper.disconnect!

   old_pid = '/var/www/r/shared/pids/unicorn.pid.oldbin'
   if File.exists?(old_pid) && server.pid != old_pid
     begin
       Process.kill("QUIT", File.read(old_pid).to_i)
     rescue Errno::ENOENT, Errno::ESRCH
       # someone else did our job for us
     end
   end
end

after_fork do |server, worker|
   ##
   #  # Unicorn master loads the app then forks off workers - because of 
the way
   #    # Unix forking works, we need to make sure we aren't using any 
of the parent's
   #      # sockets, e.g. db connection
   #
   defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
   defined?(OathKeeper) and OathKeeper.reconnect
end

Any help would be appreciated!
Thank you!

Farjad


-- 
Farjad Adamjee
844.246.4018


^ permalink raw reply	[relevance 1%]

* [PATCH 1/2] move the socket into Rack env for hijacking
  @ 2015-06-06  1:58  2% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2015-06-06  1:58 UTC (permalink / raw)
  To: unicorn-public; +Cc: Eric Wong

This avoids the expensive generic instance variable for @socket
and exposes the socket as `env["unicorn.socket"]' to the Rack
application.

As as nice side-effect, applications may access
`env["unicorn.socket"]' as part of the API may be useful for
3rd-party bits such as Raindrops::TCP_Info for reading the tcp_info
struct on Linux-based systems.

Yes, `env["unicorn.socket"]' is a proprietary API in unicorn!
News at 11!  But then again, unicorn is not the first Rack server
to implement `env["#{servername}.socket"]', either...
---
 lib/unicorn/http_request.rb | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index b60e383..f5c6b5b 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -33,6 +33,7 @@ class Unicorn::HttpParser
   # 2.2+ optimizes hash assignments when used with literal string keys
   REMOTE_ADDR = 'REMOTE_ADDR'.freeze
   RACK_INPUT = 'rack.input'.freeze
+  UNICORN_SOCKET = 'unicorn.socket'.freeze
   HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
   @@input_class = Unicorn::TeeInput
   @@check_client_connection = false
@@ -99,7 +100,7 @@ class Unicorn::HttpParser
                     NULL_IO : @@input_class.new(socket, self)
 
     # for Rack hijacking in Rack 1.5 and later
-    @socket = socket
+    e[UNICORN_SOCKET] = socket
     e[RACK_HIJACK] = self
 
     e.merge!(DEFAULTS)
@@ -108,7 +109,7 @@ class Unicorn::HttpParser
   # for rack.hijack, we respond to this method so no extra allocation
   # of a proc object
   def call
-    env[RACK_HIJACK_IO] = @socket
+    env[RACK_HIJACK_IO] = env[UNICORN_SOCKET]
   end
 
   def hijacked?
-- 
EW


^ permalink raw reply related	[relevance 2%]

* Re: On USR2, new master runs with same PID
  2015-03-12  6:45  0%     ` Eric Wong
@ 2015-03-20  1:58  2%       ` Kevin Yank
  0 siblings, 0 replies; 200+ results
From: Kevin Yank @ 2015-03-20  1:58 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

[-- Attachment #1: Type: text/plain, Size: 576 bytes --]

Regarding zero-downtime deploys:

On 12 Mar 2015, at 5:45 pm, Eric Wong <e@80x24.org> wrote:

> Best bet would be to run with double the workers temporarily unless
> you're too low on memory (and swapping) or backend (DB) connections or
> any other resource.

I’d like to take this approach as I do have enough memory to spare. How do you usually implement this? Any good write-ups or sample configs you can point me to?

Thanks again,

--
Kevin Yank
Chief Technology Officer, Avalanche Technology Group
http://avalanche.com.au/

ph: +61 4 2241 0083




^ permalink raw reply	[relevance 2%]

* Re: On USR2, new master runs with same PID
  2015-03-12  6:26  1%   ` Kevin Yank
@ 2015-03-12  6:45  0%     ` Eric Wong
  2015-03-20  1:58  2%       ` Kevin Yank
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2015-03-12  6:45 UTC (permalink / raw)
  To: Kevin Yank; +Cc: unicorn-public

Kevin Yank <kyank@avalanche.com.au> wrote:
> It’s possible; I’m using eye (https://github.com/kostya/eye) as a

Aha!  I forgot about that one, try upgrading to unicorn 4.8.3 which
fixed this issue last year.  ref:

http://bogomips.org/unicorn-public/m/20140504023338.GA583@dcvr.yhbt.net.html
http://bogomips.org/unicorn-public/m/20140502231558.GA4215@dcvr.yhbt.net.html

<snip>

> unicorn master
> `- unicorn master 
> `- unicorn worker[2]
> |  `- unicorn worker[2]
> `- unicorn worker[1]
> |  `- unicorn worker[1]
> `- unicorn worker[0]
>    `- unicorn worker[0]

The second master there is the new one, and the first is the old one.

I was more concerned if there are multiple masters with no parent-child
relationship to each other; but that seems to not be the case.

But yeah, give 4.8.3 a try since it should've fixed the problem
(which was reported and confirmed privately)

> This has been happening pretty frequently across multiple server
> instances, and again once it starts happening on an instance, it keeps
> happening 100% of the time (until I restart Unicorn completely). So
> it’s not a rare edge case.

You can probably recover by removing the pid files entirely
and HUP the (only) master; but I haven't tried it...

> > That's a pretty fragile config and I regret ever including it in the
> > example config files

> Any better examples/docs you’d recommend I consult for guidance? Or is
> expecting to achieve a robust zero-downtime restart using
> before_fork/after_fork hooks unrealistic?

Best bet would be to run with double the workers temporarily unless
you're too low on memory (and swapping) or backend (DB) connections or
any other resource.

If you're really low on memory/connections, do WINCH + USR2 + QUIT (and
pray the new master works right :).  Clients will suffer in response
time as your new app loads, so do it during non-peak traffic and/or
shift traffic away from that machine.

You can also send TTOU a few times to reduce worker counts instead of
WINCH to nuke all of them, but I'd send all signals via deploy
script/commands rather than automatically via (synchronous) hooks.

But that's just me; probably others do things differently...

^ permalink raw reply	[relevance 0%]

* Re: On USR2, new master runs with same PID
  @ 2015-03-12  6:26  1%   ` Kevin Yank
  2015-03-12  6:45  0%     ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Kevin Yank @ 2015-03-12  6:26 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

[-- Attachment #1: Type: text/plain, Size: 5164 bytes --]

Thanks for your help, Eric!

> On 12 Mar 2015, at 12:45 pm, Eric Wong <e@80x24.org> wrote:
> 
> Kevin Yank <kyank@avalanche.com.au> wrote:
>> When I send USR2 to the master process (PID 19216 in this example), I
>> get the following in the Unicorn log:
>> 
>> I, [2015-03-11T23:47:33.992274 #6848]  INFO -- : executing ["/srv/ourapp/shared/bundle/ruby/2.2.0/bin/unicorn", "/srv/ourapp/current/config.ru", "-Dc", "/srv/ourapp/shared/config/unicorn.rb", {10=>#<Kgio::UNIXServer:/srv/ourapp/shared/sockets/unicorn.sock>}] (in /srv/ourapp/releases/a0e8b5df474ad5129200654f92a76af00a750f47)
>> I, [2015-03-11T23:47:36.504235 #6848]  INFO -- : inherited addr=/srv/ourapp/shared/sockets/unicorn.sock fd=10
>> /srv/ourapp/shared/bundle/ruby/2.2.0/gems/unicorn-4.8.1/lib/unicorn/http_server.rb:206:in `pid=': Already running on PID:19216 (or pid=/srv/ourapp/shared/pids/unicorn.pid is stale) (ArgumentError)
> 
> Nothing suspicious until the above line...

That’s right.

>> And when I check, indeed, there is now unicorn.pid and
>> unicorn.pid.oldbin, both containing 19216.
> 
> Any chance you have a process manager or something else creating the
> (non-.oldbin) pid file behind unicorn's back?

It’s possible; I’m using eye (https://github.com/kostya/eye) as a process monitor. I’m not aware of it writing .pid files for processes that self-daemonize like Unicorn, though. And once one of my servers “goes bad” (i.e. Unicorn starts failing to restart in response to a USR2), it does so 100% consistently until I stop and restart Unicorn entirely. Based on that, I don’t believe it’s a race condition where my process monitor is slipping in a new unicorn.pid file some of the time.

> Can you check your process table to ensure there's not multiple
> unicorn instances running off the same config and pid files, too?

As far as I can tell, I have some servers that have gotten themselves into this “USR2 restart fails” state, while others are working just fine. In both cases, the Unicorn process tree (as show in htop) looks like this “at rest” (i.e. no deployments/restarts in progress):

unicorn master
`- unicorn master
`- unicorn worker[2]
|  `- unicorn worker[2]
`- unicorn worker[1]
|  `- unicorn worker[1]
`- unicorn worker[0]
   `- unicorn worker[0]

At first glance I’d definitely say it appears that I have two masters running from the same config files. However, there’s only one unicorn.pid file of course (the root process in the tree above), and when I try to kill -TERM the master process that doesn’t have a .pid file, the entire process tree exits. Am I misinterpreting the process table? Is this process list actually normal?

Thus far I’ve been unable to find any difference in state between a properly-behaving server and a misbehaving server, apart from the behaviour of the Unicorn master when it receives a USR2.

> Also, were there other activity (another USR2 or HUP) in the logs
> a few seconds beforehand?

No, didn’t see anything like that (and I was looking for it).

> What kind of filesystem / kernel is the pid file on?

EXT4 / Ubuntu Server 12.04 LTS

> A network FS or anything without the consistency guarantees provided
> by POSIX would not work for pid files.

Given my environment above, I should be OK, right?

> pid files are unfortunately prone to to nasty race conditions,
> but I'm not sure what you're seeing happens very often.

This has been happening pretty frequently across multiple server instances, and again once it starts happening on an instance, it keeps happening 100% of the time (until I restart Unicorn completely). So it’s not a rare edge case.

> -------------------8<-------------------
>>  sleep 10
>> 
>>  old_pid = "#{server.config[:pid]}.oldbin"
>>  if File.exists?(old_pid) && server.pid != old_pid
>>    begin
>>      Process.kill("QUIT", File.read(old_pid).to_i)
>>    rescue Errno::ENOENT, Errno::ESRCH
>>      # someone else did our job for us
>>    end
>>  end
> -------------------8<-------------------
> 
> I'd get rid of that hunk starting with the "sleep 10" (at least while
> debugging this issue).

If I simply delete this hunk, I’ll have old masters left running on my servers because they’ll never get sent the QUIT signal. I can definitely remove it temporarily (and kill the old master myself) while debugging, though.

> If you did a USR2 previously, maybe it could
> affect the current USR2 upgrade process.  Sleeping so long in the master
> like that is pretty bad it throws off timing and delays signal handling.

I’d definitely like to get rid of the sleep, as my restarts definitely feel slow. I’m not clear on what a better approach would be, though.

> That's a pretty fragile config and I regret ever including it in the
> example config files

Any better examples/docs you’d recommend I consult for guidance? Or is expecting to achieve a robust zero-downtime restart using before_fork/after_fork hooks unrealistic?

--
Kevin Yank
Chief Technology Officer, Avalanche Technology Group
http://avalanche.com.au/

ph: +61 4 2241 0083




^ permalink raw reply	[relevance 1%]

* [PATCH] explain 11 byte magic number for self-pipe
@ 2015-02-18  9:36  2% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2015-02-18  9:36 UTC (permalink / raw)
  To: unicorn-public; +Cc: Steven Stewart-Gallus, Jesse Storimer

Oops, this should've been explained long ago but apparently not.

In response to a comment on
http://www.sitepoint.com/the-self-pipe-trick-explained/

> Does anybody know why both unicorn and foreman read 11 bytes from
> self-pipe?

Unfortunately I couldn't find a way to comment on the site on a
JavaScript-free browser nor does it seem possible without
registering.

Again, anybody can send plain-text mail to:
unicorn-public@bogomips.org

No registration, no real name policy, no terms-of-service, just
plain-text.  Feel free to use Tor, mixmaster or any anonymity
service, too.
---
 lib/unicorn/http_server.rb | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 015bd94..683eb82 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -370,6 +370,10 @@ class Unicorn::HttpServer
   # wait for a signal hander to wake us up and then consume the pipe
   def master_sleep(sec)
     IO.select([ @self_pipe[0] ], nil, nil, sec) or return
+    # 11 bytes is the maximum string length which can be embedded within
+    # the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
+    # Most reads are only one byte here and uncommon, so it's not worth a
+    # persistent buffer, either:
     @self_pipe[0].kgio_tryread(11)
   end
 
-- 
EW

^ permalink raw reply related	[relevance 2%]

* [PATCH] ISSUES: add section for bugs in other projects
@ 2015-02-10 17:06  4% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2015-02-10 17:06 UTC (permalink / raw)
  To: unicorn-public

This is not anything new, just documenting what has been going
on since the beginning.

There's been a small number of generic networking (or mm) bugs in
the kernel which affect unicorn, but are usually found and fixed
with more popular, non-Ruby servers, first.

Aside from generic performance problems, I don't think there's ever
been a glibc bug which affected unicorn.
---
 ISSUES | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/ISSUES b/ISSUES
index f66d14a..343cab4 100644
--- a/ISSUES
+++ b/ISSUES
@@ -18,6 +18,36 @@ instead and your issue will be handled discreetly.
 If you don't get a response within a few days, we may have forgotten
 about it so feel free to ask again.
 
+== Bugs in related projects
+
+unicorn is sometimes affected by bugs in its dependencies.  Bugs
+triggered by unicorn in mainline Ruby, rack, GNU C library (glibc),
+or the Linux kernel will be reported upstream and fixed.
+
+For bugs in Ruby itself, we may forward bugs to
+https://bugs.ruby-lang.org/ and discuss+fix them on the ruby-core
+list at mailto:ruby-core@ruby-lang.org
+Subscription to post is required to ruby-core, unfortunately:
+mailto:ruby-core-request@ruby-lang.org?subject=subscribe
+
+For uncommon bugs in Rack, we may forward bugs to
+mailto:rack-devel@googlegroups.com and discuss there.
+Subscription (without any web UI or Google account) is possible via:
+mailto:rack-devel+subscribe@googlegroups.com
+Note: not everyone can use the proprietary bug tracker used by Rack,
+but their mailing list remains operational.
+
+Uncommon bugs we encounter in the Linux kernel should be Cc:-ed to the
+Linux kernel mailing list (LKML) at mailto:linux-kernel@vger.kernel.org
+and other kernel lists such as mailto:netdev@vger.kernel.org
+(for networking issues).  No subscription is necessary, and the our
+mailing list follows the same conventions as LKML for interopability.
+
+Likewise for any rare glibc bugs we might encounter, we should Cc:
+mailto:libc-alpha@sourceware.org
+Keep in mind glibc upstream does use Bugzilla for tracking bugs:
+https://sourceware.org/bugzilla/
+
 == Submitting Patches
 
 See the HACKING document (and additionally, the
-- 
EW

^ permalink raw reply related	[relevance 4%]

* Re: Unicorn workers timing out intermittently
  2014-12-30 18:12  0% ` Eric Wong
@ 2014-12-30 20:02  2%   ` Aaron Price
  0 siblings, 0 replies; 200+ results
From: Aaron Price @ 2014-12-30 20:02 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Thank for your help, Eric.

I was able to pinpoint the issue, and I've posted the solution.

The issue was that I wasn't closing a database connection for code being
run in custom Rack middleware. Which is why the logs didn't show anything.

http://serverfault.com/questions/655430/unicorn-workers-timing-out-intermittently/655478#655478

Thanks again.


On Tue, Dec 30, 2014 at 1:12 PM, Eric Wong <e@80x24.org> wrote:

> Aaron Price <aprice@flipgive.com> wrote:
> > Hey guys,
> >
> > I'm having the following issue and I was hoping you might be able to shed
> > some light on the issue. Maybe give some suggestions on how to debug the
> > actual problem.
> >
> >
> http://serverfault.com/questions/655430/unicorn-workers-timing-out-intermittently
>
> (quoting the problem in full to save folks from having to load the page)
>
> >    I'm getting intermittent timeouts from unicorn workers for seemingly
> no
> >    reason, and I'd like some help to debug the actual problem. It's worse
> >    because it works about 10 - 20 requests then 1 will timeout, then
> >    another 10 - 20 requests and the same thing will happen again.
> >
> >    I've created a dev environment to illustrate this particular problem,
> >    so there is NO traffic except mine.
> >
> >    The stack is Ubuntu 14.04, Rails 3.2.21, PostgreSQL 9.3.4, Unicorn
> >    4.8.3, Nginx 1.6.2.
> >
> >    The Problem
> >
> >    I'll describe in detail the time that it doesn't work.
> >
> >    I request a url through the browser.
> > Started GET
> "/offers.xml?q%5bupdated_at_greater_than_or_equal_to%5d=2014-12-28T1
> > 8:01:16Z&q%5bupdated_at_less_than_or_equal_to%5d=2014-12-28T19:30:21Z"
> for 127.0
> > .0.1 at 2014-12-30 15:58:59 +0000
> > Completed 200 OK in 10.3ms (Views: 0.0ms | ActiveRecord: 2.1ms)
> >
> >    As you can see, the request completed successfully with a 200 response
> >    status in just 10.3ms.
> >
> >    However, the browser hangs for about 30 seconds and Unicorn kills the
> >    worker:
> > E, [2014-12-30T15:59:30.267605 #13678] ERROR -- : worker=0 PID:14594
> timeout (31
> > s > 30s), killing
> > E, [2014-12-30T15:59:30.279000 #13678] ERROR -- : reaped
> #<Process::Status: pid
> > 14594 SIGKILL (signal 9)> worker=0
> > I, [2014-12-30T15:59:30.355085 #23533]  INFO -- : worker=0 ready
> >
> >    And the following error in the Nginx logs:
> > 2014/12/30 15:59:30 [error] 23463#0: *27 upstream prematurely closed
> connection
> > while reading response header from upstream, client: 127.0.0.1, server:
> localhos
> > t, request: "GET
> /offers.xml?q%5bupdated_at_greater_than_or_equal_to%5d=2014-12-
> >
> 28T18:01:16Z&q%5bupdated_at_less_than_or_equal_to%5d=2014-12-28T19:30:21Z
> HTTP/1
> > .1", upstream: "http://unix:
> /app/shared/tmp/sockets/unicorn.sock:/offers.xml?q%5
> >
> bupdated_at_greater_than_or_equal_to%5d=2014-12-28T18:01:16Z&q%5bupdated_at_less
> > _than_or_equal_to%5d=2014-12-28T19:30:21Z", host: "localhost", referrer:
> "http:/
> >
> /localhost/offers.xml?q%5bupdated_at_greater_than_or_equal_to%5d=2014-12-28T18:0
> > 1:16Z&q%5bupdated_at_less_than_or_equal_to%5d=2014-12-28T19:30:21Z"
>
> OK, the problem should be reproducible hitting unicorn directly (easy to
> configure a TCP listener) and eliminating nginx as a possible
> point-of-failure.  Also, I suggest using curl so you can script and see
> everything going on more easily at the protocol/syscall level.
>
> >    Again. There's no load on the server at all. The only requests going
> >    through are my own and every 10 - 20 requests at random have this same
> >    problem.
>
> OK, the main culprit is usually external connections acting up.  Postgres,
> external monitoring services, network failures, etc...
>
> >    It doesn't look like unicorn is eating memory at all. I know this
> >    because I'm using watch -n 0.5 free -m and this is the result.
>
> What does the CPU usage look like?
>
> >              total       used       free     shared    buffers     cached
> > Mem:          1995        765       1229          0         94        405
> > -/+ buffers/cache:        264       1730
> > Swap:          511          0        511
> >
> >    So the server isn't running out of memory.
> >
> >    Is there anything further I can do to debug this issue? or any insight
> >    into what's happening?
>
> Basically, minimize and isolate things.  Use a single worker, strace
> that worker ("strace -f -p $PID_OF_WORKER") and send a request to it
> using curl.
>
> Is the Postgres doing OK?  How is the network connection to that?
> What other external resources (monitoring services, memcached, redis,
> etc...) might you be hitting?  You'll see blocking (on things like
> read*/write*/select/poll/recvmsg/sendmsg) on strace.
>
> You can map numeric file descriptors to ports via:
>  lsof -p $PID_OF_WORKER
>
> Also, is your server running out of entropy?  (You'll see blocking on
> reading /dev/random via strace) and you may also see low values in
> /proc/sys/kernel/random/entropy_avail
>



-- 
*Aaron Price*
Sr. Web Developer
FlipGive
P: (416) 583-2510

312 Adelaide St. West, Suite 301
Toronto, ON, M5V 1R2
www.flipgive.com

Follow us on: Facebook <http://www.facebook.com/FlipGive> | Twitter
<http://twitter.com/FlipGive> | LinkedIn
<http://www.linkedin.com/company/FlipGive>


^ permalink raw reply	[relevance 2%]

* Re: Unicorn workers timing out intermittently
  @ 2014-12-30 18:12  0% ` Eric Wong
  2014-12-30 20:02  2%   ` Aaron Price
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2014-12-30 18:12 UTC (permalink / raw)
  To: Aaron Price; +Cc: unicorn-public

Aaron Price <aprice@flipgive.com> wrote:
> Hey guys,
> 
> I'm having the following issue and I was hoping you might be able to shed
> some light on the issue. Maybe give some suggestions on how to debug the
> actual problem.
> 
> http://serverfault.com/questions/655430/unicorn-workers-timing-out-intermittently

(quoting the problem in full to save folks from having to load the page)

>    I'm getting intermittent timeouts from unicorn workers for seemingly no
>    reason, and I'd like some help to debug the actual problem. It's worse
>    because it works about 10 - 20 requests then 1 will timeout, then
>    another 10 - 20 requests and the same thing will happen again.
> 
>    I've created a dev environment to illustrate this particular problem,
>    so there is NO traffic except mine.
> 
>    The stack is Ubuntu 14.04, Rails 3.2.21, PostgreSQL 9.3.4, Unicorn
>    4.8.3, Nginx 1.6.2.
> 
>    The Problem
> 
>    I'll describe in detail the time that it doesn't work.
> 
>    I request a url through the browser.
> Started GET "/offers.xml?q%5bupdated_at_greater_than_or_equal_to%5d=2014-12-28T1
> 8:01:16Z&q%5bupdated_at_less_than_or_equal_to%5d=2014-12-28T19:30:21Z" for 127.0
> .0.1 at 2014-12-30 15:58:59 +0000
> Completed 200 OK in 10.3ms (Views: 0.0ms | ActiveRecord: 2.1ms)
> 
>    As you can see, the request completed successfully with a 200 response
>    status in just 10.3ms.
> 
>    However, the browser hangs for about 30 seconds and Unicorn kills the
>    worker:
> E, [2014-12-30T15:59:30.267605 #13678] ERROR -- : worker=0 PID:14594 timeout (31
> s > 30s), killing
> E, [2014-12-30T15:59:30.279000 #13678] ERROR -- : reaped #<Process::Status: pid
> 14594 SIGKILL (signal 9)> worker=0
> I, [2014-12-30T15:59:30.355085 #23533]  INFO -- : worker=0 ready
> 
>    And the following error in the Nginx logs:
> 2014/12/30 15:59:30 [error] 23463#0: *27 upstream prematurely closed connection
> while reading response header from upstream, client: 127.0.0.1, server: localhos
> t, request: "GET /offers.xml?q%5bupdated_at_greater_than_or_equal_to%5d=2014-12-
> 28T18:01:16Z&q%5bupdated_at_less_than_or_equal_to%5d=2014-12-28T19:30:21Z HTTP/1
> .1", upstream: "http://unix:/app/shared/tmp/sockets/unicorn.sock:/offers.xml?q%5
> bupdated_at_greater_than_or_equal_to%5d=2014-12-28T18:01:16Z&q%5bupdated_at_less
> _than_or_equal_to%5d=2014-12-28T19:30:21Z", host: "localhost", referrer: "http:/
> /localhost/offers.xml?q%5bupdated_at_greater_than_or_equal_to%5d=2014-12-28T18:0
> 1:16Z&q%5bupdated_at_less_than_or_equal_to%5d=2014-12-28T19:30:21Z"

OK, the problem should be reproducible hitting unicorn directly (easy to
configure a TCP listener) and eliminating nginx as a possible
point-of-failure.  Also, I suggest using curl so you can script and see
everything going on more easily at the protocol/syscall level.

>    Again. There's no load on the server at all. The only requests going
>    through are my own and every 10 - 20 requests at random have this same
>    problem.

OK, the main culprit is usually external connections acting up.  Postgres,
external monitoring services, network failures, etc...

>    It doesn't look like unicorn is eating memory at all. I know this
>    because I'm using watch -n 0.5 free -m and this is the result.

What does the CPU usage look like?

>              total       used       free     shared    buffers     cached
> Mem:          1995        765       1229          0         94        405
> -/+ buffers/cache:        264       1730
> Swap:          511          0        511
> 
>    So the server isn't running out of memory.
> 
>    Is there anything further I can do to debug this issue? or any insight
>    into what's happening?

Basically, minimize and isolate things.  Use a single worker, strace
that worker ("strace -f -p $PID_OF_WORKER") and send a request to it
using curl.

Is the Postgres doing OK?  How is the network connection to that?
What other external resources (monitoring services, memcached, redis,
etc...) might you be hitting?  You'll see blocking (on things like
read*/write*/select/poll/recvmsg/sendmsg) on strace.

You can map numeric file descriptors to ports via:
 lsof -p $PID_OF_WORKER

Also, is your server running out of entropy?  (You'll see blocking on
reading /dev/random via strace) and you may also see low values in
/proc/sys/kernel/random/entropy_avail

^ permalink raw reply	[relevance 0%]

* RE: Issue with Unicorn: Big latency when getting a request
  2014-11-14  9:09  2%         ` Roberto Cordoba del Moral
@ 2014-11-14  9:32  1%           ` Roberto Cordoba del Moral
  0 siblings, 0 replies; 200+ results
From: Roberto Cordoba del Moral @ 2014-11-14  9:32 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public@bogomips.org

Hi Eric,
 as you don´t like web. I don´t know if you read my latest updates. Please, find them attached. I don´t know if they could be helpful tracks.
UPDATE: I don´t know why it seems to be related to cache or cookies. When I delete browsing history with cache and cookies in my browser and I load the site the issue doesn´t happen anymore. After that, if I just refresh the page, the issue happens.
Thanks,Roberto.

From: roberto.chingon@hotmail.com
To: e@80x24.org
CC: unicorn-public@bogomips.org
Subject: RE: Issue with Unicorn: Big latency when getting a request
Date: Fri, 14 Nov 2014 10:09:13 +0100




Hi Eric,
   I´m not pretty sure how to crank up the verbosity of nginx although I think I got it, because when I restart Nginx I get next logs:
2014/11/14 10:00:10 [notice] 39156#0: using the "kqueue" event method2014/11/14 10:00:10 [warn] 39156#0: 1024 worker_connections exceed open file resource limit: 2562014/11/14 10:00:10 [notice] 39156#0: nginx/1.6.02014/11/14 10:00:10 [notice] 39156#0: built by clang 5.1 (clang-503.0.40) (based on LLVM 3.4svn)2014/11/14 10:00:10 [notice] 39156#0: OS: Darwin 13.4.02014/11/14 10:00:10 [notice] 39156#0: hw.ncpu: 42014/11/14 10:00:10 [notice] 39156#0: net.inet.tcp.sendspace: 1310722014/11/14 10:00:10 [notice] 39156#0: kern.ipc.somaxconn: 1282014/11/14 10:00:10 [notice] 39156#0: getrlimit(RLIMIT_NOFILE): 256:92233720368547758072014/11/14 10:00:10 [notice] 39157#0: start worker processes2014/11/14 10:00:10 [notice] 39157#0: start worker process 39158
Besides that, I got nothing else in this log when I load my website.
I think I have just one worker. Please find my nginx.conf file:

#user  nobody;worker_processes  1;
#error_log  logs/error.log;#error_log  logs/error.log  notice;#error_log  logs/error.log  info;error_log   logs/error.log debug;
#pid        logs/nginx.pid;

events {    worker_connections  1024;





















}
http {    include       mime.types;    default_type  application/octet-stream;
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '    #                  '$status $body_bytes_sent "$http_referer" '    #                  '"$http_user_agent" "$http_x_forwarded_for"';
   # access_log  logs/access.log  main;
    sendfile        on;    #tcp_nopush     on;
    #keepalive_timeout  0;    keepalive_timeout  65;
    #gzip  on;
    server {        listen       80;        server_name  localhost;
        #charset koi8-r;        access_log  logs/host.access.log;        rewrite_log on;        location / {            root   /Users/Rober/Projects/yanpy/dev/yanpy/app;            index  index.html index.htm;        }
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html        #        error_page   500 502 503 504  /50x.html;










































        location = /50x.html {            root   html;        }
    }




















}
Regarding your comment: Again, please do not send HTML or top-post. We adhere to old-fashioned mailing list posting conventions here.
I don´t know what you mean. I´m answering you from my outlook web mail account.
Thanks, Roberto.


> Date: Fri, 14 Nov 2014 07:19:22 +0000
> From: e@80x24.org
> To: roberto.chingon@hotmail.com
> CC: unicorn-public@bogomips.org
> Subject: Re: Issue with Unicorn: Big latency when getting a request
> 
> Roberto Cordoba del Moral <roberto.chingon@hotmail.com> wrote:
> > Hi Eric,
> >   nothing in my Nginx error.log.
> > If I point my browser directly to my endpoint by localhost:8080/getTranslationLanguages I get the results in json format in my browser inmediately (with no latencies). This is working fine.
> > Thanks for your support.Roberto.
> 
> So the issue is with nginx communicating with unicorn, and not unicorn
> itself.  Can you crank up the verbosity of nginx logging and/or strace
> an nginx worker to see what's going on?
> 
> (only configure nginx to use one worker to guarantee you're stracing
> right one).
> 
> 
> Again, please do not send HTML or top-post.  We adhere to old-fashioned
> mailing list posting conventions here.  Thanks.
 		 	   		   		 	   		  

^ permalink raw reply	[relevance 1%]

* RE: Issue with Unicorn: Big latency when getting a request
  2014-11-14  7:19  2%       ` Eric Wong
@ 2014-11-14  9:09  2%         ` Roberto Cordoba del Moral
  2014-11-14  9:32  1%           ` Roberto Cordoba del Moral
  0 siblings, 1 reply; 200+ results
From: Roberto Cordoba del Moral @ 2014-11-14  9:09 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public@bogomips.org

Hi Eric,
   I´m not pretty sure how to crank up the verbosity of nginx although I think I got it, because when I restart Nginx I get next logs:
2014/11/14 10:00:10 [notice] 39156#0: using the "kqueue" event method2014/11/14 10:00:10 [warn] 39156#0: 1024 worker_connections exceed open file resource limit: 2562014/11/14 10:00:10 [notice] 39156#0: nginx/1.6.02014/11/14 10:00:10 [notice] 39156#0: built by clang 5.1 (clang-503.0.40) (based on LLVM 3.4svn)2014/11/14 10:00:10 [notice] 39156#0: OS: Darwin 13.4.02014/11/14 10:00:10 [notice] 39156#0: hw.ncpu: 42014/11/14 10:00:10 [notice] 39156#0: net.inet.tcp.sendspace: 1310722014/11/14 10:00:10 [notice] 39156#0: kern.ipc.somaxconn: 1282014/11/14 10:00:10 [notice] 39156#0: getrlimit(RLIMIT_NOFILE): 256:92233720368547758072014/11/14 10:00:10 [notice] 39157#0: start worker processes2014/11/14 10:00:10 [notice] 39157#0: start worker process 39158
Besides that, I got nothing else in this log when I load my website.
I think I have just one worker. Please find my nginx.conf file:

#user  nobody;worker_processes  1;
#error_log  logs/error.log;#error_log  logs/error.log  notice;#error_log  logs/error.log  info;error_log   logs/error.log debug;
#pid        logs/nginx.pid;

events {    worker_connections  1024;





















}
http {    include       mime.types;    default_type  application/octet-stream;
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '    #                  '$status $body_bytes_sent "$http_referer" '    #                  '"$http_user_agent" "$http_x_forwarded_for"';
   # access_log  logs/access.log  main;
    sendfile        on;    #tcp_nopush     on;
    #keepalive_timeout  0;    keepalive_timeout  65;
    #gzip  on;
    server {        listen       80;        server_name  localhost;
        #charset koi8-r;        access_log  logs/host.access.log;        rewrite_log on;        location / {            root   /Users/Rober/Projects/yanpy/dev/yanpy/app;            index  index.html index.htm;        }
        #error_page  404              /404.html;
        # redirect server error pages to the static page /50x.html        #        error_page   500 502 503 504  /50x.html;










































        location = /50x.html {            root   html;        }
    }




















}
Regarding your comment: Again, please do not send HTML or top-post. We adhere to old-fashioned mailing list posting conventions here.
I don´t know what you mean. I´m answering you from my outlook web mail account.
Thanks, Roberto.


> Date: Fri, 14 Nov 2014 07:19:22 +0000
> From: e@80x24.org
> To: roberto.chingon@hotmail.com
> CC: unicorn-public@bogomips.org
> Subject: Re: Issue with Unicorn: Big latency when getting a request
> 
> Roberto Cordoba del Moral <roberto.chingon@hotmail.com> wrote:
> > Hi Eric,
> >   nothing in my Nginx error.log.
> > If I point my browser directly to my endpoint by localhost:8080/getTranslationLanguages I get the results in json format in my browser inmediately (with no latencies). This is working fine.
> > Thanks for your support.Roberto.
> 
> So the issue is with nginx communicating with unicorn, and not unicorn
> itself.  Can you crank up the verbosity of nginx logging and/or strace
> an nginx worker to see what's going on?
> 
> (only configure nginx to use one worker to guarantee you're stracing
> right one).
> 
> 
> Again, please do not send HTML or top-post.  We adhere to old-fashioned
> mailing list posting conventions here.  Thanks.
 		 	   		  

^ permalink raw reply	[relevance 2%]

* Re: Issue with Unicorn: Big latency when getting a request
  @ 2014-11-14  7:19  2%       ` Eric Wong
  2014-11-14  9:09  2%         ` Roberto Cordoba del Moral
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2014-11-14  7:19 UTC (permalink / raw)
  To: Roberto Cordoba del Moral; +Cc: unicorn-public

Roberto Cordoba del Moral <roberto.chingon@hotmail.com> wrote:
> Hi Eric,
>   nothing in my Nginx error.log.
> If I point my browser directly to my endpoint by localhost:8080/getTranslationLanguages I get the results in json format in my browser inmediately (with no latencies). This is working fine.
> Thanks for your support.Roberto.

So the issue is with nginx communicating with unicorn, and not unicorn
itself.  Can you crank up the verbosity of nginx logging and/or strace
an nginx worker to see what's going on?

(only configure nginx to use one worker to guarantee you're stracing
right one).


Again, please do not send HTML or top-post.  We adhere to old-fashioned
mailing list posting conventions here.  Thanks.

^ permalink raw reply	[relevance 2%]

* Re: Having issue with Unicorn
  @ 2014-10-24 21:24  2%                                   ` Imdad
  0 siblings, 0 replies; 200+ results
From: Imdad @ 2014-10-24 21:24 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Thanks Eric, will look into this issue.

Much appreciated if you could forward this issue to someone who can help
here.

Thanks again for your cooperation.

Cheers!
Imdad Ali Khan
Mob (0) 9818484057
http://www.linkedin.com/in/imdad

On 25 October 2014 02:28, Eric Wong <e@80x24.org> wrote:

> Imdad <khanimdad@gmail.com> wrote:
> > My app logs (shared/log/production.log) and /var/log/nginx/error.log both
> > are empty
>
> I'm not up-to-date on Rails logging these days (see Rails docs if nobody
> else answers), but for nginx, you can use this to increase verbosity:
>
>         error_log /path/to/nginx/error.log debug
>
> ref: http://nginx.org/en/docs/ngx_core_module.html#error_log
>
> In unicorn, you can also bypass nginx for debugging purposes by
> setting up another listener on any port you want:
>
>         listen 12345
>


^ permalink raw reply	[relevance 2%]

* Re: Having issue with Unicorn
  2014-10-24 19:41  3%                 ` Eric Wong
@ 2014-10-24 19:58  0%                   ` Imdad
    0 siblings, 1 reply; 200+ results
From: Imdad @ 2014-10-24 19:58 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Here you go...
root@Hailisys:/var/www/hailisys/shared/pids# kill -HUP $(cat unicorn.pid)
root@Hailisys:/var/www/hailisys/shared/pids# kill -USR2 $(cat unicorn.pid)
root@Hailisys:/var/www/hailisys/shared/pids# cd ..
root@Hailisys:/var/www/hailisys/shared# tail -n 20 -f
log/unicorn.stderr.log
E, [2014-10-24T19:55:54.656013 #8814] ERROR -- :
/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/gems/unicorn-4.8.3/lib/unicorn/util.rb:39:in
`each'
E, [2014-10-24T19:55:54.656044 #8814] ERROR -- :
/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/gems/unicorn-4.8.3/lib/unicorn/util.rb:39:in
`reopen_logs'
E, [2014-10-24T19:55:54.656119 #8814] ERROR -- :
/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:743:in
`load_config!'
E, [2014-10-24T19:55:54.656150 #8814] ERROR -- :
/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/gems/unicorn-4.8.3/lib/unicorn/http_server.rb:325:in
`join'
E, [2014-10-24T19:55:54.656179 #8814] ERROR -- :
/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/gems/unicorn-4.8.3/bin/unicorn:126:in
`<top (required)>'
E, [2014-10-24T19:55:54.656206 #8814] ERROR -- :
/var/www/hailisys/releases/6/vendor/bundle/ruby/2.1.0/bin/unicorn:23:in
`load'
E, [2014-10-24T19:55:54.656233 #8814] ERROR -- :
/var/www/hailisys/releases/6/vendor/bundle/ruby/2.1.0/bin/unicorn:23:in
`<main>'
I, [2014-10-24T19:55:54.795603 #8814]  INFO -- : reaped #<Process::Status:
pid 17502 exit 0> worker=0
I, [2014-10-24T19:55:54.803712 #18246]  INFO -- : worker=0 ready
I, [2014-10-24T19:55:54.838425 #8814]  INFO -- : reaped #<Process::Status:
pid 17505 exit 0> worker=1
I, [2014-10-24T19:55:54.851649 #18249]  INFO -- : worker=1 ready
I, [2014-10-24T19:56:16.054817 #18256]  INFO -- : executing
["/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/bin/unicorn", "-c",
"/var/www/hailisys/current/config/unicorn.rb", "-D", "-E", "production",
{11=>#<Kgio::UNIXServer:fd 11>}] (in /var/www/hailisys/releases/31)
I, [2014-10-24T19:56:16.626199 #18256]  INFO -- : inherited
addr=/var/www/hailisys/shared/sockets/unicorn.sock fd=11
I, [2014-10-24T19:56:16.626930 #18256]  INFO -- : Refreshing Gem list
I, [2014-10-24T19:56:18.723512 #18256]  INFO -- : master process ready
I, [2014-10-24T19:56:18.727108 #18261]  INFO -- : worker=0 ready
I, [2014-10-24T19:56:18.742734 #18264]  INFO -- : worker=1 ready
I, [2014-10-24T19:56:18.921508 #8814]  INFO -- : reaped #<Process::Status:
pid 18246 exit 0> worker=0
I, [2014-10-24T19:56:18.921691 #8814]  INFO -- : reaped #<Process::Status:
pid 18249 exit 0> worker=1
I, [2014-10-24T19:56:18.921776 #8814]  INFO -- : master complete


Cheers!
Imdad Ali Khan
Mob (0) 9818484057
http://www.linkedin.com/in/imdad

On 25 October 2014 01:11, Eric Wong <e@80x24.org> wrote:

> Imdad <khanimdad@gmail.com> wrote:
> > INFO -- : executing
> > ["/var/www/hailisys/releases/6/vendor/bundle/ruby/2.1.0/bin/unicorn",
> "-c"
>
> Gack, we missed that having the wrong path, too.  You probably need
> this in your config/unicorn.rb:
>
>    Unicorn::HttpServer::START_CTX[0] =
>         "/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/bin/unicorn"
>
> And do the HUP/USR2 again.  And if it fails again, show us the logs
> right after the HUP, too.  Thanks.
>


^ permalink raw reply	[relevance 0%]

* Re: Having issue with Unicorn
  @ 2014-10-24 19:41  3%                 ` Eric Wong
  2014-10-24 19:58  0%                   ` Imdad
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2014-10-24 19:41 UTC (permalink / raw)
  To: Imdad; +Cc: unicorn-public

Imdad <khanimdad@gmail.com> wrote:
> INFO -- : executing
> ["/var/www/hailisys/releases/6/vendor/bundle/ruby/2.1.0/bin/unicorn", "-c"

Gack, we missed that having the wrong path, too.  You probably need
this in your config/unicorn.rb:

   Unicorn::HttpServer::START_CTX[0] =
        "/var/www/hailisys/current/vendor/bundle/ruby/2.1.0/bin/unicorn"

And do the HUP/USR2 again.  And if it fails again, show us the logs
right after the HUP, too.  Thanks.

^ permalink raw reply	[relevance 3%]

* Re: Reserved workers not as webservers
  2014-10-11  3:35  2%           ` Bráulio Bhavamitra
@ 2014-10-13  0:10  0%             ` Bráulio Bhavamitra
  0 siblings, 0 replies; 200+ results
From: Bráulio Bhavamitra @ 2014-10-13  0:10 UTC (permalink / raw)
  To: Eric Wong; +Cc: Devin Ben-Hur, Michael Fischer, unicorn-public

I'm pretty happy to say this daemons setup is working beautifully on
three production rails apps (10 workers each). It is really nice to
have one pid/master process for the entire app, to know unicorn master
restarts the daemons if they crash (which sometimes happens with
delayed_job), and to restart the app and daemons all by once (with
USR2 signal), and fast!

Thanks Eric and other unicorn developers!

cheers,
bráulio

On Sat, Oct 11, 2014 at 12:35 AM, Bráulio Bhavamitra
<braulio@eita.org.br> wrote:
> On Thu, Oct 9, 2014 at 3:15 PM, Eric Wong <e@80x24.org> wrote:
>> Bráulio Bhavamitra <braulio@eita.org.br> wrote:
>>> Eric, how with a simple monkey patch will allow a worker to not
>>> connect with the kernel request queue?
>>
>> The listen socket is inherited by default.  Closing it works.  You can
>> also keep the socket open and avoid calling any accept() wrappers, that
>> is like a "pop" operation on the queue:
>>
>>   kgio_tryaccept in unicorn, accept/accept_nonblock/sysaccept in stdlib
> Done. If you have free time, please take a look at
> https://gist.github.com/brauliobo/11298486#file-unicorn-conf-rb-L139
>
>>
>> Also, please don't top-post (or send giant sigs).  Thanks.
> Sorry for that (again)

^ permalink raw reply	[relevance 0%]

* Re: Reserved workers not as webservers
  @ 2014-10-11  3:35  2%           ` Bráulio Bhavamitra
  2014-10-13  0:10  0%             ` Bráulio Bhavamitra
  0 siblings, 1 reply; 200+ results
From: Bráulio Bhavamitra @ 2014-10-11  3:35 UTC (permalink / raw)
  To: Eric Wong; +Cc: Devin Ben-Hur, Michael Fischer, unicorn-public

On Thu, Oct 9, 2014 at 3:15 PM, Eric Wong <e@80x24.org> wrote:
> Bráulio Bhavamitra <braulio@eita.org.br> wrote:
>> Eric, how with a simple monkey patch will allow a worker to not
>> connect with the kernel request queue?
>
> The listen socket is inherited by default.  Closing it works.  You can
> also keep the socket open and avoid calling any accept() wrappers, that
> is like a "pop" operation on the queue:
>
>   kgio_tryaccept in unicorn, accept/accept_nonblock/sysaccept in stdlib
Done. If you have free time, please take a look at
https://gist.github.com/brauliobo/11298486#file-unicorn-conf-rb-L139

>
> Also, please don't top-post (or send giant sigs).  Thanks.
Sorry for that (again)

^ permalink raw reply	[relevance 2%]

* Re: Master hooks needed
  2014-10-04  1:57  6%         ` Eric Wong
@ 2014-10-04  2:04  0%           ` Bráulio Bhavamitra
  0 siblings, 0 replies; 200+ results
From: Bráulio Bhavamitra @ 2014-10-04  2:04 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Documentation is an excelent solution :)

On Fri, Oct 3, 2014 at 10:57 PM, Eric Wong <e@80x24.org> wrote:
> Bráulio Bhavamitra <braulio@eita.org.br> wrote:
>> I think the hook is needed because I took too much time to figure out
>> the problem and much more time to figure out the solution (this
>> master_run variable). Also, I don't think this master_run solution is
>> elegant.
>
> A guard variable is fairly common practice for initialization.
> It's not always nice, but I do not consider the existing hooks
> to be elegant, either; they're only unfortunately necessary.
>
> I consider having redundant features to be even worse.
>
> How about the following documentation change instead?
>
> --- a/examples/unicorn.conf.rb
> +++ b/examples/unicorn.conf.rb
> @@ -54,12 +54,23 @@ GC.respond_to?(:copy_on_write_friendly=) and
>  # fast LAN.
>  check_client_connection false
>
> +# local variable to guard against running a hook multiple times
> +run_once = true
> +
>  before_fork do |server, worker|
>    # the following is highly recomended for Rails + "preload_app true"
>    # as there's no need for the master process to hold a connection
>    defined?(ActiveRecord::Base) and
>      ActiveRecord::Base.connection.disconnect!
>
> +  # Occasionally, it may be necessary to run non-idempotent code in the
> +  # master before forking.  Keep in mind the above disconnect! example
> +  # is idempotent and does not need a guard.
> +  if run_once
> +    # do_something_once_here ...
> +    run_once = false # prevent from firing again
> +  end
> +
>    # The following is only recommended for memory/DB-constrained
>    # installations.  It is not needed if your system can house
>    # twice as many worker_processes as you have configured.



-- 
"Lute pela sua ideologia. Seja um com sua ideologia. Viva pela sua
ideologia. Morra por sua ideologia" P.R. Sarkar

EITA - Educação, Informação e Tecnologias para Autogestão
http://cirandas.net/brauliobo
http://eita.org.br

"Paramapurusha é meu pai e Parama Prakriti é minha mãe. O universo é
meu lar e todos nós somos cidadãos deste cosmo. Este universo é a
imaginação da Mente Macrocósmica, e todas as entidades estão sendo
criadas, preservadas e destruídas nas fases de extroversão e
introversão do fluxo imaginativo cósmico. No âmbito pessoal, quando
uma pessoa imagina algo em sua mente, naquele momento, essa pessoa é a
única proprietária daquilo que ela imagina, e ninguém mais. Quando um
ser humano criado mentalmente caminha por um milharal também
imaginado, a pessoa imaginada não é a propriedade desse milharal, pois
ele pertence ao indivíduo que o está imaginando. Este universo foi
criado na imaginação de Brahma, a Entidade Suprema, por isso a
propriedade deste universo é de Brahma, e não dos microcosmos que
também foram criados pela imaginação de Brahma. Nenhuma propriedade
deste mundo, mutável ou imutável, pertence a um indivíduo em
particular; tudo é o patrimônio comum de todos."
Restante do texto em
http://cirandas.net/brauliobo/blog/a-problematica-de-hoje-em-dia

^ permalink raw reply	[relevance 0%]

* Re: Master hooks needed
  @ 2014-10-04  1:57  6%         ` Eric Wong
  2014-10-04  2:04  0%           ` Bráulio Bhavamitra
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2014-10-04  1:57 UTC (permalink / raw)
  To: Bráulio Bhavamitra; +Cc: unicorn-public

Bráulio Bhavamitra <braulio@eita.org.br> wrote:
> I think the hook is needed because I took too much time to figure out
> the problem and much more time to figure out the solution (this
> master_run variable). Also, I don't think this master_run solution is
> elegant.

A guard variable is fairly common practice for initialization.
It's not always nice, but I do not consider the existing hooks
to be elegant, either; they're only unfortunately necessary.

I consider having redundant features to be even worse.

How about the following documentation change instead?

--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -54,12 +54,23 @@ GC.respond_to?(:copy_on_write_friendly=) and
 # fast LAN.
 check_client_connection false
 
+# local variable to guard against running a hook multiple times
+run_once = true
+
 before_fork do |server, worker|
   # the following is highly recomended for Rails + "preload_app true"
   # as there's no need for the master process to hold a connection
   defined?(ActiveRecord::Base) and
     ActiveRecord::Base.connection.disconnect!
 
+  # Occasionally, it may be necessary to run non-idempotent code in the
+  # master before forking.  Keep in mind the above disconnect! example
+  # is idempotent and does not need a guard.
+  if run_once
+    # do_something_once_here ...
+    run_once = false # prevent from firing again
+  end
+
   # The following is only recommended for memory/DB-constrained
   # installations.  It is not needed if your system can house
   # twice as many worker_processes as you have configured.

^ permalink raw reply	[relevance 6%]

* Re: fork() errors lead to a completely dead unicorn
  @ 2014-09-07 10:12  2%   ` Jonathan del Strother
  0 siblings, 0 replies; 200+ results
From: Jonathan del Strother @ 2014-09-07 10:12 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Just wanted to say thanks for the reply - I've been trying to figure
this out over the weekend and not succeeding.  I can't seem to
reproduce it in a self-contained environment, it only ever happens in
production, which is making debugging a bit frustrating...

>
>> I, [2014-09-03T08:51:48.876341 #10484]  INFO -- : master complete
>
> But the original master does not die after this?

99% sure it doesn't - it just sits there in a zombie state with no
workers.  But I want to verify that, so I guess I'm stuck waiting
until it happens in production again.  Will let you know.

^ permalink raw reply	[relevance 2%]

* Re: Weird Unicorn Timeout Issues (Hibernation problem?)
  @ 2014-08-05 14:46  2%             ` Tony Devlin
  0 siblings, 0 replies; 200+ results
From: Tony Devlin @ 2014-08-05 14:46 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public, Daniel Condomitti

I appreciate all your help Eric and Daniel.  I have not solved this yet,
but I think I have narrowed it down to a Firewall timeout issue.  One app
uses a database connection to Oracle, the other app uses a 3rd Party API
(still on location, but across the network).  The ping times to both of
these devices are extremely fast, however 30 minutes of inactivity across
the Firewall seems to disconnect these connections.  At least that appears
to be what the strace is telling me.  The place in the strace that the
timeout occurs is consistent, every time.  For example the strace of the
app that connects to Oracle shows this:

pid  7825] write(14,
"\0\373\0\0\6\0\0\0\0\0\21iB\376\377\377\377\377\377\377\377\1\0\0\0\0\0\0\0\v\0\0\0\3^Ca\201\0\0\0\0\0\0\376\377\377\377\377\377\377\377\22\0\0\0\376\377\377\377\377\377\377\377\r\0\0\0\376\377\377\377\377\377\377\377\376\377\377\377\377\377\377\377\0\0\0\0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\376\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\376\377\377\377\377\377\377\377\376\377\377\377\377\377\377\377\376\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\376\377\377\377\377\377\377\377\376\377\377\377\377\377\377\377\22select
1 from
dual\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
251) = 251
[pid  7825] read(14,  <unfinished ...>
[pid  7827] +++ killed by SIGKILL +++
PANIC: handle_group_exit: 7827 leader 7825
[pid  7846] +++ killed by SIGKILL +++
PANIC: handle_group_exit: 7846 leader 7825
+++ killed by SIGKILL +++

Clearly that is a database query 'select 1 from dual'.  It times out trying
to read the response.  At the same time if I watch the lsof -p <pid>, I see
that the database connection drops after 30 minutes.

I'll update this thread again once it is solved, for historical and future
issues (in case someone else experiences something similar).

Again thank you for your help Eric!


On Mon, Aug 4, 2014 at 4:46 PM, Eric Wong <e@80x24.org> wrote:

> Eric Wong <e@80x24.org> wrote:
> > Did you try strace-ing for 30 minutes and reproducing the error?
>
> You can also try setting the unicorn timeout to longer than 30
> minutes and get a longer/stalled strace.
>


^ permalink raw reply	[relevance 2%]

* Re: Weird Unicorn Timeout Issues (Hibernation problem?)
  2014-08-04 18:39  0% ` Eric Wong
  2014-08-04 18:41  0%   ` Kapil Israni
  2014-08-04 18:45  0%   ` Tony Devlin
@ 2014-08-04 18:55  0%   ` Daniel Condomitti
    2 siblings, 1 reply; 200+ results
From: Daniel Condomitti @ 2014-08-04 18:55 UTC (permalink / raw)
  To: Eric Wong; +Cc: Tony Devlin, unicorn-public

It could also be that your TCP keepalive interval is higher than your dat=
abase server=E2=80=99s connection timeout. I=E2=80=99ve run into that in =
the past. =20


On Monday, August 4, 2014 at 11:39 AM, Eric Wong wrote:

> Tony Devlin <tonydevlin=40gmail.com (mailto:tonydevlin=40gmail.com)> wr=
ote:
> > We have an issue where if a site is not accessed for around (average)=
 30
> > minutes the next query will timeout, and it will timeout on all the w=
orkers
> > opened. IE: If I have two workers, then both of those workers will
> > timeout, even if the first one,
> > after timeout, starts to work. As soon as the second worker is called=
 upon
> > it will timeout. Then everything runs perfectly good and great until =
the
> > site is not accessed for 30 minutes or more. Then the timeout issue s=
tarts
> > all over again.
> > =20
> =20
> =20
> This sounds like the idle timeout for MySQL (or similar) kicking in.
> What database(s) or other backends are you using=3F
> =20
> That said, we've had problems with hibernate/suspend in the past,
> so I'll double check.
> =20
> =20




^ permalink raw reply	[relevance 0%]

* Re: Weird Unicorn Timeout Issues (Hibernation problem?)
  2014-08-04 18:41  0%   ` Kapil Israni
@ 2014-08-04 18:48  2%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2014-08-04 18:48 UTC (permalink / raw)
  To: Kapil Israni; +Cc: unicorn-public

Kapil Israni <kapil.israni@gmail.com> wrote:
> I unsubscribed from this list few days back, still getting emails?

*checking logs*  You sent the unsubscribe request, but never responded
to the confirmation email.  If you lost the confirmation email, you
can resend the unsubscribe request again and make sure you respond to
the confirmation

^ permalink raw reply	[relevance 2%]

* Re: Weird Unicorn Timeout Issues (Hibernation problem?)
  2014-08-04 18:39  0% ` Eric Wong
  2014-08-04 18:41  0%   ` Kapil Israni
@ 2014-08-04 18:45  0%   ` Tony Devlin
  2014-08-04 18:55  0%   ` Daniel Condomitti
  2 siblings, 0 replies; 200+ results
From: Tony Devlin @ 2014-08-04 18:45 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Eric,

Thank you for responding.  We use a database on only one of the apps, it is
a Oracle 11G RAC Server.  I'll get the DBA to double check the idle timeout
for that DB.  Though the other app does not use a database.


On Mon, Aug 4, 2014 at 2:39 PM, Eric Wong <e@80x24.org> wrote:

> Tony Devlin <tonydevlin@gmail.com> wrote:
> > We have an issue where if a site is not accessed for around (average) 30
> > minutes the next query will timeout, and it will timeout on all the
> workers
> > opened.  IE:   If I have two workers, then both of those workers will
> > timeout, even if the first one,
> > after timeout, starts to work.  As soon as the second worker is called
> upon
> > it will timeout.  Then everything runs perfectly good and great until the
> > site is not accessed for 30 minutes or more.  Then the timeout issue
> starts
> > all over again.
>
> This sounds like the idle timeout for MySQL (or similar) kicking in.
> What database(s) or other backends are you using?
>
> That said, we've had problems with hibernate/suspend in the past,
> so I'll double check.
>



-- 

*Tony Devlin*
Founder / CTO
PrintKEG.com
(800) 676-0856
http://www.printkeg.com

Facebook: http://www.facebook.com/printkeg
Twitter: https://twitter.com/printkeg

250 business cards are only $10
http://www.printkeg.com/cheap-business-cards.php


^ permalink raw reply	[relevance 0%]

* Re: Weird Unicorn Timeout Issues (Hibernation problem?)
  2014-08-04 18:39  0% ` Eric Wong
@ 2014-08-04 18:41  0%   ` Kapil Israni
  2014-08-04 18:48  2%     ` Eric Wong
  2014-08-04 18:45  0%   ` Tony Devlin
  2014-08-04 18:55  0%   ` Daniel Condomitti
  2 siblings, 1 reply; 200+ results
From: Kapil Israni @ 2014-08-04 18:41 UTC (permalink / raw)
  To: unicorn-public

I unsubscribed from this list few days back, still getting emails?


On Mon, Aug 4, 2014 at 11:39 AM, Eric Wong <e@80x24.org> wrote:

> Tony Devlin <tonydevlin@gmail.com> wrote:
> > We have an issue where if a site is not accessed for around (average) 30
> > minutes the next query will timeout, and it will timeout on all the
> workers
> > opened.  IE:   If I have two workers, then both of those workers will
> > timeout, even if the first one,
> > after timeout, starts to work.  As soon as the second worker is called
> upon
> > it will timeout.  Then everything runs perfectly good and great until the
> > site is not accessed for 30 minutes or more.  Then the timeout issue
> starts
> > all over again.
>
> This sounds like the idle timeout for MySQL (or similar) kicking in.
> What database(s) or other backends are you using?
>
> That said, we've had problems with hibernate/suspend in the past,
> so I'll double check.
>
>


-- 
Kapil


^ permalink raw reply	[relevance 0%]

* Re: Weird Unicorn Timeout Issues (Hibernation problem?)
  2014-08-04 18:12  1% Weird Unicorn Timeout Issues (Hibernation problem?) Tony Devlin
@ 2014-08-04 18:39  0% ` Eric Wong
  2014-08-04 18:41  0%   ` Kapil Israni
                     ` (2 more replies)
  0 siblings, 3 replies; 200+ results
From: Eric Wong @ 2014-08-04 18:39 UTC (permalink / raw)
  To: Tony Devlin; +Cc: unicorn-public

Tony Devlin <tonydevlin@gmail.com> wrote:
> We have an issue where if a site is not accessed for around (average) 30
> minutes the next query will timeout, and it will timeout on all the workers
> opened.  IE:   If I have two workers, then both of those workers will
> timeout, even if the first one,
> after timeout, starts to work.  As soon as the second worker is called upon
> it will timeout.  Then everything runs perfectly good and great until the
> site is not accessed for 30 minutes or more.  Then the timeout issue starts
> all over again.

This sounds like the idle timeout for MySQL (or similar) kicking in.
What database(s) or other backends are you using?

That said, we've had problems with hibernate/suspend in the past,
so I'll double check.

^ permalink raw reply	[relevance 0%]

* Weird Unicorn Timeout Issues (Hibernation problem?)
@ 2014-08-04 18:12  1% Tony Devlin
  2014-08-04 18:39  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Tony Devlin @ 2014-08-04 18:12 UTC (permalink / raw)
  To: unicorn-public

Current Setup:  (the problem existed before updating nginx, unicorn and
rails; but in an attempt to solve the problem I updated them).

CentOS 6.3 (x64)
Ruby 2.1.2p95
Rails 4.0.0
Nginx 1.6.0
Unicorn 4.8.3

====

We have an issue where if a site is not accessed for around (average) 30
minutes the next query will timeout, and it will timeout on all the workers
opened.  IE:   If I have two workers, then both of those workers will
timeout, even if the first one,
after timeout, starts to work.  As soon as the second worker is called upon
it will timeout.  Then everything runs perfectly good and great until the
site is not accessed for 30 minutes or more.  Then the timeout issue starts
all over again.

This happens on our dev machine on two different applications (only apps on
the server) and on our production machine (also running two apps).

I can't figure out exactly what is going on.  I tried strace the master
unicorn process, but I get no relevant information.   One of the
applications is also tied to a New Relic account and it gives no
information either.

Nginx logs show this: (sorry, obfuscated certain details due to the
internal enterprise nature)

2014/08/04 11:31:21 [error] 6353#0: *339 upstream prematurely closed
connection while reading response header from upstream, client: *.*.*.*,
server: ****.org, request: "GET /assets/application.css HTTP/1.1",
upstream:
"http://unix:/var/www/sites/****/shared/sockets/.unicorn.sock.0:/assets/application.css",
host: "****.org", referrer: "http://****.org/outages"

2014/08/04 11:31:53 [error] 31058#0: *1 upstream prematurely closed
connection while reading response header from upstream, client: *.*.*.*,
server: ****.org, request: "GET /outages HTTP/1.1", upstream:
"http://unix:/var/www/sites/****/shared/sockets/.unicorn.sock.0:/outages",
host: "****.org", referrer: "http://****.org/outages"

Unicorn stderr shows this: (sorry, obfuscated certain details due to the
internal enterprise nature)

E, [2014-08-04T11:31:21.620379 #11991] ERROR -- : worker=1 PID:29701
timeout (21s > 20s), killing
E, [2014-08-04T11:31:21.630521 #11991] ERROR -- : reaped #<Process::Status:
pid 29701 SIGKILL (signal 9)> worker=1
I, [2014-08-04T11:31:21.639881 #30521]  INFO -- : worker=1 ready

E, [2014-08-04T11:31:53.666300 #11991] ERROR -- : worker=0 PID:29705
timeout (21s > 20s), killing
E, [2014-08-04T11:31:53.676984 #11991] ERROR -- : reaped #<Process::Status:
pid 29705 SIGKILL (signal 9)> worker=0
I, [2014-08-04T11:31:53.687157 #30528]  INFO -- : worker=0 ready

Its interesting that the timeout is occurring on different stages, for
example if you look at the two nginx errors, one timeout at
/assets/application.css and the other at the root /outages/, I've had it be
small gifs as well.   I'd also like to point at
that I have changed the timeout from 30s to 60s to 300s to 20s and it
occurs at every interval.  So it is not a "true" timeout issue.    Also the
two apps on the server are completely different, with only Ruby and Rails
being the same process.  The
gems are different in both, the database is different in both, in fact one
of them has no assets, no css and no ui files at all.  The only
similarities are Ruby, Rails, Nginx and Unicorn.

It's like there is some sort of hibernation problem with the unicorn
workers.  The hibernation thought comes from these debug messages (I
dropped to recording debug messages trying to locate the problem).  I get
these every now and then:

D, [2014-08-03T22:14:49.776538 #11991] DEBUG -- : waiting 11.0s after
suspend/hibernation

But then they cease to occur for days, example is that message was the last
time that occurred (non in the logs for today).

I've been trying for the past 4 days to solve this, make a large number of
changes to no avail.  Any help would be GREATLY appreciated.


^ permalink raw reply	[relevance 1%]

* Re: Please move to github
  2014-08-02 19:07  1%   ` Gary Grossman
@ 2014-08-02 20:15  0%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2014-08-02 20:15 UTC (permalink / raw)
  To: Gary Grossman; +Cc: unicorn-public, michael

Gary Grossman <gary.grossman@gmail.com> wrote:
> We'd pretty much need to introduce some kind of configuration
> switch, at least for the short term and maybe for the long term.
> The hope would be that it could become the default setting.
> Apps that don't use UTF8 should be able to set their desired default
> external encoding appropriately.

If possible, I would like to avoid an option and rely on
Encoding.default_external in a new major version.  Too many ways to set
the same thing is confusing and requires extra documentation overhead.

> >The rack-devel mailing list had a discussion on this in September 2010
> >and a decision was never reached. You can search the archives at:
> >http://groups.google.com/group/rack-devel
> 
> I came across this thread but didn't realize that was the last word
> so far when it came to Rack and encodings.
> 
> This might be one of those instances where it would be helpful for
> implementation to lead specification. Unicorn is one of the leading
> servers of its genre, if not the leader. If you supported a switch
> that made the encoding regime more sane, I think other popular servers
> like Thin and Passenger would swiftly follow and it might re-energize
> the discussion about getting encodings into the Rack spec once and
> for all, and give a base for experimentation and iteration for
> getting the encodings in the spec right.

I might start with WEBrick (or the Rack/WEBrick handler).  WEBrick is
distributed with Ruby and maintained by the core team.  It's not used in
production much, but it the reference implementation which is usable
from all Ruby implementations.

naruse (from that rack-devel thread) is also active in Ruby core and
is very knowledgeable in these areas.

> Thanks again for reviewing the patch. I'll work on a new patch that
> incorporates your comments and has a switch for enabling/disabling
> the functionality, and I'll try to follow roughly what the spec
> group in 2010 thought would make sense in terms of encodings for
> the various strings in the env. And I'll see if I can ask the
> Rack folks to chime in.

Definitely get other Rack folks to chime in, even if it is a
unicorn-only change.  This has been a problem for years already,
so taking more time to get things right won't hurt.

^ permalink raw reply	[relevance 0%]

* Re: Please move to github
  @ 2014-08-02 19:07  1%   ` Gary Grossman
  2014-08-02 20:15  0%     ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Gary Grossman @ 2014-08-02 19:07 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public, michael

Hi Eric,

Thanks for your reply and for reviewing the patch!

>Right, the Rack spec does not dictate this.  Doing this out-of-spec has
>the ability to break existing apps as well as compatibility with other
>app servers.

It's true, my patch is too naive since it's a pretty drastic change
in behavior not behind any kind of switch.

>What do other app servers do?

I did a little survey. ASCII-8BIT is kind of the de facto standard
even if it's not mandated by the Rack specification. Phusion
Passenger, Thin and WEBrick all send mostly ASCII-8BIT strings in
the env.

>My main concern is having more different behavior between various Rack
>servers servers, making it harder to switch between them.

Very valid; Rack wouldn't be much of a standard if there were a bunch
of variants in use.

>Another concern is breaking apps which are already working around this
>but work with non-UTF-8 encodings.

We'd pretty much need to introduce some kind of configuration
switch, at least for the short term and maybe for the long term.
The hope would be that it could become the default setting.
Apps that don't use UTF8 should be able to set their desired default
external encoding appropriately.

>The rack-devel mailing list had a discussion on this in September 2010
>and a decision was never reached. You can search the archives at:
>http://groups.google.com/group/rack-devel

I came across this thread but didn't realize that was the last word
so far when it came to Rack and encodings.

This might be one of those instances where it would be helpful for
implementation to lead specification. Unicorn is one of the leading
servers of its genre, if not the leader. If you supported a switch
that made the encoding regime more sane, I think other popular servers
like Thin and Passenger would swiftly follow and it might re-energize
the discussion about getting encodings into the Rack spec once and
for all, and give a base for experimentation and iteration for
getting the encodings in the spec right.

There's a lot of developer pain here. Many apps probably are serving
up encoding-related 500 errors without knowing it. There are
stories of developers adding "# encoding" everywhere, setting
the external/internal encoding, and then "things are fine until it
blows up somewhere else." I heard recently that a very large company
has stuck with Ruby 1.8.7, probably to avoid these encoding issues
among other things. It would be nice to improve the situation.

>Disclaimer: I am not an encoding expert, so for that reason I prefer
>to let other Rack folks make the decision.

I'm not an encoding expert either! Most people aren't... which is
why it'd be nice if they didn't have to know so much about it when
they write a Rack app!

>Do you have performance measurements for doing this as pure-Ruby
>middleware vs your patch?

I don't have measurements currently but I'll get some.
Our app is several years old and so there's a lot of stuff in
request.env by the time we get around to forcing everything to
UTF8 encoding. I wouldn't be surprised if the hit on
every single request is small but significant for us.

>So it should be best if there were a way to do this for all Rack
>servers.

Thanks again for reviewing the patch. I'll work on a new patch that
incorporates your comments and has a switch for enabling/disabling
the functionality, and I'll try to follow roughly what the spec
group in 2010 thought would make sense in terms of encodings for
the various strings in the env. And I'll see if I can ask the
Rack folks to chime in.

Gary


^ permalink raw reply	[relevance 1%]

* Re: Old unicorn workers still receives requests?
  2014-07-24 17:04  2% ` Eric Wong
@ 2014-07-24 17:22  0%   ` Bráulio Bhavamitra
  0 siblings, 0 replies; 200+ results
From: Bráulio Bhavamitra @ 2014-07-24 17:22 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Thanks Eric! It is not clear to me if old workers have a lower priority to
pull requets than new workers.

cheers,
br=C3=A1ulio


On Thu, Jul 24, 2014 at 2:04 PM, Eric Wong <e@80x24.org> wrote:

> Br=C3=A1ulio Bhavamitra <braulio@eita.org.br> wrote:
> > Hello all,
> >
> > I would like to know if old unicorn still receives and process requests
> in
> > a QUIT signal is sent to the old master.
>
> During heavy traffic, yes, there's a chance due to the optimization
> in worker_loop (lib/unicorn/http_server.rb):
>
>       # 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 =3D=3D 0
>         tmp =3D ready.dup
>         redo
>       end
>
> Note, "receives" requests isn't correct, workers "pull" requests from
> a central queue in the kernel.
>



--=20
"Lute pela sua ideologia. Seja um com sua ideologia. Viva pela sua
ideologia. Morra por sua ideologia" P.R. Sarkar

EITA - Educa=C3=A7=C3=A3o, Informa=C3=A7=C3=A3o e Tecnologias para Autogest=
=C3=A3o
http://cirandas.net/brauliobo
http://eita.org.br

"Paramapurusha =C3=A9 meu pai e Parama Prakriti =C3=A9 minha m=C3=A3e. O un=
iverso =C3=A9 meu
lar e todos n=C3=B3s somos cidad=C3=A3os deste cosmo. Este universo =C3=A9 =
a imagina=C3=A7=C3=A3o da
Mente Macroc=C3=B3smica, e todas as entidades est=C3=A3o sendo criadas, pre=
servadas e
destru=C3=ADdas nas fases de extrovers=C3=A3o e introvers=C3=A3o do fluxo i=
maginativo
c=C3=B3smico. No =C3=A2mbito pessoal, quando uma pessoa imagina algo em sua=
 mente,
naquele momento, essa pessoa =C3=A9 a =C3=BAnica propriet=C3=A1ria daquilo =
que ela
imagina, e ningu=C3=A9m mais. Quando um ser humano criado mentalmente camin=
ha
por um milharal tamb=C3=A9m imaginado, a pessoa imaginada n=C3=A3o =C3=A9 a=
 propriedade
desse milharal, pois ele pertence ao indiv=C3=ADduo que o est=C3=A1 imagina=
ndo. Este
universo foi criado na imagina=C3=A7=C3=A3o de Brahma, a Entidade Suprema, =
por isso
a propriedade deste universo =C3=A9 de Brahma, e n=C3=A3o dos microcosmos q=
ue tamb=C3=A9m
foram criados pela imagina=C3=A7=C3=A3o de Brahma. Nenhuma propriedade dest=
e mundo,
mut=C3=A1vel ou imut=C3=A1vel, pertence a um indiv=C3=ADduo em particular; =
tudo =C3=A9 o
patrim=C3=B4nio comum de todos."
Restante do texto em
http://cirandas.net/brauliobo/blog/a-problematica-de-hoje-em-dia


^ permalink raw reply	[relevance 0%]

* Re: Old unicorn workers still receives requests?
  @ 2014-07-24 17:04  2% ` Eric Wong
  2014-07-24 17:22  0%   ` Bráulio Bhavamitra
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2014-07-24 17:04 UTC (permalink / raw)
  To: Bráulio Bhavamitra; +Cc: unicorn-public

Bráulio Bhavamitra <braulio@eita.org.br> wrote:
> Hello all,
> 
> I would like to know if old unicorn still receives and process requests in
> a QUIT signal is sent to the old master.

During heavy traffic, yes, there's a chance due to the optimization
in worker_loop (lib/unicorn/http_server.rb):

      # 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
        tmp = ready.dup
        redo
      end

Note, "receives" requests isn't correct, workers "pull" requests from
a central queue in the kernel.

^ permalink raw reply	[relevance 2%]

* Re: Log reopening broken on Rails 4 with config.autoflush_log = false?
  @ 2014-07-04  9:13  2%   ` Cedric Maion
  0 siblings, 0 replies; 200+ results
From: Cedric Maion @ 2014-07-04  9:13 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn-public

Hi Eric,

Thanks for your answer (and update to the FAQ ;) ).

I'm playing with autoflush_log = true and see if I find a performance
impact (I'm logging approx 20KB per sec).

sys.vm.dirty* sysctls are already changed, but with low ratios as those
systems are also writing large files (and I don't want those pages in
memory for too long).

If a performance impact can be measured, I believe that I will turn
autoflush_log to false again and modify Unicorn::Util#is_log? to accept
!fp.sync ;) (I'm willing to accept occasional log lines corruption esp
during rotation)... or just restart unicorn after rotation instead.

Or, thinking out loud... keep autoflush_log to false and have unicorn
flush logs itself regularly, outside the HTTP request path (and when
reopening logs), to avoid blocking on IO during a log flush in the
middle of a request? (I need to refresh myself on the sizes of the ruby
& system IO buffers though, not sure if it's worth the pain).

    Cedric

On Thu, Jul 03, 2014 at 05:24:10PM +0000, Eric Wong wrote:
>
> Cedric Maion <cedric@maion.com> wrote:
> > On a Rails 4.0 app with 'config.autoflush_log = false', it seems to me
> > that telling unicorn to reopen log files fails (e.g., after a log
> > rotation and USR1 signal, Rails/unicorn still writes to the old rotated
> > file instead of reopening a new one).
> >
> > Without this config option, logs does properly get reopened.
> >
> > Is this something known?
>
> Yes, log buffering via File#sync=true is incompatible with reopening in
> multiprocess servers.  File#sync=true does not guarantee writes are
> atomic on line boundaries, so it's dangerous to assume they're logs.
>
> Even without reopening, enabling this option on a multiprocess server
> might corrupt logs (depending on the buffering implementation)
> because the synchronization is within each process rather for the
> entire OS.
>
> Looking at the rails source:
> ==> railties/lib/rails/application/bootstrap.rb <==
>           f = File.open path, 'a'
>           f.binmode
>           f.sync = config.autoflush_log # if true make sure every write flushes
>
> You're not likely to notice any performance difference unless you're
> logging excessively.  Keep in mind the Linux kernel also does buffering
> and you can tune that via the sys.vm.dirty_* sysctls.

^ permalink raw reply	[relevance 2%]

* Re: [ANN] unicorn 4.8.3 - the end of an era
  @ 2014-05-07 21:25  3%       ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2014-05-07 21:25 UTC (permalink / raw)
  To: Michael Fischer
  Cc: Eric Wong, Jérémy Lecour, Liste Unicorn, unicorn-public

Michael Fischer <mfischer@zendesk.com> wrote:
> Is there some compelling reason why the mailing list simply cannot be
> moved to another provider?  IMHO your users and fellow developers
> shouldn't have to do anything other than change the submission
> address.

We would need to migrate again if/when that provider goes dead or
service starts deteriorating.  Ease-of-migration and being forkable
again in the future was the top priority.

If I'm hit by a bus or start allowing too much spam, it should be
trivially easy to migrate the project[1] and all its archives and
infrastructure.

> I respect your desire to power the communication platform with free
> software (and I'm sure this can still be done with Mailman or
> whatever), but keep in mind the practical reality of our time, where
> most of us these days are now comfortably using Webmail or or POP/IMAP
> against a remote server that's not under the user's control and have
> no desire to implement yet another communication conduit.

I will probably take the addresses of active subscribers who've posted
here[2] imported into the new delivery system, even.

It would be great to be able to make the list of ML subscribers public,
too, to ensure forkability.  I'm not sure how the lurkers will react to
that, though...


[1] of course, whoever takes over may not be a Free Software zealot
    like myself.
[2] those addresses are already public, but lurkers will probably
    have to resubscribe (or use ssoma or the Atom feed).

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] tests: switch to minitest
  @ 2014-04-26  6:07  2% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2014-04-26  6:07 UTC (permalink / raw)
  To: Ken Dreyer; +Cc: unicorn-public, mongrel-unicorn

Ken Dreyer <ktdreyer@ktdreyer.com> wrote:
> Ruby 1.9+ uses Minitest as the backend for Test::Unit. As of Minitest 5,
> the shim has lost some backwards compatibility. It is time to make the
> jump to minitest.
> 
> Adjust the unicorn test suite to support Minitest 5's syntax.

Thank you very much for taking a look at this.  I was going to do it
myself over the summer.

I ran into some problems with our tests forking + minitest/autorun
causing some problems due to at_exit usage.

We probably need to workaround, I already did something for yahns
which we can reuse[1] in unicorn.

> Minitest versions 4 and below do not support the newer Minitest::Test
> class that arrived in version 5. For that case, use the
> MiniTest::Unit::TestCase class as a fallback.

For yahns, I did the following:
----------- git://yhbt.net/yahns -- test/helper.rb ------------------
gem 'minitest'
begin # favor minitest 5
  require 'minitest'
  Testcase = Minitest::Test
  mtobj = Minitest
rescue NameError, LoadError # but support minitest 4
  require 'minitest/unit'
  Testcase = Minitest::Unit::TestCase
  mtobj = MiniTest::Unit.new
end

# Not using minitest/autorun because that doesn't guard against redundant
# extra runs with fork.  We cannot use exit! in the tests either
# (since users/apps hosted on yahns _should_ expect exit, not exit!).
TSTART_PID = $$
at_exit do
  # skipping @@after_run stuff in minitest since we don't need it
  case $!
  when nil, SystemExit
    exit(mtobj.run(ARGV)) if $$ == TSTART_PID
  end
end
-------------------------------- 8< -------------------------------
I'd appreciate a second opinion on what I did with yahns above,
but it should work with unicorn.

Can you take a look at integrating+fixing those cases which fork?
Thanks again.

> Please keep me in the CC as I'm not subscribed to the unicorn email list.

No problem.  Also keeping unicorn-public@bogomips.org Cc-ed since that's
the new public-inbox[2] address and reply-all will be the norm there.
Hopefully public-inbox can encourage more drive-by contributors like
you :)


[1] - Fwiw, I give myself permission to relicense any yahns GPLv3+
      test code I wrote to (GPLv2+ || Ruby 1.8 license) for unicorn.
[2] - http://public-inbox.org/

^ permalink raw reply	[relevance 2%]

* Re: Stucks DB connections
  2014-02-24 12:33  2% Stucks DB connections Ilya Bazylchuk
@ 2014-02-24 18:25  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2014-02-24 18:25 UTC (permalink / raw)
  To: unicorn list; +Cc: Ilya Bazylchuk

Ilya Bazylchuk <ilya.bazylchuk@gmail.com> wrote:
> Hey Guys,
> 
> We have problems with DB connection in unicorn. When workers work some
> hours we see in Postgres activity stucks connections with query
> "COMMIT". After restart unicorn it work fine some time, then
> connections stucks again.
> 
> database.yml
> 
> adapter: postgis
> postgis_extension: true
> schema_search_path: public,postgis
> encoding: utf8
> pool: 5
> checkout_timeout: 10
> 
> unicorn 4.8.2
> pg 0.17.1
> rails 3.2.17

I'm not familiar with postgis.  Is there any sort of idle timeout on the
database side?  Is this high or low traffic when things get stuck?

I think the postgis/pg adapter guys might be able to help, too, since
unicorn doesn't do anything with the DB connections itself.

Some generic, DB-independent thoughts/questions:

Sometimes, tiny socket buffer sizes with the DB connection might hit this.
Any non-standard kernel/socket knobs or tuning?

Also, if running Linux, which kernel version are you running?  If it's
3.7, make sure commit 8fb74b9fb2b182d54beee592350d9ea1f325917a
(mm: compaction: partially revert capture of suitable high-order page)
got backported.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Stucks DB connections
@ 2014-02-24 12:33  2% Ilya Bazylchuk
  2014-02-24 18:25  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Ilya Bazylchuk @ 2014-02-24 12:33 UTC (permalink / raw)
  To: mongrel-unicorn

Hey Guys,

We have problems with DB connection in unicorn. When workers work some hours we see in Postgres activity stucks connections with query "COMMIT". After restart unicorn it work fine some time, then connections stucks again.

database.yml

adapter: postgis
postgis_extension: true
schema_search_path: public,postgis
encoding: utf8
pool: 5
checkout_timeout: 10

unicorn 4.8.2
pg 0.17.1
rails 3.2.17

unicorn use with nginx

Can you help with something?
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Different behavior with pid files and SIGUSR2
  2014-01-28 20:03  2% ` Eric Wong
@ 2014-01-28 20:08  0%   ` Michael Graff
  0 siblings, 0 replies; 200+ results
From: Michael Graff @ 2014-01-28 20:08 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn list

Good deal.  This was breaking my restart script in a non-obvious way.  Granted, my restart script should be smarter...

What is the preferred way to start-on-server-reboot and upgrade-in-place these days?  I have an older linux-expecting init.d-style script I've been using for years, so it's likely time to consider alternatives.

--Michael



On Jan 28, 2014, at 2:03 PM, Eric Wong <normalperson@yhbt.net> wrote:

> Michael Graff <explorer@flame.org> wrote:
>> What I appear to be seeing now, with 4.7.0, is:
>> 
>> (1)  The PID file is removed.
>> (2)  The new instance spins up, but until it starts its first worker, no .pid file is created.
>> 
>> I don't mind the new behavior particularly, but it did surprise me a bit.  Is this new behavior working as intended?
> 
> Yes in 4.7.0, but Jimmy convinced me to change it so the PID is written
> early again in 4.8.0 (also pushing out 4.8.1 soon)
> 
> ref: http://mid.gmane.org/CAHStS5gFYcPBDxkVizAHrOeDKAkjT69kruFdgaY0CbB+vLbK8Q@mail.gmail.com
> 

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: Different behavior with pid files and SIGUSR2
  @ 2014-01-28 20:03  2% ` Eric Wong
  2014-01-28 20:08  0%   ` Michael Graff
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2014-01-28 20:03 UTC (permalink / raw)
  To: unicorn list; +Cc: Michael Graff

Michael Graff <explorer@flame.org> wrote:
> What I appear to be seeing now, with 4.7.0, is:
> 
> (1)  The PID file is removed.
> (2)  The new instance spins up, but until it starts its first worker, no .pid file is created.
> 
> I don't mind the new behavior particularly, but it did surprise me a bit.  Is this new behavior working as intended?

Yes in 4.7.0, but Jimmy convinced me to change it so the PID is written
early again in 4.8.0 (also pushing out 4.8.1 soon)

ref: http://mid.gmane.org/CAHStS5gFYcPBDxkVizAHrOeDKAkjT69kruFdgaY0CbB+vLbK8Q@mail.gmail.com
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* [ANN] unicorn 4.8.0 - big internal changes, but compatible
@ 2014-01-11  7:40  2% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2014-01-11  7:40 UTC (permalink / raw)
  To: mongrel-unicorn

Changes:

This release contains fairly major internal workings of master-to-worker
notifications.  The master process no longer sends signals to workers
for most tasks.  This works around some compatibility issues with some
versions of the "pg" gem (and potentially any other code which may not
handle EINTR properly).  One extra benefit is it also helps stray
workers notice a rare, unexpected master death more easily.  Workers
continue to (and will always) accept existing signals for compatibility
with tools/scripts which may signal workers.

PID file are always written early (even on upgrade) again to avoid
breaking strange monitoring setups which use PID files.  Keep in mind we
have always discouraged monitoring based on PID files as they are
fragile.

We now avoid bubbling IOError to the Rack app on premature client
disconnects when streaming the input body.  This is usually not a
problem with nginx, but may be on some LAN setups without nginx).

Thanks to Sam Saffron, Jimmy Soho, Rodrigo Rosenfeld Rosas,
Michael Fischer, and Andrew Hobson for their help with this release.

Note: the unicorn mailing list will be moved/changed soon due to the
RubyForge shutdown.  unicorn will always rely only on Free Software.
There will never be any sign-up requirements nor terms-of-service to
agree to when communicating with us.

* http://unicorn.bogomips.org/
* mongrel-unicorn@rubyforge.org
* git://bogomips.org/unicorn.git
* http://unicorn.bogomips.org/NEWS.atom.xml

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: [PATCH] rework master-to-worker signaling to use a pipe
  2013-12-09  9:52  1% [PATCH] rework master-to-worker signaling to use a pipe Eric Wong
@ 2013-12-10  0:22  0% ` Sam Saffron
  0 siblings, 0 replies; 200+ results
From: Sam Saffron @ 2013-12-10  0:22 UTC (permalink / raw)
  To: unicorn list

Eric,

Your work on unicorn is exemplary and responsiveness second to none. I
did want to take a full circle back though on the issue.

After discussing with tmm1 and ko1 the conclusion is that pg is using
ubfs incorrectly. They are just there to unblock io, not as a
termination hook, once io is unblocked you still need to be able to
re-acquire. pg was using ubfs as termination hooks. See full
discussion at: https://groups.google.com/forum/#!topic/ruby-pg/5_ylGmog1S4

The end result is that I suggested Lars open a ticket for redmine if
we need a ubfs to act as termination hooks, the change to pg was
reverted.

Bottom line is that your change is not really required.

Thanks you so much for being super responsive here. Sorry if I caused
you to go on a tangent you did need to go on.

Sam

On Mon, Dec 9, 2013 at 8:52 PM, Eric Wong <normalperson@yhbt.net> wrote:
> Signaling using normal kill(2) is preserved, but the master now
> prefers to signal workers using a pipe rather than kill(2).
> Non-graceful signals (:TERM/:KILL) are still sent using kill(2),
> as they ask for immediate shutdown.
>
> This change is necessary to avoid triggering the ubf (unblocking
> function) for rb_thread_call_without_gvl (and similar) functions
> extensions.  Most notably, this fixes compatibility with newer
> versions of the 'pg' gem which will cancel a running DB query if
> signaled[1].
>
> This also has the nice side-effect of allowing a premature
> master death (assuming preload_app didn't cause the master to
> spawn off rogue child daemons).
>
> Note: users should also refrain from using "killall" if using the
> 'pg' gem or something like it.
>
> Unfortunately, this increases FD usage in the master as the writable
> end of the pipe is preserved in the master.  This limit the number
> of worker processes the master may run to the open file limit of the
> master process.  Increasing the open file limit of the master
> process may be needed.  However, the FD use on the workers is
> reduced by one as the internal self-pipe is no longer used.  Thus,
> overall pipe allocation for the kernel remains unchanged.
>
> [1] - pg is correct to cancel a query, as it cannot know if
>       the signal was for a) graceful unicorn shutdown or
>       b) oh-noes-I-started-a-bad-query-ABORT-ABORT-ABORT!!
> ---
>   Pushed to master on git://bogomips.org/unicorn.git
>   commit 6f6e4115b4bb03e5e7c55def91527799190566f2
>
>  SIGNALS                    |  6 ++++
>  lib/unicorn.rb             |  5 +++
>  lib/unicorn/http_server.rb | 85 +++++++++++++++++++++++-----------------------
>  lib/unicorn/worker.rb      | 64 ++++++++++++++++++++++++++++++++++
>  4 files changed, 118 insertions(+), 42 deletions(-)
>
> diff --git a/SIGNALS b/SIGNALS
> index 48c651e..ef0b0d9 100644
> --- a/SIGNALS
> +++ b/SIGNALS
> @@ -45,6 +45,10 @@ http://unicorn.bogomips.org/examples/init.sh
>
>  === Worker Processes
>
> +Note: as of unicorn 4.8, the master uses a pipe to signal workers
> +instead of kill(2) for most cases.  Using signals still (and works and
> +remains supported for external tools/libraries), however.
> +
>  Sending signals directly to the worker processes should not normally be
>  needed.  If the master process is running, any exited worker will be
>  automatically respawned.
> @@ -52,6 +56,8 @@ automatically respawned.
>  * INT/TERM - Quick shutdown, immediately exit.
>    Unless WINCH has been sent to the master (or the master is killed),
>    the master process will respawn a worker to replace this one.
> +  Immediate shutdown is still triggered using kill(2) and not the
> +  internal pipe as of unicorn 4.8
>
>  * QUIT - Gracefully exit after finishing the current request.
>    Unless WINCH has been sent to the master (or the master is killed),
> diff --git a/lib/unicorn.rb b/lib/unicorn.rb
> index 2535159..638b846 100644
> --- a/lib/unicorn.rb
> +++ b/lib/unicorn.rb
> @@ -97,6 +97,11 @@ module Unicorn
>      logger.error "#{prefix}: #{message} (#{exc.class})"
>      exc.backtrace.each { |line| logger.error(line) }
>    end
> +
> +  # remove this when we only support Ruby >= 2.0
> +  def self.pipe # :nodoc:
> +    Kgio::Pipe.new.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
> +  end
>    # :startdoc:
>  end
>  # :enddoc:
> diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
> index f15c8a7..ae8ad13 100644
> --- a/lib/unicorn/http_server.rb
> +++ b/lib/unicorn/http_server.rb
> @@ -42,16 +42,8 @@ class Unicorn::HttpServer
>    # it to wake up the master from IO.select in exactly the same manner
>    # djb describes in http://cr.yp.to/docs/selfpipe.html
>    #
> -  # * The workers immediately close the pipe they inherit from the
> -  # master and replace it with a new pipe after forking.  This new
> -  # pipe is also used to wakeup from IO.select from inside (worker)
> -  # signal handlers.  However, workers *close* the pipe descriptors in
> -  # the signal handlers to raise EBADF in IO.select instead of writing
> -  # like we do in the master.  We cannot easily use the reader set for
> -  # IO.select because LISTENERS is already that set, and it's extra
> -  # work (and cycles) to distinguish the pipe FD from the reader set
> -  # once IO.select returns.  So we're lazy and just close the pipe when
> -  # a (rare) signal arrives in the worker and reinitialize the pipe later.
> +  # * The workers immediately close the pipe they inherit.  See the
> +  # Unicorn::Worker class for the pipe workers use.
>    SELF_PIPE = []
>
>    # signal queue used for self-piping
> @@ -127,7 +119,7 @@ class Unicorn::HttpServer
>      inherit_listeners!
>      # this pipe is used to wake us up from select(2) in #join when signals
>      # are trapped.  See trap_deferred.
> -    init_self_pipe!
> +    SELF_PIPE.replace(Unicorn.pipe)
>
>      # setup signal handlers before writing pid file in case people get
>      # trigger happy and send signals as soon as the pid file exists.
> @@ -306,14 +298,14 @@ class Unicorn::HttpServer
>          logger.info "master reopening logs..."
>          Unicorn::Util.reopen_logs
>          logger.info "master done reopening logs"
> -        kill_each_worker(:USR1)
> +        soft_kill_each_worker(:USR1)
>        when :USR2 # exec binary, stay alive in case something went wrong
>          reexec
>        when :WINCH
>          if Unicorn::Configurator::RACKUP[:daemonized]
>            respawn = false
>            logger.info "gracefully stopping all workers"
> -          kill_each_worker(:QUIT)
> +          soft_kill_each_worker(:QUIT)
>            self.worker_processes = 0
>          else
>            logger.info "SIGWINCH ignored because we're not daemonized"
> @@ -345,7 +337,11 @@ class Unicorn::HttpServer
>      self.listeners = []
>      limit = Time.now + timeout
>      until WORKERS.empty? || Time.now > limit
> -      kill_each_worker(graceful ? :QUIT : :TERM)
> +      if graceful
> +        soft_kill_each_worker(:QUIT)
> +      else
> +        kill_each_worker(:TERM)
> +      end
>        sleep(0.1)
>        reap_all_workers
>      end
> @@ -498,6 +494,7 @@ class Unicorn::HttpServer
>    end
>
>    def after_fork_internal
> +    SELF_PIPE.each { |io| io.close }.clear # this is master-only, now
>      @ready_pipe.close if @ready_pipe
>      Unicorn::Configurator::RACKUP.clear
>      @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
> @@ -517,6 +514,7 @@ class Unicorn::HttpServer
>        before_fork.call(self, worker)
>        if pid = fork
>          WORKERS[pid] = worker
> +        worker.atfork_parent
>        else
>          after_fork_internal
>          worker_loop(worker)
> @@ -531,9 +529,7 @@ class Unicorn::HttpServer
>    def maintain_worker_count
>      (off = WORKERS.size - worker_processes) == 0 and return
>      off < 0 and return spawn_missing_workers
> -    WORKERS.dup.each_pair { |wpid,w|
> -      w.nr >= worker_processes and kill_worker(:QUIT, wpid) rescue nil
> -    }
> +    WORKERS.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
>    end
>
>    # if we get any error, try to write something back to the client
> @@ -600,6 +596,7 @@ class Unicorn::HttpServer
>    # traps for USR1, USR2, and HUP may be set in the after_fork Proc
>    # by the user.
>    def init_worker_process(worker)
> +    worker.atfork_child
>      # we'll re-trap :QUIT later for graceful shutdown iff we accept clients
>      EXIT_SIGS.each { |sig| trap(sig) { exit!(0) } }
>      exit!(0) if (SIG_QUEUE & EXIT_SIGS)[0]
> @@ -608,23 +605,27 @@ class Unicorn::HttpServer
>      SIG_QUEUE.clear
>      proc_name "worker[#{worker.nr}]"
>      START_CTX.clear
> -    init_self_pipe!
>      WORKERS.clear
> +
> +    after_fork.call(self, worker) # can drop perms and create listeners
>      LISTENERS.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
> -    after_fork.call(self, worker) # can drop perms
> +
>      worker.user(*user) if user.kind_of?(Array) && ! worker.switched
>      self.timeout /= 2.0 # halve it for select()
>      @config = nil
>      build_app! unless preload_app
>      ssl_enable!
>      @after_fork = @listener_opts = @orig_app = nil
> +    readers = LISTENERS.dup
> +    readers << worker
> +    trap(:QUIT) { readers.each { |io| io.close }.replace([false]) }
> +    readers
>    end
>
>    def reopen_worker_logs(worker_nr)
>      logger.info "worker=#{worker_nr} reopening logs..."
>      Unicorn::Util.reopen_logs
>      logger.info "worker=#{worker_nr} done reopening logs"
> -    init_self_pipe!
>      rescue => e
>        logger.error(e) rescue nil
>        exit!(77) # EX_NOPERM in sysexits.h
> @@ -635,22 +636,24 @@ class Unicorn::HttpServer
>    # given a INT, QUIT, or TERM signal)
>    def worker_loop(worker)
>      ppid = master_pid
> -    init_worker_process(worker)
> +    readers = init_worker_process(worker)
>      nr = 0 # this becomes negative if we need to reopen logs
> -    l = LISTENERS.dup
> -    ready = l.dup
>
> -    # closing anything we IO.select on will raise EBADF
> -    trap(:USR1) { nr = -65536; SELF_PIPE[0].close rescue nil }
> -    trap(:QUIT) { worker = nil; LISTENERS.each { |s| s.close rescue nil }.clear }
> -    logger.info "worker=#{worker.nr} ready"
> +    # this only works immediately if the master sent us the signal
> +    # (which is the normal case)
> +    trap(:USR1) { nr = -65536 }
> +
> +    ready = readers.dup
> +    @logger.info "worker=#{worker.nr} ready"
>
>      begin
>        nr < 0 and reopen_worker_logs(worker.nr)
>        nr = 0
> -
>        worker.tick = Time.now.to_i
> -      while sock = ready.shift
> +      tmp = ready.dup
> +      while sock = tmp.shift
> +        # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
> +        # but that will return false
>          if client = sock.kgio_tryaccept
>            process_client(client)
>            nr += 1
> @@ -663,8 +666,8 @@ class Unicorn::HttpServer
>        # 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
> +      unless nr == 0
> +        tmp = ready.dup
>          redo
>        end
>
> @@ -672,11 +675,11 @@ class Unicorn::HttpServer
>
>        # timeout used so we can detect parent death:
>        worker.tick = Time.now.to_i
> -      ret = IO.select(l, nil, SELF_PIPE, @timeout) and ready = ret[0]
> +      ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
>      rescue => e
> -      redo if nr < 0 && (Errno::EBADF === e || IOError === e) # reopen logs
> -      Unicorn.log_error(@logger, "listen loop error", e) if worker
> -    end while worker
> +      redo if nr < 0
> +      Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
> +    end while readers[0]
>    end
>
>    # delivers a signal to a worker and fails gracefully if the worker
> @@ -692,6 +695,10 @@ class Unicorn::HttpServer
>      WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
>    end
>
> +  def soft_kill_each_worker(signal)
> +    WORKERS.each_value { |worker| worker.soft_kill(signal) }
> +  end
> +
>    # unlinks a PID file at given +path+ if it contains the current PID
>    # still potentially racy without locking the directory (which is
>    # non-portable and may interact badly with other programs), but the
> @@ -720,7 +727,7 @@ class Unicorn::HttpServer
>      config[:listeners].replace(@init_listeners)
>      config.reload
>      config.commit!(self)
> -    kill_each_worker(:QUIT)
> +    soft_kill_each_worker(:QUIT)
>      Unicorn::Util.reopen_logs
>      self.app = orig_app
>      build_app! if preload_app
> @@ -756,12 +763,6 @@ class Unicorn::HttpServer
>      io.sync = true
>    end
>
> -  def init_self_pipe!
> -    SELF_PIPE.each { |io| io.close rescue nil }
> -    SELF_PIPE.replace(Kgio::Pipe.new)
> -    SELF_PIPE.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
> -  end
> -
>    def inherit_listeners!
>      # inherit sockets from parents, they need to be plain Socket objects
>      # before they become Kgio::UNIXServer or Kgio::TCPServer
> diff --git a/lib/unicorn/worker.rb b/lib/unicorn/worker.rb
> index 1fb6a4a..e74a1c9 100644
> --- a/lib/unicorn/worker.rb
> +++ b/lib/unicorn/worker.rb
> @@ -12,6 +12,7 @@ class Unicorn::Worker
>    # :stopdoc:
>    attr_accessor :nr, :switched
>    attr_writer :tmp
> +  attr_reader :to_io # IO.select-compatible
>
>    PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
>    DROPS = []
> @@ -23,6 +24,66 @@ class Unicorn::Worker
>      @raindrop[@offset] = 0
>      @nr = nr
>      @tmp = @switched = false
> +    @to_io, @master = Unicorn.pipe
> +  end
> +
> +  def atfork_child # :nodoc:
> +    # we _must_ close in child, parent just holds this open to signal
> +    @master = @master.close
> +  end
> +
> +  # master fakes SIGQUIT using this
> +  def quit # :nodoc:
> +    @master = @master.close if @master
> +  end
> +
> +  # parent does not read
> +  def atfork_parent # :nodoc:
> +    @to_io = @to_io.close
> +  end
> +
> +  # call a signal handler immediately without triggering EINTR
> +  # We do not use the more obvious Process.kill(sig, $$) here since
> +  # that signal delivery may be deferred.  We want to avoid signal delivery
> +  # while the Rack app.call is running because some database drivers
> +  # (e.g. ruby-pg) may cancel pending requests.
> +  def fake_sig(sig) # :nodoc:
> +    old_cb = trap(sig, "IGNORE")
> +    old_cb.call
> +  ensure
> +    trap(sig, old_cb)
> +  end
> +
> +  # master sends fake signals to children
> +  def soft_kill(sig) # :nodoc:
> +    case sig
> +    when Integer
> +      signum = sig
> +    else
> +      signum = Signal.list[sig.to_s] or
> +          raise ArgumentError, "BUG: bad signal: #{sig.inspect}"
> +    end
> +    # writing and reading 4 bytes on a pipe is atomic on all POSIX platforms
> +    # Do not care in the odd case the buffer is full, here.
> +    @master.kgio_trywrite([signum].pack('l'))
> +  rescue Errno::EPIPE
> +    # worker will be reaped soon
> +  end
> +
> +  # this only runs when the Rack app.call is not running
> +  # act like a listener
> +  def kgio_tryaccept # :nodoc:
> +    case buf = @to_io.kgio_tryread(4)
> +    when String
> +      # unpack the buffer and trigger the signal handler
> +      signum = buf.unpack('l')
> +      fake_sig(signum[0])
> +      # keep looping, more signals may be queued
> +    when nil # EOF: master died, but we are at a safe place to exit
> +      fake_sig(:QUIT)
> +    when :wait_readable # keep waiting
> +      return false
> +    end while true # loop, as multiple signals may be sent
>    end
>
>    # worker objects may be compared to just plain Integers
> @@ -49,8 +110,11 @@ class Unicorn::Worker
>      end
>    end
>
> +  # called in both the master (reaping worker) and worker (SIGQUIT handler)
>    def close # :nodoc:
>      @tmp.close if @tmp
> +    @master.close if @master
> +    @to_io.close if @to_io
>    end
>
>    # :startdoc:
> --
> Eric Wong
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* [PATCH] rework master-to-worker signaling to use a pipe
@ 2013-12-09  9:52  1% Eric Wong
  2013-12-10  0:22  0% ` Sam Saffron
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2013-12-09  9:52 UTC (permalink / raw)
  To: mongrel-unicorn

Signaling using normal kill(2) is preserved, but the master now
prefers to signal workers using a pipe rather than kill(2).
Non-graceful signals (:TERM/:KILL) are still sent using kill(2),
as they ask for immediate shutdown.

This change is necessary to avoid triggering the ubf (unblocking
function) for rb_thread_call_without_gvl (and similar) functions
extensions.  Most notably, this fixes compatibility with newer
versions of the 'pg' gem which will cancel a running DB query if
signaled[1].

This also has the nice side-effect of allowing a premature
master death (assuming preload_app didn't cause the master to
spawn off rogue child daemons).

Note: users should also refrain from using "killall" if using the
'pg' gem or something like it.

Unfortunately, this increases FD usage in the master as the writable
end of the pipe is preserved in the master.  This limit the number
of worker processes the master may run to the open file limit of the
master process.  Increasing the open file limit of the master
process may be needed.  However, the FD use on the workers is
reduced by one as the internal self-pipe is no longer used.  Thus,
overall pipe allocation for the kernel remains unchanged.

[1] - pg is correct to cancel a query, as it cannot know if
      the signal was for a) graceful unicorn shutdown or
      b) oh-noes-I-started-a-bad-query-ABORT-ABORT-ABORT!!
---
  Pushed to master on git://bogomips.org/unicorn.git
  commit 6f6e4115b4bb03e5e7c55def91527799190566f2

 SIGNALS                    |  6 ++++
 lib/unicorn.rb             |  5 +++
 lib/unicorn/http_server.rb | 85 +++++++++++++++++++++++-----------------------
 lib/unicorn/worker.rb      | 64 ++++++++++++++++++++++++++++++++++
 4 files changed, 118 insertions(+), 42 deletions(-)

diff --git a/SIGNALS b/SIGNALS
index 48c651e..ef0b0d9 100644
--- a/SIGNALS
+++ b/SIGNALS
@@ -45,6 +45,10 @@ http://unicorn.bogomips.org/examples/init.sh
 
 === Worker Processes
 
+Note: as of unicorn 4.8, the master uses a pipe to signal workers
+instead of kill(2) for most cases.  Using signals still (and works and
+remains supported for external tools/libraries), however.
+
 Sending signals directly to the worker processes should not normally be
 needed.  If the master process is running, any exited worker will be
 automatically respawned.
@@ -52,6 +56,8 @@ automatically respawned.
 * INT/TERM - Quick shutdown, immediately exit.
   Unless WINCH has been sent to the master (or the master is killed),
   the master process will respawn a worker to replace this one.
+  Immediate shutdown is still triggered using kill(2) and not the
+  internal pipe as of unicorn 4.8
 
 * QUIT - Gracefully exit after finishing the current request.
   Unless WINCH has been sent to the master (or the master is killed),
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index 2535159..638b846 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -97,6 +97,11 @@ module Unicorn
     logger.error "#{prefix}: #{message} (#{exc.class})"
     exc.backtrace.each { |line| logger.error(line) }
   end
+
+  # remove this when we only support Ruby >= 2.0
+  def self.pipe # :nodoc:
+    Kgio::Pipe.new.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
+  end
   # :startdoc:
 end
 # :enddoc:
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index f15c8a7..ae8ad13 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -42,16 +42,8 @@ class Unicorn::HttpServer
   # it to wake up the master from IO.select in exactly the same manner
   # djb describes in http://cr.yp.to/docs/selfpipe.html
   #
-  # * The workers immediately close the pipe they inherit from the
-  # master and replace it with a new pipe after forking.  This new
-  # pipe is also used to wakeup from IO.select from inside (worker)
-  # signal handlers.  However, workers *close* the pipe descriptors in
-  # the signal handlers to raise EBADF in IO.select instead of writing
-  # like we do in the master.  We cannot easily use the reader set for
-  # IO.select because LISTENERS is already that set, and it's extra
-  # work (and cycles) to distinguish the pipe FD from the reader set
-  # once IO.select returns.  So we're lazy and just close the pipe when
-  # a (rare) signal arrives in the worker and reinitialize the pipe later.
+  # * The workers immediately close the pipe they inherit.  See the
+  # Unicorn::Worker class for the pipe workers use.
   SELF_PIPE = []
 
   # signal queue used for self-piping
@@ -127,7 +119,7 @@ class Unicorn::HttpServer
     inherit_listeners!
     # this pipe is used to wake us up from select(2) in #join when signals
     # are trapped.  See trap_deferred.
-    init_self_pipe!
+    SELF_PIPE.replace(Unicorn.pipe)
 
     # setup signal handlers before writing pid file in case people get
     # trigger happy and send signals as soon as the pid file exists.
@@ -306,14 +298,14 @@ class Unicorn::HttpServer
         logger.info "master reopening logs..."
         Unicorn::Util.reopen_logs
         logger.info "master done reopening logs"
-        kill_each_worker(:USR1)
+        soft_kill_each_worker(:USR1)
       when :USR2 # exec binary, stay alive in case something went wrong
         reexec
       when :WINCH
         if Unicorn::Configurator::RACKUP[:daemonized]
           respawn = false
           logger.info "gracefully stopping all workers"
-          kill_each_worker(:QUIT)
+          soft_kill_each_worker(:QUIT)
           self.worker_processes = 0
         else
           logger.info "SIGWINCH ignored because we're not daemonized"
@@ -345,7 +337,11 @@ class Unicorn::HttpServer
     self.listeners = []
     limit = Time.now + timeout
     until WORKERS.empty? || Time.now > limit
-      kill_each_worker(graceful ? :QUIT : :TERM)
+      if graceful
+        soft_kill_each_worker(:QUIT)
+      else
+        kill_each_worker(:TERM)
+      end
       sleep(0.1)
       reap_all_workers
     end
@@ -498,6 +494,7 @@ class Unicorn::HttpServer
   end
 
   def after_fork_internal
+    SELF_PIPE.each { |io| io.close }.clear # this is master-only, now
     @ready_pipe.close if @ready_pipe
     Unicorn::Configurator::RACKUP.clear
     @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
@@ -517,6 +514,7 @@ class Unicorn::HttpServer
       before_fork.call(self, worker)
       if pid = fork
         WORKERS[pid] = worker
+        worker.atfork_parent
       else
         after_fork_internal
         worker_loop(worker)
@@ -531,9 +529,7 @@ class Unicorn::HttpServer
   def maintain_worker_count
     (off = WORKERS.size - worker_processes) == 0 and return
     off < 0 and return spawn_missing_workers
-    WORKERS.dup.each_pair { |wpid,w|
-      w.nr >= worker_processes and kill_worker(:QUIT, wpid) rescue nil
-    }
+    WORKERS.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
   end
 
   # if we get any error, try to write something back to the client
@@ -600,6 +596,7 @@ class Unicorn::HttpServer
   # traps for USR1, USR2, and HUP may be set in the after_fork Proc
   # by the user.
   def init_worker_process(worker)
+    worker.atfork_child
     # we'll re-trap :QUIT later for graceful shutdown iff we accept clients
     EXIT_SIGS.each { |sig| trap(sig) { exit!(0) } }
     exit!(0) if (SIG_QUEUE & EXIT_SIGS)[0]
@@ -608,23 +605,27 @@ class Unicorn::HttpServer
     SIG_QUEUE.clear
     proc_name "worker[#{worker.nr}]"
     START_CTX.clear
-    init_self_pipe!
     WORKERS.clear
+
+    after_fork.call(self, worker) # can drop perms and create listeners
     LISTENERS.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
-    after_fork.call(self, worker) # can drop perms
+
     worker.user(*user) if user.kind_of?(Array) && ! worker.switched
     self.timeout /= 2.0 # halve it for select()
     @config = nil
     build_app! unless preload_app
     ssl_enable!
     @after_fork = @listener_opts = @orig_app = nil
+    readers = LISTENERS.dup
+    readers << worker
+    trap(:QUIT) { readers.each { |io| io.close }.replace([false]) }
+    readers
   end
 
   def reopen_worker_logs(worker_nr)
     logger.info "worker=#{worker_nr} reopening logs..."
     Unicorn::Util.reopen_logs
     logger.info "worker=#{worker_nr} done reopening logs"
-    init_self_pipe!
     rescue => e
       logger.error(e) rescue nil
       exit!(77) # EX_NOPERM in sysexits.h
@@ -635,22 +636,24 @@ class Unicorn::HttpServer
   # given a INT, QUIT, or TERM signal)
   def worker_loop(worker)
     ppid = master_pid
-    init_worker_process(worker)
+    readers = init_worker_process(worker)
     nr = 0 # this becomes negative if we need to reopen logs
-    l = LISTENERS.dup
-    ready = l.dup
 
-    # closing anything we IO.select on will raise EBADF
-    trap(:USR1) { nr = -65536; SELF_PIPE[0].close rescue nil }
-    trap(:QUIT) { worker = nil; LISTENERS.each { |s| s.close rescue nil }.clear }
-    logger.info "worker=#{worker.nr} ready"
+    # this only works immediately if the master sent us the signal
+    # (which is the normal case)
+    trap(:USR1) { nr = -65536 }
+
+    ready = readers.dup
+    @logger.info "worker=#{worker.nr} ready"
 
     begin
       nr < 0 and reopen_worker_logs(worker.nr)
       nr = 0
-
       worker.tick = Time.now.to_i
-      while sock = ready.shift
+      tmp = ready.dup
+      while sock = tmp.shift
+        # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
+        # but that will return false
         if client = sock.kgio_tryaccept
           process_client(client)
           nr += 1
@@ -663,8 +666,8 @@ class Unicorn::HttpServer
       # 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
+      unless nr == 0
+        tmp = ready.dup
         redo
       end
 
@@ -672,11 +675,11 @@ class Unicorn::HttpServer
 
       # timeout used so we can detect parent death:
       worker.tick = Time.now.to_i
-      ret = IO.select(l, nil, SELF_PIPE, @timeout) and ready = ret[0]
+      ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
     rescue => e
-      redo if nr < 0 && (Errno::EBADF === e || IOError === e) # reopen logs
-      Unicorn.log_error(@logger, "listen loop error", e) if worker
-    end while worker
+      redo if nr < 0
+      Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
+    end while readers[0]
   end
 
   # delivers a signal to a worker and fails gracefully if the worker
@@ -692,6 +695,10 @@ class Unicorn::HttpServer
     WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
   end
 
+  def soft_kill_each_worker(signal)
+    WORKERS.each_value { |worker| worker.soft_kill(signal) }
+  end
+
   # unlinks a PID file at given +path+ if it contains the current PID
   # still potentially racy without locking the directory (which is
   # non-portable and may interact badly with other programs), but the
@@ -720,7 +727,7 @@ class Unicorn::HttpServer
     config[:listeners].replace(@init_listeners)
     config.reload
     config.commit!(self)
-    kill_each_worker(:QUIT)
+    soft_kill_each_worker(:QUIT)
     Unicorn::Util.reopen_logs
     self.app = orig_app
     build_app! if preload_app
@@ -756,12 +763,6 @@ class Unicorn::HttpServer
     io.sync = true
   end
 
-  def init_self_pipe!
-    SELF_PIPE.each { |io| io.close rescue nil }
-    SELF_PIPE.replace(Kgio::Pipe.new)
-    SELF_PIPE.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
-  end
-
   def inherit_listeners!
     # inherit sockets from parents, they need to be plain Socket objects
     # before they become Kgio::UNIXServer or Kgio::TCPServer
diff --git a/lib/unicorn/worker.rb b/lib/unicorn/worker.rb
index 1fb6a4a..e74a1c9 100644
--- a/lib/unicorn/worker.rb
+++ b/lib/unicorn/worker.rb
@@ -12,6 +12,7 @@ class Unicorn::Worker
   # :stopdoc:
   attr_accessor :nr, :switched
   attr_writer :tmp
+  attr_reader :to_io # IO.select-compatible
 
   PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
   DROPS = []
@@ -23,6 +24,66 @@ class Unicorn::Worker
     @raindrop[@offset] = 0
     @nr = nr
     @tmp = @switched = false
+    @to_io, @master = Unicorn.pipe
+  end
+
+  def atfork_child # :nodoc:
+    # we _must_ close in child, parent just holds this open to signal
+    @master = @master.close
+  end
+
+  # master fakes SIGQUIT using this
+  def quit # :nodoc:
+    @master = @master.close if @master
+  end
+
+  # parent does not read
+  def atfork_parent # :nodoc:
+    @to_io = @to_io.close
+  end
+
+  # call a signal handler immediately without triggering EINTR
+  # We do not use the more obvious Process.kill(sig, $$) here since
+  # that signal delivery may be deferred.  We want to avoid signal delivery
+  # while the Rack app.call is running because some database drivers
+  # (e.g. ruby-pg) may cancel pending requests.
+  def fake_sig(sig) # :nodoc:
+    old_cb = trap(sig, "IGNORE")
+    old_cb.call
+  ensure
+    trap(sig, old_cb)
+  end
+
+  # master sends fake signals to children
+  def soft_kill(sig) # :nodoc:
+    case sig
+    when Integer
+      signum = sig
+    else
+      signum = Signal.list[sig.to_s] or
+          raise ArgumentError, "BUG: bad signal: #{sig.inspect}"
+    end
+    # writing and reading 4 bytes on a pipe is atomic on all POSIX platforms
+    # Do not care in the odd case the buffer is full, here.
+    @master.kgio_trywrite([signum].pack('l'))
+  rescue Errno::EPIPE
+    # worker will be reaped soon
+  end
+
+  # this only runs when the Rack app.call is not running
+  # act like a listener
+  def kgio_tryaccept # :nodoc:
+    case buf = @to_io.kgio_tryread(4)
+    when String
+      # unpack the buffer and trigger the signal handler
+      signum = buf.unpack('l')
+      fake_sig(signum[0])
+      # keep looping, more signals may be queued
+    when nil # EOF: master died, but we are at a safe place to exit
+      fake_sig(:QUIT)
+    when :wait_readable # keep waiting
+      return false
+    end while true # loop, as multiple signals may be sent
   end
 
   # worker objects may be compared to just plain Integers
@@ -49,8 +110,11 @@ class Unicorn::Worker
     end
   end
 
+  # called in both the master (reaping worker) and worker (SIGQUIT handler)
   def close # :nodoc:
     @tmp.close if @tmp
+    @master.close if @master
+    @to_io.close if @to_io
   end
 
   # :startdoc:
-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply related	[relevance 1%]

* Re: What does it mean for the unicorn process to be bound to a terminal?
  @ 2013-11-26 19:21  2%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2013-11-26 19:21 UTC (permalink / raw)
  To: unicorn list; +Cc: Rodrigo Rosenfeld Rosas

Rodrigo Rosenfeld Rosas <rr.rosas@gmail.com> wrote:
> I see. If I understood correctly this only happens in foreground
> mode, so something like that would help me to understand the
> sentence:

> "...If your unicorn process is bound to an interactive terminal
> (running in the foreground), you can skip this step."

I used "not daemonized", since it's possible for a background process
to have a controlling terminal:

     unicorn ... & # runs background, still attached to terminal
     fg # foreground again

Pushed as fa17da92aa4e76d5fd63cb9b74d6884d611ec899
(also added a link to the init.sh example)
---------------------------8<----------------------------
Subject: [PATCH] doc: clarify SIGNALS and reference init example

"interactive terminal" needed clarification.

While we're at it, link to the init.sh example since it may
be shared with nginx.

Reported-by: Rodrigo Rosenfeld Rosas
ref: <5294E9D4.5030608@gmail.com>
---
 SIGNALS | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/SIGNALS b/SIGNALS
index 8851775..48c651e 100644
--- a/SIGNALS
+++ b/SIGNALS
@@ -7,6 +7,9 @@ signal handling matches the behavior of {nginx}[http://nginx.net/] so it
 should be possible to easily share process management scripts between
 Unicorn and nginx.
 
+One example init script is distributed with unicorn:
+http://unicorn.bogomips.org/examples/init.sh
+
 === Master Process
 
 * HUP - reloads config file and gracefully restart all workers.
@@ -101,8 +104,8 @@ The procedure is exactly like that of nginx:
 
 3. You can now send WINCH to the old master process so only the new workers
    serve requests.  If your unicorn process is bound to an interactive
-   terminal, you can skip this step.  Step 5 will be more difficult but
-   you can also skip it if your process is not daemonized.
+   terminal (not daemonized), you can skip this step.  Step 5 will be more
+   difficult but you can also skip it if your process is not daemonized.
 
 4. You should now ensure that everything is running correctly with the
    new workers as the old workers die off.
---------------------------8<----------------------------
> I believe it would help the documentation if you added a link to
> init.sh in the SIGNALS page:
> 
> http://bogomips.org/unicorn.git/tree/examples/init.sh

> Anyway, even that script won't take care of making sure that the
> reload action will rollback in case of an unsuccessful deploy.
> 
> I realize it's not easy to detect if the deploy was successful and
> that it's very application dependent, but some generic procedures
> would be helpful for a basic Rails application. Unfortunately I
> don't know what would be a good strategy, that's why I asked :P

Right.  Different apps require different validation, especially
those intended for GUI web browsers.

> Thank you for explaining the interactive terminal binding question :)

No problem!
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply related	[relevance 2%]

* Re: Handling closed clients
       [not found]           ` <m21u2sjpc9.fsf@macdaddy.atl.damballa>
@ 2013-11-07 16:48  0%         ` Eric Wong
  2013-11-07 20:22  2%           ` [PATCH] stream_input: avoid IO#close on client disconnect Eric Wong
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2013-11-07 16:48 UTC (permalink / raw)
  To: Andrew Hobson; +Cc: mongrel-unicorn

Andrew Hobson <ahobson@gmail.com> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
> 
> > (Please don't cull Cc:, I'm assuming you're not subscribed to the
> >  mailing list since we don't require subscriptions)
> 
> Sorry, that was unintentional.

No worries, and it is good to also send a copy to each recipient in the
thread in case Rubyforge is down (like it is right now).  If it stays
down, I'll have to find/make a replacement myself.

...And move to something more decentralized and resilient to downtime
while I'm at it.

> > With my proposed patch to eliminate IO#close from StreamInput,
> > this test is no longer an accurate representation of unicorn behavior.
> 
> I applied that one line patch a day and a half ago and we haven't seen
> the error in the field (yet). I am optimistic you have elegantly fixed
> the problem.
> 
> If we do see an error, I will send another email to the list.
> 
> Thanks again for your help,

No problem, I'll push that out later today.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* [PATCH] stream_input: avoid IO#close on client disconnect
  2013-11-07 16:48  0%         ` Eric Wong
@ 2013-11-07 20:22  2%           ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2013-11-07 20:22 UTC (permalink / raw)
  To: Andrew Hobson; +Cc: mongrel-unicorn

Eric Wong <normalperson@yhbt.net> wrote:
> Andrew Hobson <ahobson@gmail.com> wrote:
> > I applied that one line patch a day and a half ago and we haven't seen
> > the error in the field (yet). I am optimistic you have elegantly fixed
> > the problem.
> > 
> > If we do see an error, I will send another email to the list.
> > 
> > Thanks again for your help,
> 
> No problem, I'll push that out later today.

Pushed, thanks again:

>From f4005d5efc608e7d75371f0d0527041facd33f89 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Thu, 7 Nov 2013 20:10:01 +0000
Subject: [PATCH] stream_input: avoid IO#close on client disconnect

This can avoid IOError from being seen by the application, and also
reduces points where IO#close may be called.  This is a good thing
if we eventually port this code into a low-level server like
cmogstored where per-client memory space is defined by FD number of
a client.

Reported-by: Andrew Hobson <ahobson@gmail.com>
---
 lib/unicorn/stream_input.rb | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/lib/unicorn/stream_input.rb b/lib/unicorn/stream_input.rb
index c8a4240..9278f47 100644
--- a/lib/unicorn/stream_input.rb
+++ b/lib/unicorn/stream_input.rb
@@ -139,10 +139,7 @@ private
     # we do support clients that shutdown(SHUT_WR) after the
     # _entire_ request has been sent, and those will not have
     # raised EOFError on us.
-    if @socket
-      @socket.shutdown
-      @socket.close
-    end
+    @socket.shutdown if @socket
   ensure
     raise Unicorn::ClientShutdown, "bytes_read=#{@bytes_read}", []
   end
-- 
1.8.4.483.g7fe67e6.dirty
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply related	[relevance 2%]

* Re: [PATCH] construct listener_fds Hash in 1.8 compatible way
  @ 2013-11-01 19:00  2%       ` Ernest W. Durbin III
  0 siblings, 0 replies; 200+ results
From: Ernest W. Durbin III @ 2013-11-01 19:00 UTC (permalink / raw)
  To: unicorn list

On Fri, Nov 1, 2013 at 2:54 PM, Eric Wong <normalperson@yhbt.net> wrote:
> "Ernest W. Durbin III" <ewdurbin@gmail.com> wrote:
>> On Fri, Nov 1, 2013 at 12:50 PM, Eric Wong <normalperson@yhbt.net> wrote:
>> > "Ernest W. Durbin III" <ewdurbin@gmail.com> wrote:
>> >> This renables the ability for Ruby 1.8 environments to perform reexecs
>> >
>> > Is this for Ruby 1.8.6?  I've only tested on 1.8.7,
>> > I haven't had a 1.8.6 installation in a while.
>> >
>>
>> I'll admit that the reason I need it is for a Ruby 1.8.6 environment...
>
> OK (wow!).   I've been pondering dropping 1.8 entirely, but I think I'll
> keep it for now since CentOS 6.x still uses it
>
> Anyways, was that the only 1.8.6-incompatible thing we did?

That's all I've found so far! Happy to be dropping thin, and I promise
we're on course to get past 1.8.6 at some point... But I'm just the
Ops guy :)

>
>> However, Ruby 1.8.7 does not have support for that Hash constructor
>> either it simply fails silently and in a bug prone manner.
>>
>> ```
>> [ernestd@ewd3do ~]$ ruby --version
>> ruby 1.8.7 (2011-06-30 patchlevel 352) [i386-linux]
>> [ernestd@ewd3do ~]$ irb
>> irb(main):001:0> thing = Hash[ [ 'foo', 'bar' ], ['fizz', 'buzz'] ]
>> => {["foo", "bar"]=>["fizz", "buzz"]}
>
> Actually, on 1.8.7, unicorn does the following (note the extra outer array)
>
> irb(main):001:0> thing = Hash[ [ [ 'foo', 'bar' ], ['fizz', 'buzz'] ] ]
> => {"fizz"=>"buzz", "foo"=>"bar"}
>
> so it was fine

Interesting. Sorry I missed that in the initial irb tests.

Funnily enough, the List of List's Constructor type isn't documented for 1.8.7

Thanks Again!

> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: pid file handling issue
  2013-10-23 22:55  2% pid file handling issue Michael Fischer
@ 2013-10-24  0:53  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2013-10-24  0:53 UTC (permalink / raw)
  To: unicorn list

Michael Fischer <mfischer@zendesk.com> wrote:
> Hi everyone,
> 
> While writing a script to determine the success or failure of a
> Unicorn reload attempt (without having to parse a log), I noticed that
> Unicorn doesn't preserve the timestamp of its pid file.  In other
> words, instead of renaming pidfile to pidfile.oldbin (and then back
> again if the reload failed), it creates a new pid file for each master
> phase change.
> 
> This means we cannot simply compare the mtime of the current pidfile
> against the time the USR2 signal was given in order to make a
> reasonable conclusion.
> 
> I tried another method, which was to look at the start time of the
> process as reported by ps(1), but on Linux, that time does not come
> from the wall clock: it's derived from the number of jiffies since
> system boot.  So it's not guaranteed to be accurate, especially if the
> wall clock was incorrect at system boot.
> 
> Are there any other methods anyone can suggest?  Otherwise, a change
> to Unicorn's behavior with respect to pid file maintenance would be
> kindly appreciated.

I read and stash the value of the pid file before issuing any USR2.
Later, you can issue "kill -0 $old_pid" after sending SIGQUIT
to ensure it's dead.

Checking the mtime of the pidfile is really bizarre...

OTOH, there's times when users accidentally remove a pid
file and regenerate by hand it from ps(1), too...
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* pid file handling issue
@ 2013-10-23 22:55  2% Michael Fischer
  2013-10-24  0:53  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Michael Fischer @ 2013-10-23 22:55 UTC (permalink / raw)
  To: mongrel-unicorn

Hi everyone,

While writing a script to determine the success or failure of a
Unicorn reload attempt (without having to parse a log), I noticed that
Unicorn doesn't preserve the timestamp of its pid file.  In other
words, instead of renaming pidfile to pidfile.oldbin (and then back
again if the reload failed), it creates a new pid file for each master
phase change.

This means we cannot simply compare the mtime of the current pidfile
against the time the USR2 signal was given in order to make a
reasonable conclusion.

I tried another method, which was to look at the start time of the
process as reported by ps(1), but on Linux, that time does not come
from the wall clock: it's derived from the number of jiffies since
system boot.  So it's not guaranteed to be accurate, especially if the
wall clock was incorrect at system boot.

Are there any other methods anyone can suggest?  Otherwise, a change
to Unicorn's behavior with respect to pid file maintenance would be
kindly appreciated.

Best regards,

--Michael
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* More unexplained timeouts
@ 2013-09-29 20:13  2% nick
  0 siblings, 0 replies; 200+ results
From: nick @ 2013-09-29 20:13 UTC (permalink / raw)
  To: mongrel-unicorn

We're still suffering from unexplained workers timing out.  We recently upgraded to the latest unicorn 4.6.3 (while still on REE 1.8.7) in the hopes that it would solve our issues.  Unfortunately, this seemed to exacerbate the problem, with timeouts happening more frequently, but that could be related to greater precision in timeouts in newer versions of unicorn.  (In our unicorn 3.6.2, a timeout set to 120s might not ACTUALLY timeout until 180s or more, thus allowing a bit more time for Ruby to finish whatever it was choking on.)

We dropped the timeout down to 65s (to make sure it was triggered) and then tried to add greater logging (per http://permalink.gmane.org/gmane.comp.lang.ruby.unicorn.general/1269.)  The START/FINISH approach confirms it's not an issue with our application code, ie:

HH:MM:SS- S/F[PID]- /PATH
15:21:01- START-25904- /pathA
15:21:01- FINISH-25904- /pathA
15:21:01- START-25904- /pathB
15:21:01- FINISH-25904- /pathB
15:21:01- START-25904- /pathC
15:21:01- FINISH-25904- /pathC
worker=11 PID:25904 timeout (66s > 65s), killing
reaped #<Process::Status: pid=25904,signaled(SIGKILL=9)> worker=11

For each START we always get a corresponding FINISH and then the worker is killed.  Additionally, our nginx logs confirm that this last request was sent back to the client.  No 'upstream' errors in our nginx log, either.

When we tried the Thread sleep approach, nothing actually appeared in the logs.  I imagine this means that ruby or some C extension is misbehaving.

Unfortunately, it's been impossible for us to recreate this in development.  

Thoughts?

RHEL 5.6
REE 1.8.7 2011.12
Unicorn 4.6.3
16 unicorn workers on 8 cores
No swap activity, no peaks in load

Again, thanks for all your help!

-Nick

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: A barrage of unexplained timeouts
  2013-08-20 20:42  0%             ` Eric Wong
@ 2013-08-20 21:19  0%               ` nick
  0 siblings, 0 replies; 200+ results
From: nick @ 2013-08-20 21:19 UTC (permalink / raw)
  To: unicorn list

"Eric Wong" <normalperson@yhbt.net> said:

> nick@auger.net wrote:
>> "Eric Wong" <normalperson@yhbt.net> said:
>> > This is really strange.  This was only really bad for a 7s period?
>>
>> It was a 7 minute period.  All of the workers would become busy and
>> exceed their >120s timeout.  Master would kill and re-spawn them,
>> they'd start to respond to a handful of requests (anywhere from 5-50)
>> after which they'd become "busy" again, and get force killed by
>> master.  This pattern happened 3 times over a 7 minute period.
>>
>> > Has it happened again?
>>
>> No
>>
>> > Anything else going on with the system at that time?  Swapping,
>> > particularly...
>>
>> No swap activity or high load.  Our munin graphs indicate a peak of
>> web/app server disk latency around that time, although our graphs show
>> many other similar peaks, without incident.
> 
> I'm stumped :<

I was afraid you'd say that :(.

> Do you have any background threads running that could be hanging the
> workers?   This is Ruby 1.8, after all, so there's more likely to be
> some blocking call hanging the entire process.  AFAIK, some monitoring
> software runs a background thread in the unicorn worker and maybe the
> OpenSSL extension doesn't work as well if it encountered network
> problems under Ruby 1.8

We don't explicitly create any threads in our rails code.  We do communicate with backgroundrb worker processes, although, none of the strangeness today involved any routes that would hit backgroundrb workers.

Is there any instrumentation that I could add that might help debugging in the future? ($request_time and $upstream_response_time are now in my nginx logs.)  We have noticed these "unexplainable timeouts" before, but typically for a single worker.  If there's some debugging that could be added I might be able to track it down during these one-off events.

Thoughts?

I absolutely appreciate all your help!

-Nick

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: A barrage of unexplained timeouts
  2013-08-20 20:03  2%           ` nick
@ 2013-08-20 20:42  0%             ` Eric Wong
  2013-08-20 21:19  0%               ` nick
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2013-08-20 20:42 UTC (permalink / raw)
  To: unicorn list

nick@auger.net wrote:
> "Eric Wong" <normalperson@yhbt.net> said:
> > This is really strange.  This was only really bad for a 7s period?
> 
> It was a 7 minute period.  All of the workers would become busy and
> exceed their >120s timeout.  Master would kill and re-spawn them,
> they'd start to respond to a handful of requests (anywhere from 5-50)
> after which they'd become "busy" again, and get force killed by
> master.  This pattern happened 3 times over a 7 minute period.
> 
> > Has it happened again?
> 
> No
> 
> > Anything else going on with the system at that time?  Swapping,
> > particularly...
> 
> No swap activity or high load.  Our munin graphs indicate a peak of
> web/app server disk latency around that time, although our graphs show
> many other similar peaks, without incident.

I'm stumped :<

Do you have any background threads running that could be hanging the
workers?   This is Ruby 1.8, after all, so there's more likely to be
some blocking call hanging the entire process.  AFAIK, some monitoring
software runs a background thread in the unicorn worker and maybe the
OpenSSL extension doesn't work as well if it encountered network
problems under Ruby 1.8

Otherwise, I don't know...
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: A barrage of unexplained timeouts
  2013-08-20 18:49  2%         ` Eric Wong
@ 2013-08-20 20:03  2%           ` nick
  2013-08-20 20:42  0%             ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: nick @ 2013-08-20 20:03 UTC (permalink / raw)
  To: unicorn list

"Eric Wong" <normalperson@yhbt.net> said:

> nick@auger.net wrote:
>> "Eric Wong" <normalperson@yhbt.net> said:
>> >
>> > Do you have any other requests in your logs which could be taking
>> > a long time and hogging workers, but not high enough to trigger the
>> > unicorn kill timeout.
> 
>> I don't *think* so.  Most requests finish <300ms.  We do have some
>> more intensive code-paths, but they're administrative and called much
>> less frequently.  Most of these pages complete in <3seconds.
>>
>> For requests that made it to rails logging, the LAST processed request
>> before the worker timed-out all completed very quickly (and no real
>> pattern in terms of which page may be triggering it.)
> 
> This is really strange.  This was only really bad for a 7s period?

It was a 7 minute period.  All of the workers would become busy and exceed their >120s timeout.  Master would kill and re-spawn them, they'd start to respond to a handful of requests (anywhere from 5-50) after which they'd become "busy" again, and get force killed by master.  This pattern happened 3 times over a 7 minute period.

> Has it happened again?

No

> Anything else going on with the system at that time?  Swapping, particularly...

No swap activity or high load.  Our munin graphs indicate a peak of web/app server disk latency around that time, although our graphs show many other similar peaks, without incident.

> And if you're inside a VM, maybe your neighbors were hogging things.
> Large PUT/POST requests which require filesystem I/O are particularly
> sensitive to this.

We can't blame virtualization as we're on dedicated hardware.

>> > Is this with Unix or TCP sockets?  If it's over a LAN, maybe there's
>> > still a bad switch/port/cable somewhere (that happens often to me).
>>
>> TCP sockets, with nginx and unicorn running on the same box.
> 
> OK, that probably rules out a bunch of problems.
> Just to be thorough, anything interesting in dmesg or syslogs?

Nothing interesting in /var/log/messages.  dmesg (no timestamps) includes lines like:

TCP: Treason uncloaked! Peer ###.###.###.###:63554/80 shrinks window 1149440448:1149447132. Repaired.

>> > With Unix sockets, I don't recall encountering recent problems under
>> > Linux.  Which OS are you running?
>>
>> Stock RHEL 5, kernel 2.6.18.
> 
> RHEL 5.0 or 5.x?  I can't remember /that/ far back to 5.0 (I don't think
> I even tried it until 5.2), but don't recall anything being obviously
> broken in those...

We're on RHEL 5.6.

Thanks,

-Nick

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: A barrage of unexplained timeouts
  2013-08-20 18:11  2%       ` nick
@ 2013-08-20 18:49  2%         ` Eric Wong
  2013-08-20 20:03  2%           ` nick
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2013-08-20 18:49 UTC (permalink / raw)
  To: unicorn list

nick@auger.net wrote:
> "Eric Wong" <normalperson@yhbt.net> said:
> > 
> > Do you have any other requests in your logs which could be taking
> > a long time and hogging workers, but not high enough to trigger the
> > unicorn kill timeout.

> I don't *think* so.  Most requests finish <300ms.  We do have some
> more intensive code-paths, but they're administrative and called much
> less frequently.  Most of these pages complete in <3seconds.
> 
> For requests that made it to rails logging, the LAST processed request
> before the worker timed-out all completed very quickly (and no real
> pattern in terms of which page may be triggering it.)

This is really strange.  This was only really bad for a 7s period?
Has it happened again?  Anything else going on with the system at that
time?  Swapping, particularly...

And if you're inside a VM, maybe your neighbors were hogging things.
Large PUT/POST requests which require filesystem I/O are particularly
sensitive to this.

> > Is this with Unix or TCP sockets?  If it's over a LAN, maybe there's
> > still a bad switch/port/cable somewhere (that happens often to me).
> 
> TCP sockets, with nginx and unicorn running on the same box.

OK, that probably rules out a bunch of problems.
Just to be thorough, anything interesting in dmesg or syslogs?

> > With Unix sockets, I don't recall encountering recent problems under
> > Linux.  Which OS are you running?
> 
> Stock RHEL 5, kernel 2.6.18.

RHEL 5.0 or 5.x?  I can't remember /that/ far back to 5.0 (I don't think
I even tried it until 5.2), but don't recall anything being obviously
broken in those...
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: A barrage of unexplained timeouts
  @ 2013-08-20 18:11  2%       ` nick
  2013-08-20 18:49  2%         ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: nick @ 2013-08-20 18:11 UTC (permalink / raw)
  To: unicorn list

"Eric Wong" <normalperson@yhbt.net> said:
> nick@auger.net wrote:
>> "Eric Wong" <normalperson@yhbt.net> said:
>> > Can you take a look at the nginx error and access logs?  From what
>> > you're saying, there's a chance a request never even got to the Rails
>> > layer.  However, nginx should be logging failed/long-running requests to
>> > unicorn.
>>
>> The nginx access logs show frequent 499 responses.  The error logs are filled
>> with:
>>
>> connect() failed (110: Connection timed out) while connecting to upstream
>> upstream timed out (110: Connection timed out) while reading response header from
>> upstream
>>
>> What specific pieces of information should I be looking for in the logs?
> 
> Do you have any other requests in your logs which could be taking
> a long time and hogging workers, but not high enough to trigger the
> unicorn kill timeout.

I don't *think* so.  Most requests finish <300ms.  We do have some more intensive code-paths, but they're administrative and called much less frequently.  Most of these pages complete in <3seconds.

For requests that made it to rails logging, the LAST processed request before the worker timed-out all completed very quickly (and no real pattern in terms of which page may be triggering it.)

> (enable $request_time in nginx access logs if you haven't already)

I'll enable this.

> Is this with Unix or TCP sockets?  If it's over a LAN, maybe there's
> still a bad switch/port/cable somewhere (that happens often to me).

TCP sockets, with nginx and unicorn running on the same box.

> With Unix sockets, I don't recall encountering recent problems under
> Linux.  Which OS are you running?

Stock RHEL 5, kernel 2.6.18.

Thanks again,

-Nick



_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: A barrage of unexplained timeouts
  @ 2013-08-20 17:27  2%   ` nick
    0 siblings, 1 reply; 200+ results
From: nick @ 2013-08-20 17:27 UTC (permalink / raw)
  To: unicorn list

"Eric Wong" <normalperson@yhbt.net> said:
> Can you take a look at the nginx error and access logs?  From what
> you're saying, there's a chance a request never even got to the Rails
> layer.  However, nginx should be logging failed/long-running requests to
> unicorn.

The nginx access logs show frequent 499 responses.  The error logs are filled with:

connect() failed (110: Connection timed out) while connecting to upstream
upstream timed out (110: Connection timed out) while reading response header from upstream

What specific pieces of information should I be looking for in the logs?

Thanks Again,

-Nick

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* A barrage of unexplained timeouts
@ 2013-08-20 14:47  2% nick
    0 siblings, 1 reply; 200+ results
From: nick @ 2013-08-20 14:47 UTC (permalink / raw)
  To: mongrel-unicorn

We've been running unicorn-3.6.2 on REE 1.8.7 2011.12 in production for quite some time and we use monit to monitor each unicorn worker.  Occasionally, I'll get a notification that a worker has timed-out and has been re-spawned.  In all these cases, when I look at the rails logs, I can see the last request that the worker handled, and they all have appeared to complete successfully from the client's perspective (rails and nginx respond with 200), but the unicorn log shows that it was killed due to timeout.  This has always been relatively rare and I thought it was a non-problem.

Until today.

Today, for about a 7 minute period, our workers would continually report as having timed-out and would be killed by the master.  After re-spawning, the workers would serve a handful of requests and then eventually be killed again.

During this time, our servers (Web, PG DB, and redis) were not under load and IO was normal.  After the last monit notification at 8:30, everything went back to normal.  I understand why unicorns would timeout if they were waiting (>120 secs) on IO, but there aren't any orphaned requests in the rails log.  For each request line, there's a corresponding completion line.  No long running queries to blame on PG, either.

I know we're probably due for an upgrade, but I'm hoping to get to the bottom of these unexplained timeouts.

Thanks for your help!

-Nick

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Increassing timeouts
  @ 2013-08-03  6:32  2%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2013-08-03  6:32 UTC (permalink / raw)
  To: unicorn list

Troex Nevelin <list@mrtech.ru> wrote:
> On Jan 15, 2013, at 21:44 , Eric Wong <normalperson@yhbt.net> wrote:
> > But seriously, who will wait 120s for a website to load?
> 
> I have similar situation, in my case it's admin interface so admin
> knows that some operations need time to process and will wait.
> 
> Right now I'm running two packs of Unicorn instances - one for quick
> and one for long requests. But most of the time long request pool is 
> idling and eating memory.

Cranking up the timeout will improve its chances of being swapped out.
If memory usage of an infrequently used instance is a concern.

> My question: is possible to change Unicorn timeout per request from
> Rails app? Middleware hook could be ok too. (not sure if it's possible
> as I think timeout is set in master).

Right, it's currently not possible to influence the master.  And I'm
not convinced it's the right way to go, either.

What I've done in the past is to put the client in a periodic refresh
loop (meta refresh or whatever tag it was) and have it poll the app
while it was waiting for a long-running job to finish.  I think it was
(or still is) a common thing...

Btw, Rubyforge delayed this email by over 3 weeks?  But then again you
responded to a 6 month old thread %x
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Why doesn't SIGTERM quit gracefully?
  2013-04-25 18:09  2%     ` Eric Wong
@ 2013-04-25 20:45  0%       ` Andreas Falk
  0 siblings, 0 replies; 200+ results
From: Andreas Falk @ 2013-04-25 20:45 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn list

On Thu, Apr 25, 2013 at 8:09 PM, Eric Wong <normalperson@yhbt.net> wrote:
> If nginx developers are willing to change this, definitely.

I guess I'll go camp out among some nginx developers then.

> If I had to do this all over again[1], graceful shutdown would be the
> _only_ option.
>
> [1] - I haven't released a new web server for 2013... yet :)

Hehe, i hear you! Maybe a unicorn 5 or do you have any plans for
something completely new :)? I guess i'll have to wait and see.

Thanks for the help!

Andreas
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: Why doesn't SIGTERM quit gracefully?
  @ 2013-04-25 18:09  2%     ` Eric Wong
  2013-04-25 20:45  0%       ` Andreas Falk
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2013-04-25 18:09 UTC (permalink / raw)
  To: unicorn list; +Cc: Andreas Falk

Andreas Falk <mail@andreasfalk.se> wrote:
> On Thu, Apr 25, 2013 at 10:51 AM, Eric Wong <normalperson@yhbt.net> wrote:
> > Andreas Falk <mail@andreasfalk.se> wrote:
> >> I'm wondering why SIGINT and SIGTERM both were chosen for the quick
> >> shutdown? I agree with SIGINT but not with SIGTERM. A lot of unix
> >> tools send SIGTERM as default (kill, runit among some) and it seems to
> >> be the standard way of telling a process to quit gracefully but not
> >> among Ruby people (there are a few other ruby processes behaving the
> >> same way). I just think it's weird that the default command will exit
> >> without taking care of their current request.
> >>
> >> Also i'm not on the mailinglist so it would be great if you could cc
> >> mail@andreasfalk.se
> >
> > I think it's weird, too.  But that's what nginx does, and I based most
> > of the UI decisions on nginx (so it's easy to reuse nginx scripts
> > with unicorn).
> 
> Is it something you'd be willing to change?

If nginx developers are willing to change this, definitely.

Otherwise, at this point, I'm not certain what the ramifications of a
change would be to existing users.  Maybe an option would work, but too
many options overwhelms people.

With unicorn, I suppose having the following after_fork hook works:

   # Note: after_fork hook is run before the :QUIT handler is installed,
   # so we can't use the result of trap(:QUIT) at this point:
   after_fork do |server,worker|
     trap(:TERM) { Process.kill(:QUIT, $$) }
   end

If I had to do this all over again[1], graceful shutdown would be the
_only_ option.

[1] - I haven't released a new web server for 2013... yet :)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Unicorn hangs on POST request
  2013-04-03  4:56  2%                   ` Lin Jen-Shin (godfat)
@ 2013-04-03 11:38  0%                     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2013-04-03 11:38 UTC (permalink / raw)
  To: unicorn list

"Lin Jen-Shin (godfat)" <godfat@godfat.org> wrote:
> On Wed, Apr 3, 2013 at 6:36 AM, Eric Wong <normalperson@yhbt.net> wrote:
> > Fwiw, Cool.io works pretty well in my experience.
> [...]
> > I can also help fix Cool.io bugs since it's written in C, but I can't
> > fix EM bugs: C++ is too big for my tiny brain.
> 
> Last time I tried Cool.io it would occasionally crash with assertion errors.
> It's been a while from now, but I guess since there's no much development
> happened for a long time, it won't change too much right now.
> 
> I could try to get the assertion errors again and see if anyone wants to step
> in, but personally since the author is heading to celluloid-io, I am not too
> interested in fixing Cool.io...

I'd be willing to look into this and send a fix to coolio upstream if
you can help me reproduce it.  No guarantees there'll be a release even
if I patch it, though, that's up to Tony Arcieri (Coolio maintainer).
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: Unicorn hangs on POST request
  @ 2013-04-03  4:56  2%                   ` Lin Jen-Shin (godfat)
  2013-04-03 11:38  0%                     ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Lin Jen-Shin (godfat) @ 2013-04-03  4:56 UTC (permalink / raw)
  To: unicorn list

On Wed, Apr 3, 2013 at 6:36 AM, Eric Wong <normalperson@yhbt.net> wrote:
> Fwiw, Cool.io works pretty well in my experience.
[...]
> I can also help fix Cool.io bugs since it's written in C, but I can't
> fix EM bugs: C++ is too big for my tiny brain.

Last time I tried Cool.io it would occasionally crash with assertion errors.
It's been a while from now, but I guess since there's no much development
happened for a long time, it won't change too much right now.

I could try to get the assertion errors again and see if anyone wants to step
in, but personally since the author is heading to celluloid-io, I am not too
interested in fixing Cool.io...



Off topic, I have some EM issue with Ruby 2.0, which would hang on our
tests and after interrupting the hung test, the following tests would yield:
"RuntimeError: eventmachine not initialized: evma_signal_loopbreak"
I have no idea what's going on... :( I hope we could leave EM soon...
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: No middleware without touching RACK_ENV
  2013-01-28 23:21  2%     ` Eric Wong
@ 2013-01-29  2:31  0%       ` Lin Jen-Shin (godfat)
  0 siblings, 0 replies; 200+ results
From: Lin Jen-Shin (godfat) @ 2013-01-29  2:31 UTC (permalink / raw)
  To: unicorn list

On Tue, Jan 29, 2013 at 7:21 AM, Eric Wong <normalperson@yhbt.net> wrote:
> "Lin Jen-Shin (godfat)" <godfat@godfat.org> wrote:
>> Speaking to this, I might want to complain about the other severs :P
>> As far as I know, only if you use `rackup' would you get those middleware.
>>
>> That is, `unicorn' is compatible with `rackup'. That's a good thing.
>> But it seems to me that both `thin' and `puma' (and maybe including
>> others, I didn't check) would not do this if you use their command line
>> tools. They even have different options to enable Rack::CommonLogger.
>>
>> Actually this is also the reason why sometimes `thin' and `unicorn'
>> worked differently when people switching from Thin to Unicorn.
>> And you know a lot of people are using Thin in the first place,
>> thus sometimes blaming Unicorn works differently.
>>
>> I am not sure how many people are using `rackup', but according to
>> my observation, not too much. If people are using Sinatra directly,
>> eventually they would launch the server via Rack::Handler.get.run,
>> which does not try to wrap additional middleware as what `rackup'
>> would do if I read correctly.
>>
>> So I am not sure if a lot of people would be expecting this actually.
>> I used to use `rackup' a lot to get consistent behaviour, but it looks to
>> me not many people even know the existence of rackup :(
>
> *sigh*  :<
>
> I kinda wish there was no default middleware, either.  But it's
> too late to change unicorn defaults.
>
> I think your -N/--no-default-middleware patch can be accepted,
> then.  after_app_load is more prone to user error/confusion, I think
> (I believe RAILS_ENV/RACK_ENV should be frozen for the lifetime of
>  the process)

Awesome. Thanks! Hope this would also be helpful for others.

> Can you try send-email again with the --no-default-middleware patch? :)
>
> The git-send-email(1) manpage has several good examples for your email
> provider.  There's also git-imap-send which you can use to stick email
> in your Drafts folder, but I've never needed/used it, though.

Just got managed to send a test mail to myself. I realized that my git is
using /usr/bin/perl instead of /usr/local/bin/perl, so that instead of running
`cpan Net::SMTP::SSL` I have to run `sudo -H /usr/bin/cpan Net::SMTP::SSL`
following instructions from this site:
http://kbase.wincent.com/old/knowledge-base/Installing_Net::SMTP::SSL_for_sending_patches_with_Git_over_secure_SMTP.html

I'll send the patch shortly after updating the commit message.
Feel free to edit it as usual :)
And should I send patches for rainbows and zbatery as well?
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: No middleware without touching RACK_ENV
  2013-01-28 14:43  1%   ` Lin Jen-Shin (godfat)
@ 2013-01-28 23:21  2%     ` Eric Wong
  2013-01-29  2:31  0%       ` Lin Jen-Shin (godfat)
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2013-01-28 23:21 UTC (permalink / raw)
  To: unicorn list

"Lin Jen-Shin (godfat)" <godfat@godfat.org> wrote:
> On Sat, Jan 26, 2013 at 2:39 AM, Eric Wong <normalperson@yhbt.net> wrote:
> > Doesn't Rails favor RAILS_ENV over RACK_ENV?  unicorn ignores RAILS_ENV
> > (unicorn_rails respects RAILS_ENV, but unicorn_rails isn't recommended
> > for modern (Rack-enabled) Rails)
> 
> Yes, it is. But it would still look into RACK_ENV if RAILS_ENV is not available.
> One solution would be setting RAILS_ENV=development while setting
> RACK_ENV=none. I don't really want to do this for several reasons:
> 
> * It would be better to keep RAILS_ENV == RACK_ENV to avoid confusion.
> * It might be tricky to make RAILS_ENV default to development while setting
>   RACK_ENV since Rails would be looking into it. So we need to make sure
>   that Rails is loaded and default to development and then we can setup
>   RACK_ENV to avoid messing up the original Rails default.

OK, I agree making RAILS_ENV==RACK_ENV in deployments is less confusing.
(unicorn should continue NOT enforcing this internally, though)

> > I'd imagine users wanting the same app-wide settings going between
> > different application servers, too.
> 
> Speaking to this, I might want to complain about the other severs :P
> As far as I know, only if you use `rackup' would you get those middleware.
> 
> That is, `unicorn' is compatible with `rackup'. That's a good thing.
> But it seems to me that both `thin' and `puma' (and maybe including
> others, I didn't check) would not do this if you use their command line
> tools. They even have different options to enable Rack::CommonLogger.
> 
> Actually this is also the reason why sometimes `thin' and `unicorn'
> worked differently when people switching from Thin to Unicorn.
> And you know a lot of people are using Thin in the first place,
> thus sometimes blaming Unicorn works differently.
> 
> I am not sure how many people are using `rackup', but according to
> my observation, not too much. If people are using Sinatra directly,
> eventually they would launch the server via Rack::Handler.get.run,
> which does not try to wrap additional middleware as what `rackup'
> would do if I read correctly.
> 
> So I am not sure if a lot of people would be expecting this actually.
> I used to use `rackup' a lot to get consistent behaviour, but it looks to
> me not many people even know the existence of rackup :(

*sigh*  :<

I kinda wish there was no default middleware, either.  But it's
too late to change unicorn defaults.

I think your -N/--no-default-middleware patch can be accepted,
then.  after_app_load is more prone to user error/confusion, I think
(I believe RAILS_ENV/RACK_ENV should be frozen for the lifetime of
 the process)

> Last time I tried to setup SMTP for git send-email failed.
> I'll try again next time. Thanks!

Can you try send-email again with the --no-default-middleware patch? :)

The git-send-email(1) manpage has several good examples for your email
provider.  There's also git-imap-send which you can use to stick email
in your Drafts folder, but I've never needed/used it, though.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: No middleware without touching RACK_ENV
  @ 2013-01-28 14:43  1%   ` Lin Jen-Shin (godfat)
  2013-01-28 23:21  2%     ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Lin Jen-Shin (godfat) @ 2013-01-28 14:43 UTC (permalink / raw)
  To: unicorn list

On Sat, Jan 26, 2013 at 2:39 AM, Eric Wong <normalperson@yhbt.net> wrote:
> Doesn't Rails favor RAILS_ENV over RACK_ENV?  unicorn ignores RAILS_ENV
> (unicorn_rails respects RAILS_ENV, but unicorn_rails isn't recommended
> for modern (Rack-enabled) Rails)

Yes, it is. But it would still look into RACK_ENV if RAILS_ENV is not available.
One solution would be setting RAILS_ENV=development while setting
RACK_ENV=none. I don't really want to do this for several reasons:

* It would be better to keep RAILS_ENV == RACK_ENV to avoid confusion.
* It might be tricky to make RAILS_ENV default to development while setting
  RACK_ENV since Rails would be looking into it. So we need to make sure
  that Rails is loaded and default to development and then we can setup
  RACK_ENV to avoid messing up the original Rails default.

> Is there a benefit which RACK_ENV=development gives you for Rails
> that RAILS_ENV=development does not give you?

The main reason is that I want to avoid confusion. The other reason
is that that's also how Heroku tries to use. I think it's also because of
consistency. (so that we don't have to distinguish RACK_ENV and RAILS_ENV)

The other reason would be making them different might cause some
issues as Rails would also be looking at RACK_ENV.

> Fwiw, the times I worked on Rails, I used customized dev environments
> anyways, so I would use RAILS_ENV=dev_local or RAILS_ENV=dev_$NETWORK
> for switching between a localhost-only vs networked setup.

I would also do this I guess, but since I am not the only one working on
the application, I would avoid making such changes. We have a lot of
configurations depending on the word "development" already.

>> I know this is somehow a spec from Rack, but I guess I
>> don't like this behaviour. One workaround would be
>> providing a `after_app_load' hook, and we add this to
>> the bottom of config.ru:
>>
>>   ENV['RACK_ENV_ORIGINAL'] = ENV['RACK_ENV']
>>   ENV['RACK_ENV'] = 'none'
>>
>> and add this to the unicorn configuration file:
>>
>>   after_app_load do |_|
>>     ENV['RACK_ENV'] = ENV['RACK_ENV_ORIGINAL']
>>   end
>>
>> This is probably a stupid hack, but I wonder if after_app_load
>> hook would be useful for other cases?
>
> I don't see how this hook has benefits over putting this in config.ru
> (or somewhere else in your app, since your app is already loaded...)

It's better to me because it's clear on the loading order.
The point where to change RACK_ENV=none would be
after Rails loaded (to avoid messing Rails up), and
_before Unicorn inserting middleware_. And then I would
like to switch RACK_ENV=RAILS_ENV to avoid confusion.
It's hard to tell where I can put this code.

> I'd imagine users wanting the same app-wide settings going between
> different application servers, too.

Speaking to this, I might want to complain about the other severs :P
As far as I know, only if you use `rackup' would you get those middleware.

That is, `unicorn' is compatible with `rackup'. That's a good thing.
But it seems to me that both `thin' and `puma' (and maybe including
others, I didn't check) would not do this if you use their command line
tools. They even have different options to enable Rack::CommonLogger.

Actually this is also the reason why sometimes `thin' and `unicorn'
worked differently when people switching from Thin to Unicorn.
And you know a lot of people are using Thin in the first place,
thus sometimes blaming Unicorn works differently.

I am not sure how many people are using `rackup', but according to
my observation, not too much. If people are using Sinatra directly,
eventually they would launch the server via Rack::Handler.get.run,
which does not try to wrap additional middleware as what `rackup'
would do if I read correctly.

So I am not sure if a lot of people would be expecting this actually.
I used to use `rackup' a lot to get consistent behaviour, but it looks to
me not many people even know the existence of rackup :(

> I'm against adding more command-line or config options, especially
> around loading middleware.  RACK_ENV is already a source of confusion,
> and Rails also has/(or had?) it's own middleware loading scheme
> independent of config.ru.

Yes, I hate that, too. I can't complain about Rails less :P I don't understand
why people are building those middleware scheme on their own way,
even Sinatra does this. I guess they want more control over middleware,
so that they can reorder, delete, insert more/less middleware somehow.
I guess that makes sense in some way... but they should be really in
Rack instead of each web framework IMHO.

> git format-patch + git send-email is the recommended way if your
> mail client cannot be taught to leave formatting alone.  Also this
> is easy for sending multiple patches in the same thread.
>
> git format-patch --stdout with a good mail client which will not wrap
> messages is also great (especially for single patches).
>
> Attachments (generated using git format-patch) for text/x-diff should be
> accepted by Mailman.  Attached patches are a last resort, I prefer
> inline patches since I can quote/reply to them inline.
>
> (same patch submission guidelines as git or Linux kernel)

Last time I tried to setup SMTP for git send-email failed.
I'll try again next time. Thanks!

> Commit messages should explain why a change is made/needed, not just
> what it does.

Yeah, I know, but I can understand if none of them is acceptable,
(that's why I didn't bring this up in the first place) and the reason to
build those features might be arguing. So instead of writing them
carefully, I decided to bring this up sooner and then we can work out
the true reason later on :D

Thanks a lot for your time.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 1%]

* Re: SIGSEGV at shutdown (was: Re: your mail)
  @ 2013-01-24 10:27  2%   ` Charles Hornberger
  0 siblings, 0 replies; 200+ results
From: Charles Hornberger @ 2013-01-24 10:27 UTC (permalink / raw)
  To: unicorn list

On Thu, Jan 24, 2013 at 10:52 AM, Eric Wong <normalperson@yhbt.net> wrote:
> Charles Hornberger <charles.hornberger@gmail.com> wrote:
>> On Mon, Jan 21, 2013 at 11:28 AM, Eric Wong <normalperson@yhbt.net> wrote:
>> > Charles Hornberger <charles.hornberger@gmail.com> wrote:
>> >> E, [2013-01-18T17:54:21.502915 #59285] ERROR -- : reaped
>> >> #<Process::Status: pid 59288 SIGSEGV (signal 11)> worker=1
>
> <snip>
>
>> >> I, [2013-01-18T17:54:21.605077 #59285]  INFO -- : master complete
>> >>
>> >> Just wondering if it's something I should be concerned about? I saw no
>> >> obvious symptoms of problems before or after…
>> >>
>> >> We currently restart unicorn (which is on a freebsd jail) like so:
>> >
>> > A SEGV at shutdown is likely an ordering problem at VM shutdown
>> > (probably GC/finalization handling).  It could be specific to the
>> > malloc/pthread implementation on FreeBSD, even.
>> >
>> > Which version of Ruby are you using?
>>
>> 1.9.3p-125
>
> Can you give 1.9.3-p374 a try?  There were several GC-related segfault
> fixes along the way (fairly large and drastic changes, even).

We'll be moving to that soon. Since the problem has only occurred
*once* afaik, and there was nothing special that I can think of to do
to reproduce it, we may never know if the change fixed anything. :)
But if I happen to see it again, I will attempt to provide more info.

>
> <snip>
>
> Also, did you manage to get any backtrace from Ruby at all?  The Ruby VM
> should show a partial stack trace on SEGV (or at least attempt to, but
> it may not during shutdown).  Otherwise, you'd have to get gdb-able core
> dumps the old-fashioned way (ulimit -c unlimited) and show us (or
> ruby-core) the backtrace.

Nothing was emitted at the time -- what I sent before was all that
appeared in stderr.log.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Unicorn fails to install even though it's already installed and running
  @ 2012-12-06 21:54  2%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-12-06 21:54 UTC (permalink / raw)
  To: unicorn list

Mac Martine <99miles@gmail.com> wrote:
> Thanks,
> 
> I can install other gems just fine in the same way, so it doesn't seem
> like permissions then, right?
> 
> And disk space looks ok:

That's good to know.

> Here's the end of mkmf.log
> 
> have_macro: checking for SIZEOF_SIZE_T in ruby.h... -------------------- no
> 
> "gcc -I. -I/usr/lib/ruby/1.8/x86_64-linux -I.    -fno-strict-aliasing -g -g -O2  -fPIC     -c conftest.c"

Wait, are you now using a different version of Ruby?  Your original
message had 1.9 (via RVM)

> >>     ** [out :: mydomain.net] Results logged to /rails_apps/eg/production/shared/bundle/ruby/1.9.1/gems/unicorn-4.4.0/ext/unicorn_http/gem_make.out
> > 
> > Maybe gem_make.out will have more info than mkmf.log

Again ^^

However, scrutinizing your original email more:

> >>    "gcc -I/home/evergreen/.rvm/rubies/ruby-1.9.3-p194/include/ruby-1.9.1/x86_64-linux -I/home/eg/.rvm/rubies/ruby-1.9.3-p194/include/ruby-1.9.1/ruby/backward -I/home/eg/.rvm/rubies/ruby-1.9.3-p194/include/ruby-1.9.1 -I. -I/home/eg/.rvm/usr/include     -O3 -ggdb -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration  -fPIC   -c conftest.c"

I see both /home/evergreen/ and /home/eg/ in there; that looks
suspicious.

Did you perhaps copy anything in .rvm/ between home
directories or rename the home directory at some point after
RVM was installed?
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Issue starting unicorn with non-ActiveRecord Rails app
  @ 2012-12-05  9:56  2%     ` Peter Hall
  0 siblings, 0 replies; 200+ results
From: Peter Hall @ 2012-12-05  9:56 UTC (permalink / raw)
  To: unicorn list

> What's L68 of your config/unicorn.rb file look like? Check to see if you have copied/pasted a line that references ActiveRecord in the before_fork or after_fork blocks.
>
> A lot of unicorn examples have an ActiveRecord::Base.connection.disconnect! (with a defined?(ActiveRecord::Base) but that might not be present in your config) statement to ensure that AR connections aren't shared between the master and worker processes.


Aha! Thanks Daniel. =) I'm such an idiot =oP I totally missed that it
was in my own code - including the path on the top line of the trace
=|. The reason I presumed it wasn't is because I copied+pasted most of
the unicorn config, so I didn't remember *writing* any AR-specific
code. I guess it should work if I take that out!

The line actually turned out to be ActiveRecord::Base.establish_connection

Thanks again! *slaps forehead*
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: When a client terminates a connection
  2012-11-30 19:26  0%       ` Eric Wong
@ 2012-12-03 14:42  0%         ` Andrew Stewart
  0 siblings, 0 replies; 200+ results
From: Andrew Stewart @ 2012-12-03 14:42 UTC (permalink / raw)
  To: unicorn list

On 30 Nov 2012, at 20:26, Eric Wong wrote:
>> Nginx logged:
>> 
>>    x.xxx.xx.xx - - [27/Nov/2012:14:40:28 +0000] "POST /clients/2248 HTTP/1.1" 499 0 "https://example.com/clients/2248/edit" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
>>    x.xxx.xx.xx - - [27/Nov/2012:14:40:29 +0000] "POST /clients/2248 HTTP/1.1" 404 592 "https://example.com/companies/2248/edit" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
>> 
>> You can see the first request was terminated by the user.  Hot on its
>> heels the user tried again and got a 404.
> 
> Weird, and this is the only nginx instance that saw this request?

That's right, I only run a single nginx worker process.

> Also, according to your logs above, the two requests above came from
> different Referer pages, so perhaps there were more requests involved
> that hit a different nginx?

Bizarre as it sounds, that was a copy-paste mistake on my part.  The second referrer should read: "https://example.com/clients/2248/edit".

> Odd.  It's been a long time since I looked at Rails; but doesn't Rails
> log when a request _starts_?  Also, doesn't Rails log all DB queries?

I think Rails logs when a request starts, then logs some more when the request ends – but I'm not certain and I couldn't find the answer when I looked in the Rails source.  It must be there somewhere.

Rails doesn't log the DB queries in production, at least not by default.

> Or, by any chance, do you have query logging enabled in your DB to track
> this down?

Regrettably not.

It's a weird problem which I'd like to dismiss, but it's happened on two occasions now.  I'm running an old version of Rails (3.0.12) which may or may not be relevant.  Hmm.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: using unicorn with logging-rails gem
  2012-11-30 20:17  3%       ` Yoav Aner
@ 2012-11-30 20:55  0%         ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-11-30 20:55 UTC (permalink / raw)
  To: Yoav Aner; +Cc: unicorn list

Yoav Aner <yoav@kenhub.com> wrote:
> I simply added `Logging.reopen` to the after_fork block and now it
> looks like it's working again!

Awesome to know.

> Not sure I still fully (or even partially) understand what's going on
> there, but at least it's working.

Reading a manpage for the fork() syscall should help you understand
what's going on.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: using unicorn with logging-rails gem
  @ 2012-11-30 20:17  3%       ` Yoav Aner
  2012-11-30 20:55  0%         ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Yoav Aner @ 2012-11-30 20:17 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn list

Thanks Eric,

I think I might have found a solution :)

There was a hint in the config file I had for the logging-rails gem:

  if defined?(PhusionPassenger)
    PhusionPassenger.on_event(:starting_worker_process) do |forked|
      Logging.reopen if forked
    end
  end

I simply added `Logging.reopen` to the after_fork block and now it
looks like it's working again!

Not sure I still fully (or even partially) understand what's going on
there, but at least it's working.

Thanks again for your help.

Cheers
Yoav

On 30 November 2012 21:05, Eric Wong <normalperson@yhbt.net> wrote:
> Yoav Aner <yoav@kenhub.com> wrote:
>> Hi Eric,
>>
>> Thanks a bunch for getting back so quickly on this.
>>
>> I followed your suggestion and tried with `preload_app = false` and looks
>> like this seems to fix this problem! Any idea what can go wrong when it's
>> set to true or how I can try go about fixing this??
>
> preload_app is false by default because it's the most likely to work
> for all gems.
>
> I suspect there's a shared resource (file/socket) or some cached value
> that's initialized before fork and loses state after forking.
>
> It's a fairly common issue with database libraries
>
>> I haven't yet contacted the logging / logging-rails project. Perhaps that's
>> a good idea. Considering the gem did/does work fine on my dev environment
>> and with phusion passenger (and now it seems also with Unicorn, albeit with
>> preload_app = false), I wasn't sure whether the problem is with this gem or
>> elsewhere.
>>
>> Any tips on how to investigate this further or resolve this, or what
>> information I can give the gem maintainer(s) would be much appreciated.
>
> I would definitely contact the maintainer of logging/logging-rails
> on how to reinitialize any state after forking.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 3%]

* Re: using unicorn with logging-rails gem
  2012-11-30 19:15  0% ` Eric Wong
@ 2012-11-30 19:59  0%   ` Yoav Aner
       [not found]       ` <CA+mA6PgTGcyLy2Dua9a6gDWhRwsQLtHEyk17fhBE8DW1TkO5EA@mail.gmail.com>
  1 sibling, 0 replies; 200+ results
From: Yoav Aner @ 2012-11-30 19:59 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn list

Hi Eric,

Thanks a bunch for getting back so quickly on this.

I followed your suggestion and tried with `preload_app = false` and
looks like this seems to fix this problem! Any idea what can go wrong
when it's set to true or how I can try go about fixing this??

I haven't yet contacted the logging / logging-rails project. Perhaps
that's a good idea. Considering the gem did/does work fine on my dev
environment and with phusion passenger (and now it seems also with
Unicorn, albeit with preload_app = false), I wasn't sure whether the
problem is with this gem or elsewhere.

Any tips on how to investigate this further or resolve this, or what
information I can give the gem maintainer(s) would be much
appreciated.

Cheers
Yoav

On 30 November 2012 20:15, Eric Wong <normalperson@yhbt.net> wrote:
>
> Yoav Aner <yoav@kenhub.com> wrote:
> > Hope this is the right place to ask questions about unicorn. I'm not
> > on the mailing list though, so please CC my email address.
>
> No problem!
>
> > Since moving from phusion passenger to Unicorn, it seems like one
> > functionality of the logging-rails gem
> > (https://github.com/TwP/logging-rails) is (half) broken.
> >
> > Logging-rails supports an email appender, which aggregates log
> > messages (typically ERROR and above) and then emails them. However,
> > since moving to Unicorn we stopped getting those emails.
> >
> > The interesting thing however, is that when we USR2 signal our unicorn
> > and it restarts itself, then those emails somehow get flushed out and
> > get sent all at once... I tried sending USR1 signal to see if it has
> > something to do with log flushing, but this does not seem to make any
> > difference.
>
> USR1 only affects logging to regular files, so is unlikely to help
> in your case.
>
> Are you using preload_app true or false?  If true, perhaps some shared
> resource (socket or temporary file) isn't being handled correctly?
>
> > Any ideas how to investigate this / make it work again??
>
> I'm not familiar with the "logging" or "logging-rails" gems.
> I took a brief look at them but couldn't figure anything out,
> can you ask the author of those gems?
>
> If you do figure something out, please let the list know so it can
> possibly help others in the future.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: When a client terminates a connection
  2012-11-30  9:15  2%     ` Andrew Stewart
@ 2012-11-30 19:26  0%       ` Eric Wong
  2012-12-03 14:42  0%         ` Andrew Stewart
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2012-11-30 19:26 UTC (permalink / raw)
  To: unicorn list

Andrew Stewart <boss@airbladesoftware.com> wrote:
> On 30 Nov 2012, at 00:04, Eric Wong wrote:
> > Iñaki Baz Castillo <ibc@aliax.net> wrote:
> >> I don't understand why the application on top of the HTTP/TCP layer
> >> (so the Rails app) should be interrupted because the TCP connection is
> >> closed prematurely. The HTTP layer (which involves a single TCP
> >> connection for each HTTP request/response pair) is a layer below the
> >> application layer, should not disturb the Rails app IMHO.
> >> 
> >> Is this a design issue of Rails?
> > 
> > I suspect the the only possibility is when Rails is reading the
> > multipart response in a largish upload (any upload request which
> > can't fit in socket buffers).
> 
> On the three occasions I've had this problem it's been a tiny request.
> 
> The user clicks an ordinary Rails delete link.  This POSTs to a URL
> with two parameters: an authenticity token and the _method.
> 
> The corresponding destroy action looks like:
> 
>     def destroy
>       client = Client.find params[:id]  # 1  (ignores soft-deleted records)
>       client.soft_delete                # 2  (sets a deleted_at timestamp)
>       client.brands.each(&:destroy)     # 3  (a client has many brands)
>       redirect_to clients_path
>     end
> 
> Nginx logged:
> 
>     x.xxx.xx.xx - - [27/Nov/2012:14:40:28 +0000] "POST /clients/2248 HTTP/1.1" 499 0 "https://example.com/clients/2248/edit" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
>     x.xxx.xx.xx - - [27/Nov/2012:14:40:29 +0000] "POST /clients/2248 HTTP/1.1" 404 592 "https://example.com/companies/2248/edit" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
> 
> You can see the first request was terminated by the user.  Hot on its
> heels the user tried again and got a 404.

Weird, and this is the only nginx instance that saw this request?

Also, according to your logs above, the two requests above came from
different Referer pages, so perhaps there were more requests involved
that hit a different nginx?

> The 404 was triggered by step 1 above: the first request had
> successfully soft-deleted the client.  However the client's brands
> were still in the database.  The first request had executed steps 1
> and 2 but not step 3.
> 
> Unicorn didn't log anything.  Rails didn't log the first request but
> did log the second.

Odd.  It's been a long time since I looked at Rails; but doesn't Rails
log when a request _starts_?  Also, doesn't Rails log all DB queries?

Or, by any chance, do you have query logging enabled in your DB to track
this down?
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: using unicorn with logging-rails gem
  2012-11-30 17:20  2% using unicorn with logging-rails gem Yoav Aner
@ 2012-11-30 19:15  0% ` Eric Wong
  2012-11-30 19:59  0%   ` Yoav Aner
       [not found]       ` <CA+mA6PgTGcyLy2Dua9a6gDWhRwsQLtHEyk17fhBE8DW1TkO5EA@mail.gmail.com>
  0 siblings, 2 replies; 200+ results
From: Eric Wong @ 2012-11-30 19:15 UTC (permalink / raw)
  To: unicorn list; +Cc: Yoav Aner

Yoav Aner <yoav@kenhub.com> wrote:
> Hope this is the right place to ask questions about unicorn. I'm not
> on the mailing list though, so please CC my email address.

No problem!

> Since moving from phusion passenger to Unicorn, it seems like one
> functionality of the logging-rails gem
> (https://github.com/TwP/logging-rails) is (half) broken.
> 
> Logging-rails supports an email appender, which aggregates log
> messages (typically ERROR and above) and then emails them. However,
> since moving to Unicorn we stopped getting those emails.
> 
> The interesting thing however, is that when we USR2 signal our unicorn
> and it restarts itself, then those emails somehow get flushed out and
> get sent all at once... I tried sending USR1 signal to see if it has
> something to do with log flushing, but this does not seem to make any
> difference.

USR1 only affects logging to regular files, so is unlikely to help
in your case.

Are you using preload_app true or false?  If true, perhaps some shared
resource (socket or temporary file) isn't being handled correctly?

> Any ideas how to investigate this / make it work again??

I'm not familiar with the "logging" or "logging-rails" gems.
I took a brief look at them but couldn't figure anything out,
can you ask the author of those gems?

If you do figure something out, please let the list know so it can
possibly help others in the future.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* using unicorn with logging-rails gem
@ 2012-11-30 17:20  2% Yoav Aner
  2012-11-30 19:15  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Yoav Aner @ 2012-11-30 17:20 UTC (permalink / raw)
  To: mongrel-unicorn

Hope this is the right place to ask questions about unicorn. I'm not
on the mailing list though, so please CC my email address.

Since moving from phusion passenger to Unicorn, it seems like one
functionality of the logging-rails gem
(https://github.com/TwP/logging-rails) is (half) broken.

Logging-rails supports an email appender, which aggregates log
messages (typically ERROR and above) and then emails them. However,
since moving to Unicorn we stopped getting those emails.

The interesting thing however, is that when we USR2 signal our unicorn
and it restarts itself, then those emails somehow get flushed out and
get sent all at once... I tried sending USR1 signal to see if it has
something to do with log flushing, but this does not seem to make any
difference.

Any ideas how to investigate this / make it work again??

Cheers
Yoav
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: When a client terminates a connection
  @ 2012-11-30  9:15  2%     ` Andrew Stewart
  2012-11-30 19:26  0%       ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Andrew Stewart @ 2012-11-30  9:15 UTC (permalink / raw)
  To: unicorn list

On 30 Nov 2012, at 00:04, Eric Wong wrote:
> Iñaki Baz Castillo <ibc@aliax.net> wrote:
>> I don't understand why the application on top of the HTTP/TCP layer
>> (so the Rails app) should be interrupted because the TCP connection is
>> closed prematurely. The HTTP layer (which involves a single TCP
>> connection for each HTTP request/response pair) is a layer below the
>> application layer, should not disturb the Rails app IMHO.
>> 
>> Is this a design issue of Rails?
> 
> I suspect the the only possibility is when Rails is reading the
> multipart response in a largish upload (any upload request which
> can't fit in socket buffers).

On the three occasions I've had this problem it's been a tiny request.

The user clicks an ordinary Rails delete link.  This POSTs to a URL with two parameters: an authenticity token and the _method.

The corresponding destroy action looks like:

    def destroy
      client = Client.find params[:id]  # 1  (ignores soft-deleted records)
      client.soft_delete                # 2  (sets a deleted_at timestamp)
      client.brands.each(&:destroy)     # 3  (a client has many brands)
      redirect_to clients_path
    end

Nginx logged:

    x.xxx.xx.xx - - [27/Nov/2012:14:40:28 +0000] "POST /clients/2248 HTTP/1.1" 499 0 "https://example.com/clients/2248/edit" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
    x.xxx.xx.xx - - [27/Nov/2012:14:40:29 +0000] "POST /clients/2248 HTTP/1.1" 404 592 "https://example.com/companies/2248/edit" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"

You can see the first request was terminated by the user.  Hot on its heels the user tried again and got a 404.

The 404 was triggered by step 1 above: the first request had successfully soft-deleted the client.  However the client's brands were still in the database.  The first request had executed steps 1 and 2 but not step 3.

Unicorn didn't log anything.  Rails didn't log the first request but did log the second.

I understand the steps to mitigate the problem...but I remain confused about why the Rails app was interrupted because the TCP connection was closed prematurely.

Yours,
Andrew Stewart
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Combating nginx 499 HTTP responses during flash traffic scenario
  2012-11-29 15:52  1%                     ` Tom Burns
@ 2012-11-29 20:41  0%                       ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-11-29 20:41 UTC (permalink / raw)
  To: unicorn list

Tom Burns <tom.burns@jadedpixel.com> wrote:
> So we just finished the US Black Friday / Cyber Monday weekend running
> unicorn forked with the last version of the patch I had sent you.  It
> worked splendidly and helped us handle huge flash sales without
> increased response time over the weekend.
> 
> Whereas in previous flash traffic scenarios we would see the number of
> HTTP 499 responses grow past the number of real HTTP 200 responses,
> over the weekend we saw no growth in 499s during flash sales.
> 
> Unexpectedly the patch also helped us ward off a DoS attack where the
> attackers were disconnecting immediately after making a request.

That is absolutely wonderful to hear.  I've amended your commit message
with the above quoted portion.

> I've attached the patch again, with your last comments addressed.  Let
> me know if there's anything else.
> 
> Thanks again for your help in this matter.

Thank _you_ for the patch, documentation and most importantly:
testing with real traffic.

I fixed up some minor line-wrapping, signed-off, and added your
quote above to the commit message.  Pushed as
commit 5c700fc2cf398848ddcf71a2aa3f0f2a6563e87b
to git://bogomips.org/unicorn.git

I'll tag and push a 4.5.0.preview1 gem soon
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: Combating nginx 499 HTTP responses during flash traffic scenario
  @ 2012-11-29 15:52  1%                     ` Tom Burns
  2012-11-29 20:41  0%                       ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Tom Burns @ 2012-11-29 15:52 UTC (permalink / raw)
  To: unicorn list

On Tue, Nov 6, 2012 at 4:23 PM, Eric Wong <normalperson@yhbt.net> wrote:
>
> Tom Burns <tom.burns@jadedpixel.com> wrote:
> > On Mon, Nov 5, 2012 at 6:48 AM, Eric Wong <normalperson@yhbt.net> wrote:
> > > We can wait until you can report on how this change improves your
> > > situation before fleshing this out.  Real-world results are always
> > > good to have :>
> >
> > Agreed.
> >
> > We're going to be testing this this week so I should have some results
> > to share by the weekend.  We only experience the problem during major
> > sales so it may or may not manifest without our traffic generation
> > tool.
>
> Holidays are coming up, should be lots of traffic :)

So we just finished the US Black Friday / Cyber Monday weekend running
unicorn forked with the last version of the patch I had sent you.  It
worked splendidly and helped us handle huge flash sales without
increased response time over the weekend.

Whereas in previous flash traffic scenarios we would see the number of
HTTP 499 responses grow past the number of real HTTP 200 responses,
over the weekend we saw no growth in 499s during flash sales.

Unexpectedly the patch also helped us ward off a DoS attack where the
attackers were disconnecting immediately after making a request.

I've attached the patch again, with your last comments addressed.  Let
me know if there's anything else.

Thanks again for your help in this matter.

Cheers,
Tom

>From 89c8c51355c75b0196469812580443fba390632e Mon Sep 17 00:00:00 2001
From: Tom Burns <tom.burns@jadedpixel.com>
Date: Tue, 30 Oct 2012 16:22:21 -0400
Subject: [PATCH] Begin writing HTTP request headers early to detect
 disconnected clients

This patch checks incoming connections and avoids calling the application
if the connection has been closed.

It works by sending the beginning of the HTTP response before calling
the application to see if the socket can successfully be written to.

By enabling this feature users can avoid wasting application rendering
time only to find the connection is closed when attempting to write, and
throwing out the result.

When a client disconnects while being queued or processed, Nginx will log
HTTP response 499 but the application will log a 200.

Enabling this feature will minimize the time window during which the problem
can arise.

The feature is disabled by default and can be enabled by adding
'check_client_connection true' to the unicorn config.
---
 examples/unicorn.conf.rb         |    6 ++++++
 ext/unicorn_http/unicorn_http.rl |    4 +++-
 lib/unicorn/configurator.rb      |   25 +++++++++++++++++++++++++
 lib/unicorn/const.rb             |   12 ++++++++----
 lib/unicorn/http_request.rb      |   19 +++++++++++++++++++
 lib/unicorn/http_response.rb     |    5 +++--
 lib/unicorn/http_server.rb       |   22 ++++++++++++++++++++--
 test/exec/test_exec.rb           |    7 ++++++-
 test/unit/test_configurator.rb   |   24 ++++++++++++++++++++++++
 9 files changed, 114 insertions(+), 10 deletions(-)

diff --git a/examples/unicorn.conf.rb b/examples/unicorn.conf.rb
index 0238043..1f4c9c0 100644
--- a/examples/unicorn.conf.rb
+++ b/examples/unicorn.conf.rb
@@ -46,6 +46,12 @@ preload_app true
 GC.respond_to?(:copy_on_write_friendly=) and
   GC.copy_on_write_friendly = true

+# Enable this flag to have unicorn test client connections by writing the
+# beginning of the HTTP headers before calling the application.  This
+# prevents calling the application for connections that have disconnected
+# while queued.
+check_client_connection false
+
 before_fork do |server, worker|
   # the following is highly recomended for Rails + "preload_app true"
   # as there's no need for the master process to hold a connection
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 96dcf83..1a8003f 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -115,7 +115,7 @@ struct http_parser {
   } len;
 };

-static ID id_clear, id_set_backtrace;
+static ID id_clear, id_set_backtrace, id_response_start_sent;

 static void finalize_header(struct http_parser *hp);

@@ -626,6 +626,7 @@ static VALUE HttpParser_clear(VALUE self)

   http_parser_init(hp);
   rb_funcall(hp->env, id_clear, 0);
+  rb_ivar_set(self, id_response_start_sent, Qfalse);

   return self;
 }
@@ -1031,6 +1032,7 @@ void Init_unicorn_http(void)
   SET_GLOBAL(g_http_connection, "CONNECTION");
   id_clear = rb_intern("clear");
   id_set_backtrace = rb_intern("set_backtrace");
+  id_response_start_sent = rb_intern("@response_start_sent");
   init_unicorn_httpdate();
 }
 #undef SET_GLOBAL
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index 89cbf5c..9752cdd 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -45,6 +45,7 @@ class Unicorn::Configurator
       },
     :pid => nil,
     :preload_app => false,
+    :check_client_connection => false,
     :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
     :client_body_buffer_size => Unicorn::Const::MAX_BODY,
     :trust_x_forwarded => true,
@@ -96,6 +97,18 @@ class Unicorn::Configurator
     if ready_pipe = RACKUP.delete(:ready_pipe)
       server.ready_pipe = ready_pipe
     end
+    if set[:check_client_connection]
+      set[:listeners].each do |address|
+        if set[:listener_opts][address][:tcp_nopush] == true
+          raise ArgumentError,
+            "check_client_connection is incompatible with tcp_nopush:true"
+        end
+        if set[:listener_opts][address][:tcp_nodelay] == true
+          raise ArgumentError,
+            "check_client_connection is incompatible with tcp_nodelay:true"
+        end
+      end
+    end
     set.each do |key, value|
       value == :unset and next
       skip.include?(key) and next
@@ -454,6 +467,18 @@ class Unicorn::Configurator
     set_int(:client_body_buffer_size, bytes, 0)
   end

+  # When enabled, unicorn will check the client connection by writing
+  # the beginning of the HTTP headers before calling the application.
+  #
+  # This will prevent calling the application for clients who have
+  # disconnected while their connection was queued.
+  #
+  # This option cannot be used in conjunction with tcp_nodelay or
+  # tcp_nopush.
+  def check_client_connection(bool)
+    set_bool(:check_client_connection, bool)
+  end
+
   # Allow redirecting $stderr to a given path.  Unlike doing this from
   # the shell, this allows the unicorn process to know the path its
   # writing to and rotate the file if it is used for logging.  The
diff --git a/lib/unicorn/const.rb b/lib/unicorn/const.rb
index b3d8d71..f0c4c12 100644
--- a/lib/unicorn/const.rb
+++ b/lib/unicorn/const.rb
@@ -29,12 +29,16 @@ module Unicorn::Const

   # :stopdoc:
   # common errors we'll send back
-  ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
-  ERROR_414_RESPONSE = "HTTP/1.1 414 Request-URI Too Long\r\n\r\n"
-  ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
-  ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
+  ERROR_400_RESPONSE = "400 Bad Request\r\n\r\n"
+  ERROR_414_RESPONSE = "414 Request-URI Too Long\r\n\r\n"
+  ERROR_413_RESPONSE = "413 Request Entity Too Large\r\n\r\n"
+  ERROR_500_RESPONSE = "500 Internal Server Error\r\n\r\n"
+
   EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
+  EXPECT_100_RESPONSE_SUFFIXED = "100 Continue\r\n\r\nHTTP/1.1 "

+  HTTP_RESPONSE_START = ['HTTP', '/1.1 ']
   HTTP_EXPECT = "HTTP_EXPECT"
+
   # :startdoc:
 end
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index a0435d6..79ead2e 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -22,11 +22,14 @@ class Unicorn::HttpParser

   NULL_IO = StringIO.new("")

+  attr_accessor :response_start_sent
+
   # :stopdoc:
   # A frozen format for this is about 15% faster
   REMOTE_ADDR = 'REMOTE_ADDR'.freeze
   RACK_INPUT = 'rack.input'.freeze
   @@input_class = Unicorn::TeeInput
+  @@check_client_connection = false

   def self.input_class
     @@input_class
@@ -35,6 +38,15 @@ class Unicorn::HttpParser
   def self.input_class=(klass)
     @@input_class = klass
   end
+
+  def self.check_client_connection
+    @@check_client_connection
+  end
+
+  def self.check_client_connection=(bool)
+    @@check_client_connection = bool
+  end
+
   # :startdoc:

   # Does the majority of the IO processing.  It has been written in
@@ -70,6 +82,13 @@ class Unicorn::HttpParser
       # an Exception thrown from the parser will throw us out of the loop
       false until add_parse(socket.kgio_read!(16384))
     end
+
+    # detect if the socket is valid by writing a partial response:
+    if @@check_client_connection && headers?
+      @response_start_sent = true
+      Unicorn::Const::HTTP_RESPONSE_START.each { |c| socket.write(c) }
+    end
+
     e[RACK_INPUT] = 0 == content_length ?
                     NULL_IO : @@input_class.new(socket, self)
     e.merge!(DEFAULTS)
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index b781e20..9c2bacc 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -18,11 +18,12 @@ module Unicorn::HttpResponse
   CRLF = "\r\n"

   # writes the rack_response to socket as an HTTP response
-  def http_response_write(socket, status, headers, body)
+  def http_response_write(socket, status, headers, body,
response_start_sent=false)
     status = CODES[status.to_i] || status

+    http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
     if headers
-      buf = "HTTP/1.1 #{status}\r\n" \
+      buf = "#{http_response_start}#{status}\r\n" \
             "Date: #{httpdate}\r\n" \
             "Status: #{status}\r\n" \
             "Connection: close\r\n"
diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 13df55a..8729830 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -17,6 +17,7 @@ class Unicorn::HttpServer
                 :listener_opts, :preload_app,
                 :reexec_pid, :orig_app, :init_listeners,
                 :master_pid, :config, :ready_pipe, :user
+
   attr_reader :pid, :logger
   include Unicorn::SocketHelper
   include Unicorn::HttpResponse
@@ -355,6 +356,14 @@ class Unicorn::HttpServer
     Unicorn::HttpParser.trust_x_forwarded = bool
   end

+  def check_client_connection
+    Unicorn::HttpRequest.check_client_connection
+  end
+
+  def check_client_connection=(bool)
+    Unicorn::HttpRequest.check_client_connection = bool
+  end
+
   private

   # wait for a signal hander to wake us up and then consume the pipe
@@ -524,23 +533,32 @@ class Unicorn::HttpServer
       Unicorn.log_error(@logger, "app error", e)
       Unicorn::Const::ERROR_500_RESPONSE
     end
+    msg = "HTTP/1.1 #{msg}" unless @request.response_start_sent
     client.kgio_trywrite(msg)
     client.close
     rescue
   end

+  def expect_100_response
+    if @request.response_start_sent
+      Unicorn::Const::EXPECT_100_RESPONSE_SUFFIXED
+    else
+      Unicorn::Const::EXPECT_100_RESPONSE
+    end
+  end
+
   # once a client is accepted, it is processed in its entirety here
   # in 3 easy steps: read request, call app, write app response
   def process_client(client)
     status, headers, body = @app.call(env = @request.read(client))

     if 100 == status.to_i
-      client.write(Unicorn::Const::EXPECT_100_RESPONSE)
+      client.write(expect_100_response)
       env.delete(Unicorn::Const::HTTP_EXPECT)
       status, headers, body = @app.call(env)
     end
     @request.headers? or headers = nil
-    http_response_write(client, status, headers, body)
+    http_response_write(client, status, headers, body,
@request.response_start_sent)
     client.shutdown # in case of fork() in Rack app
     client.close # flush and uncork socket immediately, no keepalive
   rescue => e
diff --git a/test/exec/test_exec.rb b/test/exec/test_exec.rb
index b30a3d6..1cee2b7 100644
--- a/test/exec/test_exec.rb
+++ b/test/exec/test_exec.rb
@@ -871,13 +871,14 @@ EOF
     wait_for_death(pid)
   end

-  def hup_test_common(preload)
+  def hup_test_common(preload, check_client=false)
     File.open("config.ru", "wb") { |fp| fp.syswrite(HI.gsub("HI", '#$$')) }
     pid_file = Tempfile.new('pid')
     ucfg = Tempfile.new('unicorn_test_config')
     ucfg.syswrite("listen '#@addr:#@port'\n")
     ucfg.syswrite("pid '#{pid_file.path}'\n")
     ucfg.syswrite("preload_app true\n") if preload
+    ucfg.syswrite("check_client_connection true\n") if check_client
     ucfg.syswrite("stderr_path 'test_stderr.#$$.log'\n")
     ucfg.syswrite("stdout_path 'test_stdout.#$$.log'\n")
     pid = xfork {
@@ -942,6 +943,10 @@ EOF
     hup_test_common(false)
   end

+  def test_check_client_hup
+    hup_test_common(false, true)
+  end
+
   def test_default_listen_hup_holds_listener
     default_listen_lock do
       res, pid_path = default_listen_setup
diff --git a/test/unit/test_configurator.rb b/test/unit/test_configurator.rb
index c19c427..b9c22d7 100644
--- a/test/unit/test_configurator.rb
+++ b/test/unit/test_configurator.rb
@@ -139,6 +139,30 @@ class TestConfigurator < Test::Unit::TestCase
     end
   end

+  def test_check_client_connection
+    tmp = Tempfile.new('unicorn_config')
+    test_struct = TestStruct.new
+    tmp.syswrite("check_client_connection true\n")
+
+    assert_nothing_raised do
+      Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
+    end
+
+    assert test_struct.check_client_connection
+  end
+
+  def test_check_client_connection_with_tcp_bad
+    tmp = Tempfile.new('unicorn_config')
+    test_struct = TestStruct.new
+    listener = "127.0.0.1:12345"
+    tmp.syswrite("check_client_connection true\n")
+    tmp.syswrite("listen '#{listener}', :tcp_nopush => true\n")
+
+    assert_raises(ArgumentError) do
+      Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
+    end
+  end
+
   def test_after_fork_proc
     test_struct = TestStruct.new
     [ proc { |a,b| }, Proc.new { |a,b| }, lambda { |a,b| } ].each do |my_proc|
-- 
1.7.10.2 (Apple Git-33)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply related	[relevance 1%]

* Testing Unicorn on Rubinius
@ 2012-11-13  0:34  2% mike
  0 siblings, 0 replies; 200+ results
From: mike @ 2012-11-13  0:34 UTC (permalink / raw)
  To: mongrel-unicorn

Hey Eric,

I do have the isolate gem installed. In a number of gemsets as it
were. Here is a gem list:

mike@sm-mike-thinkpad:~/workspace/rubinius_testing/unicorn-4.4.0$ gem list

*** LOCAL GEMS ***

bundler (1.2.1)
isolate (3.2.2)
rack (1.4.1)
rake (0.9.2.2)
rubygems-bundler (1.1.0)
rvm (1.11.3.5)

Here is a "find" for isolate running against my .rvm directory:

mike@sm-mike-thinkpad:~/workspace/rubinius_testing/unicorn-4.4.0$ find /home/mike/.rvm/ -name isolate-3.2.2
/home/mike/.rvm/gems/rbx-2.0.testing@global/doc/isolate-3.2.2
/home/mike/.rvm/gems/rbx-2.0.testing@global/gems/isolate-3.2.2
/home/mike/.rvm/gems/rbx-2.0.testing/doc/isolate-3.2.2
/home/mike/.rvm/gems/rbx-2.0.testing/gems/isolate-3.2.2
/home/mike/.rvm/gems/rbx-2.0.testing@rubinius_testing/doc/isolate-3.2.2
/home/mike/.rvm/gems/rbx-2.0.testing@rubinius_testing/gems/isolate-3.2.2

and lastly, here is the setting I have for the "RUBYLIB" variable in my local.mk:

RUBYLIB=/home/mike/.rvm/gems/rbx-2.0.testing/

(FYI: this is injected on line 8 of your sample local.mk that ships with the project)

If I run irb, I cannot require 'isolate' without requiring 'rubygems' even with it in all these places. I assume there is some kind of path / inclusion issue I am facing here and I don't quite know how to go about getting around it. Perhaps I have RUBYLIB set wrong, or I need to try to approach this completely differently and not from rvm at all.

Thank you so much for all your efforts in trying to help me get going on this, they are very appreciated.

Also please let me know if there is a log output, setting/configuration file or something else that having access to/knowledge of would help.

Thanks again,

-Mike Thompson
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: select(): Interrupted system call from curb when stopping unicorn
  2012-11-07 16:42  2% select(): Interrupted system call from curb when stopping unicorn Graham Bleach
@ 2012-11-07 21:11  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-11-07 21:11 UTC (permalink / raw)
  To: unicorn list

Graham Bleach <graham@darkskills.org.uk> wrote:
> Hi,
> 
> We've just migrated one of our rails applications from nginx/passenger
> running on REE 1.8.7 Ubuntu 8.04 to unicorn running on MRI 1.9.3 on
> Ubuntu 10.04. The app makes a number of calls to internal services
> using curb.
> 
> Our deployment script stops unicorn by sending SIGQUIT to the unicorn
> master, sleeps for a few seconds to ensure that HAProxy has taken the
> node out of service and then starts unicorn again.
> 
> However, since the migration, we've started seeing a few errors on
> each deploy, when the workers receive the QUIT from the master:
> 
>  ERROR (  XXX) {"exception":{"class":"RuntimeError","message":"select():
> Interrupted system
> call","backtrace":["/path/bundle/ruby/1.9.1/gems/curb-0.7.18/lib/curl/easy.rb:39:in
> `perform'","/path/bundle/ruby/1.9.1/gems/curb-0.7.18/lib/curl/easy.rb:39:in
> `perform'","/path/bundle/ruby/1.9.1/gems/songkick-transport-0.1.4/lib/songkick/transport/curb.rb:50:in
> ...
> 
> If we're reading this correctly, curb is inside a select() call, gets
> interrupted by the QUIT signal and doesn't retry the select() and the
> process terminates, causing an error for the unfortunate end user.
> 
> Our options for stopping our users seeing error pages seem to be:
> 
> 1) Patch curb to retry the select() if errno = EINTR as per
> https://github.com/taf2/curb/issues/117

Taking a quick look at the curb source, I see no reason it implements
curb_select() itself instead of just using rb_thread_select() (or
rb_thread_fd_select(), the latter is recommended for new Rubies as
it handles high-numbered FDs better).

Any rb_thread*select() function implemented by Ruby is already aware of
multithreading and (for 1.9) calls rb_thread_blocking_region()
internally.

For now, you can probably just: #undef HAVE_RB_THREAD_BLOCKING_REGION

(Feel free to forward my comments to the curb folks, I don't like
 logging into websites)

> We're not confident enough in our ability to do this properly, but
> it's probably the most correct way to solve this.

Practice :)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* select(): Interrupted system call from curb when stopping unicorn
@ 2012-11-07 16:42  2% Graham Bleach
  2012-11-07 21:11  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Graham Bleach @ 2012-11-07 16:42 UTC (permalink / raw)
  To: mongrel-unicorn

Hi,

We've just migrated one of our rails applications from nginx/passenger
running on REE 1.8.7 Ubuntu 8.04 to unicorn running on MRI 1.9.3 on
Ubuntu 10.04. The app makes a number of calls to internal services
using curb.

Our deployment script stops unicorn by sending SIGQUIT to the unicorn
master, sleeps for a few seconds to ensure that HAProxy has taken the
node out of service and then starts unicorn again.

However, since the migration, we've started seeing a few errors on
each deploy, when the workers receive the QUIT from the master:

 ERROR (  XXX) {"exception":{"class":"RuntimeError","message":"select():
Interrupted system
call","backtrace":["/path/bundle/ruby/1.9.1/gems/curb-0.7.18/lib/curl/easy.rb:39:in
`perform'","/path/bundle/ruby/1.9.1/gems/curb-0.7.18/lib/curl/easy.rb:39:in
`perform'","/path/bundle/ruby/1.9.1/gems/songkick-transport-0.1.4/lib/songkick/transport/curb.rb:50:in
...

If we're reading this correctly, curb is inside a select() call, gets
interrupted by the QUIT signal and doesn't retry the select() and the
process terminates, causing an error for the unfortunate end user.

Our options for stopping our users seeing error pages seem to be:

1) Patch curb to retry the select() if errno = EINTR as per
https://github.com/taf2/curb/issues/117

We're not confident enough in our ability to do this properly, but
it's probably the most correct way to solve this.

2) Do something to make sure that requests have all completed before
sending the QUIT.

For example, we could change our deployment to make the health check
url that HAProxy polls start returning a non-200 return code, wait
until all connections have completed and then send the QUIT.

The main issue with this is that short of polling the HAProxy status
page, we're not entirely sure how to verify that all connections have
completed.

Does anyone have any suggested solutions or comments?

Regards,
Graham
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Combating nginx 499 HTTP responses during flash traffic scenario
  @ 2012-10-30 21:37  2%       ` Eric Wong
    0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2012-10-30 21:37 UTC (permalink / raw)
  To: unicorn list

Tom Burns <tom.burns@jadedpixel.com> wrote:
> On Mon, Oct 29, 2012 at 5:53 PM, Eric Wong <normalperson@yhbt.net> wrote:
> > Maybe this gross hack can work for you guys.  It writes the first
> > chunk of the HTTP response header out immediately after reading
> > the request headers, and sends the rest once it gets the status...
> 
> I tested the patch today and it does what we want, dropping
> connections before passing them to the rails app when the client has
> already disconnected.
> 
> I also benchmarked the patch to see if it had a negligible performance
> hit and it did not.  The cost was absorbed by the variation in speed
> of the other components in the stack (nginx & rails).

Good to know.  Thanks for reporting back.

> I noticed on my computer applying the patch breaks
> test_rack_lint_big_put in the unicorn test suite.  This may be just my
> issue as the test suite does not run cleanly anyways if I checkout
> origin/master.

The test suite in master should be passing cleanly, at least on a
GNU/Linux machine...

Yes, this hacky patch breaks some tests/internals and screws up
exception error/reporting badly.

> We'd prefer to not have to fork unicorn for this change.  How do you
> feel about merging this or a derivative thereof?  I can develop this
> further if you can send me what you'd want.

Sure thing!

I strongly prefer this to be optional behavior and off-by-default.

Also, I'm nearly certain two write()s is all that's needed and the
each_char is unnecessary syscall/packet overhead.

This will trigger bugs in badly-written HTTP clients/parsers (probably
some test suites :x) which assume a header comes in a single read().

For TCP users, I believe this requires both tcp_nodelay:false and
tcp_nopush:false to be completely reliable, so we need to enforce that
those if this option is in effect.  The current each_char usage is
probably masking the tcp_nodelay:false requirement.

Thanks again.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Is a client uploading a file a slow client from unicorn's point of view?
  2012-10-09 23:54  2%         ` Eric Wong
@ 2012-10-10  6:59  2%           ` Laas Toom
  0 siblings, 0 replies; 200+ results
From: Laas Toom @ 2012-10-10  6:59 UTC (permalink / raw)
  To: unicorn list


On 10.10.2012, at 2:54, Eric Wong <normalperson@yhbt.net> wrote:

> Basically, it's the message passing concurrency model vs shared
> memory+locking.  There's no clear winner, it just depends on the
> situation.  99% of the time I get away with keeping everything on
> one machine :)

Exactly.
For now we have nginx and unicorn on the same machine, both interacting w/ the storage  over network, so there is no difference if the initial write is done by Rails or Nginx.

Later, Resque will pick up the background processing task, which again could be on totally separate machine(s).

And in the most complex scenario - there could be multiple Nginx, multiple Unicorn and multiple Resque nodes, each doing it's part. In such a situation, the Unicorn node can skip the network I/O, if it can base it's upload validation solely on filename and attrs, disregarding the file data itself.

Best,
Laas
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Is a client uploading a file a slow client from unicorn's point of view?
  @ 2012-10-09 23:54  2%         ` Eric Wong
  2012-10-10  6:59  2%           ` Laas Toom
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2012-10-09 23:54 UTC (permalink / raw)
  To: unicorn list

Laas Toom <laas@toom.ee> wrote:
> On 09.10.2012, at 23:03, Eric Wong <normalperson@yhbt.net> wrote:
> > Laas Toom <laas@toom.ee> wrote:
> >> Afterwards it will only handle out the file location and Rails can
> >> complete it's work a lot faster, freeing up workers.
> >> 
> >> Unicorn won't even see the file and Rails has the responsibility to
> >> delete the file if it's invalid.
> > 
> > I think the only problem with this approach is it won't work well on
> > setups where nginx is on separate machines from unicorn.  Shared
> > storage would be required, but that ends up adding to network I/O,
> > too...
> 
> But won't (almost) the same network I/O be evident anyway, because of
> nginx transferring the data to Unicorn over network (as they are on
> different machines)?

It depends on your shared storage implementation.

It'll likely be a win if the shared storage is on the same server as
nginx (but that might mean you can only have one nginx server).  But I
think it'll be a loss if there needs to be multiple nginx servers (and
multiple unicorn servers)...

* With nginx_upload_module + shared storage:

nginx server  ------ shared storage -------- unicorn server
------------------------------------------------------------------
1. sequential write to shared storage

2. file could remain cached              do processing on file parts
   on nginx server, even if              remotely, network latency
   we'll never need to read              from reads (and possible cache
   it again                              coherency checks on rereads)

3. unlink on error                       unlink/rename/copy on success


* Without nginx_upload_module:

   nginx server -------------------------- unicorn server
------------------------------------------------------------------

1. sequential write of tempfile
2. sequential read of tempfile ----------> sequential write by Rack
3. unlink (able to free up cache)          do processing on file locally
                                           (no remote cache coherency
                                           checks)

The benefit of this approach is there's only 2 components interacting at
any one time, and the network costs are paid in full up front

Basically, it's the message passing concurrency model vs shared
memory+locking.  There's no clear winner, it just depends on the
situation.  99% of the time I get away with keeping everything on
one machine :)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Unused Unicorn processes
  @ 2012-08-22 18:16  2%     ` Konstantin Gredeskoul
  0 siblings, 0 replies; 200+ results
From: Konstantin Gredeskoul @ 2012-08-22 18:16 UTC (permalink / raw)
  To: unicorn list

Thanks Eric!  Appreciate well thought out answers.

We are actually using Rainbows also, in an project where long
server-side HTTP calls are part of the application design and are
necessary, and it's working out really well.

Our main web app mostly spends it's time in CPU cycles serving web
pages (about 80% of the request time), and 20% in database operations.

Thanks again
K

On Tue, Aug 21, 2012 at 2:11 AM, Eric Wong <normalperson@yhbt.net> wrote:
> Konstantin Gredeskoul <kig@wanelo.com> wrote:
>> Greetings!
>>
>> I have a question on optimal # of unicorn worker processes.
>>
>> We are running Unicorn 4.3.1 + Rails 3.2.6 (without threading), on
>> ruby 1.9.3-p194, hosted on SmartOS/Joyent.
>>
>> At the moment, unicorns are configured to start 30 worker processes. I
>> know this is a lot, and I am going to reduce this number. But in
>> trying to figure out what is a more appropriate number of workers to
>> run, I noticed something interesting that I couldn't explain.
>>
>> If I look at the process table on each machine (see top output below),
>> I notice that some unicorn processes are heavily used (and have
>> accumulated longer CPU times, as well as have grown their RAM usage
>> since boot time), but other processes (at the bottom of the top
>> output) appear to potentially not having been used at all.  There are
>> several processes with RSS size of 143Mb, which I believe is unicorn
>> size before it processes any requests.
>>
>> What I am gathering from this, is that only 16 unicorn processes are
>> actually processing requests, while the rest are just sitting there
>> idle.
>>
>> Is this expected behavior?
>
> It's not _unexpected_ if your load is low.  The OS scheduler does the
> load balancing itself as unicorn uses non-blocking accept().  The
> scheduler may choose busier processes because they're likely to be
> hotter in cache/memory.
>
> So you're probably getting better performance than if you had perfectly
> balanced traffic.
>
> Another possible explanation is something is killing some workers
> (timeout, or crash buts, check stderr logs to confirm).  But even in a
> situation where workers never die, it's perfectly normal to see workers
> that never/rarely serve traffic.
>
>> This Joyent SmartMachine can burst up to 8 cores. Given that our web
>> requests spend only 80% of their time in ruby, I figured we could run
>> 10 unicorn processes for maximum efficiency.  However seeing that 16
>> are actually used I am curious whether 16 is actually a better number.
>
> It depends what your app is waiting on.
>
> The only rule is your machine should not be swap thrashing under
> peak load.
>
> Extra workers won't be detrimental as long as you:
> 1) have enough memory
> 2) have enough backend resources (e.g DB connections)
>
> I know of deployments that run 30 processes/core because the app spends
> much time waiting on slow HTTP/DB requests[1]
>
> But if your app is completely bound by resources local to a machine (no
> external DB/HTTP/memcached/redis/etc requests), then a 1:1 worker:core
> (or even 1:1 worker:disk) relationship will work, too.
>
>
>
> [1] Arguably a multi-threaded or non-blocking server such as Rainbows!
> would be most efficient of machine resources if you're waiting on HTTP
> calls, but the developers decided human time was more important and
> did not want to worry about thread-safety.
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying



-- 

Konstantin Gredeskoul
CTO :: Wanelo Inc
cell: (415) 265 1054
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* SIGUSR2 from symlinked working directory
@ 2012-08-07  7:26  1% Danial Pearce
  0 siblings, 0 replies; 200+ results
From: Danial Pearce @ 2012-08-07  7:26 UTC (permalink / raw)
  To: mongrel-unicorn

Hi all,

I am battling with my unicorn deploy scripts at the moment. First it
was the "sleep" thing after sending a SIGUSR2 which I read about on
the mailing list just now so it seems I'm not the only one there. The
default of "2" was rarely working for me, upping that to 5 helps out
but still not ideal as it still fails occasionally, e.g. you might
sleep for the perfect amount of time to the point where the
unicorn.pid file disappears for a very short period of time.

Anyway, my main issue is that I am keeping 5 deploys, capistrano style
where it symlinks the current deploy as "current". I have made sure my
unicorn init scripts and configs all only use this "current" path,
nothing refers directly to the timestamped path invented by the deploy
system (chef in my case).

Yet, I am see that when I send SIGUSR2 to the unicorn master, the
following ends up in the log:

I, [2012-08-07T06:54:34.716138 #26569]  INFO -- : executing
["/home/my_user/my_app/releases/20120807030020/gems/ruby/1.9.1/bin/unicorn_rails",
"-D", "-E", "production", "-c",
"/home/my_user/my_app/current/config/unicorn.rb",
{7=>#<Kgio::TCPServer:fd 7>}] (in
/home/my_user/my_app/releases/20120807065124)

I presume here the first path is the current running unicorn and the
second path is the supposed working directory. Neither of these are
correct, since it has gone and followed the "current" symlink when
executing that unicorn the first time, as well as when resolving the
working directory that second time. (Since you will notice they are 2
different releases). The first incorrect path is from when I first did
an initial start of the unicorn_rails command.

I say "incorrect path", those paths do in fact exist, for now. The
issue is when that first release that I used to "start" the unicorn
process becomes older than the 5th release and is deleted from disk.
Then the SIGUSR2 will fail saying it can't find that path to restart
the app. I then need to jump in and send a QUIT manually and start it
again.

I'm sure heaps of people must be doing this, so clearly I'm doing
something wrong, I just can't figure out what it is. As mentioned,
I've triple checked and I am not referencing the "20120807030020" path
anywhere, according to grep anyway. My config/init script most
definitely just reference /home/my_user/my_app/current.

Furthmore, this one just weirds me out, the "gems" path is also a
symlink, to /home/my_user/my_app/shared/gems, so why would unicorn be
following the symlink on /home/my_user/my_app/current but not on
/home/my_user/my_app/current/gems?

I'm not subscribed to this mailing list, so if you could CC me in
response that would be super. Although I will be checking the archives
anyway to make sure I don't miss a possible solution.


regards,
Danial
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 1%]

* Re: Any signal other than -9 causes full CPU utilization by master unicorn process on FreeBSD
  2012-07-17 21:23  2%     ` Mark Mccraw
@ 2012-07-17 22:17  0%       ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-07-17 22:17 UTC (permalink / raw)
  To: unicorn list

Mark Mccraw <Mark.Mccraw@sas.com> wrote:
> On Jul 17, 2012, at 7:56 AM, Mark McCraw wrote:
> > We're upgrading now to see what happens.  I'm so glad you knew about
> > this.  There's no telling how long it would have taken me to
> > question the ruby interpreter implementation, and since it's
> > FreeBSD, I never would have found it by googling.  Thanks for hours
> > (days?) of my life back.
> 
> Just to follow up and close out the thread - Eric's recollection was
> spot on.  We upgraded ruby on our FreeBSD server to the latest thing,
> and the problem completely disappeared.  Thanks again!

Thanks for confirming this fix!

Fwiw, the Ruby core team probably uses/tests on GNU/Linux more than any
other platform.  Bugs on less common development platforms (especially
w.r.t tricky thread/fork/signal handling issues) may go unnoticed
elsewhere.  If you're focused on using Ruby + *BSD in a production
system, I suggest testing/fixing/reporting issues against the Ruby
development branches as much as possible before they hit production :)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: Any signal other than -9 causes full CPU utilization by master unicorn process on FreeBSD
  @ 2012-07-17 21:23  2%     ` Mark Mccraw
  2012-07-17 22:17  0%       ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Mark Mccraw @ 2012-07-17 21:23 UTC (permalink / raw)
  To: unicorn list


On Jul 17, 2012, at 7:56 AM, Mark McCraw wrote:

> 
> On Jul 16, 2012, at 10:05 PM, Eric Wong wrote:
> 
>> Mark Mccraw <Mark.Mccraw@sas.com> wrote:
>>> Hi There!
>>> 
>>> I'm having a devil of a time figuring out a weird issue I'm running
>>> into.  I have unicorn configured to start 4 worker processes, and that
>>> works great.  However, when it's time to cycle the app, everything
>>> goes haywire. By trial and error, I have narrowed it down to this:
>>> sending any signal to the master process other than SIGKILL fails
>>> miserably.  No new master process is created, as described in the
>>> documentation, nothing happens to the existing workers, nothing gets
>>> written to any log, and if I run top -u, I can see that very quickly
>>> the master ramps up to 100% CPU utilization.  This happens if I run
>>> 'kill -HUP <master pid>', 'kill -USR2 <master pid>', even 'kill -QUIT
>>> <master pid>'.
>> 
>> This sounds like a Ruby/FreeBSD bug we've seen before.  My script
>> in http://mid.gmane.org/20120201181445.GA31624@dcvr.yhbt.net should
>> reproduce the issue w/o unicorn.
> 
> You are absolutely correct!  Your script replicates the problem perfectly.
> 
>>> ruby 1.9.3p0 (2011-10-30 revision 33570) [amd64-freebsd9]
>> 
>> I think this is a Ruby bug that was fixed in 1.9.3-p30 according to
>> naruse:
>> http://mid.gmane.org/CAK6HhsppWVPijWLyZMwcKueYDT5sZroGv6ADXkgreht3aLfR9A@mail.gmail.com
>> 
>> Since 1.9.3 p194 is the latest, can you try that out and confirm the
>> fix?  I don't remember the other bug reported confirmed this issue was
>> fixed by upgrading Ruby.
> 
> We're upgrading now to see what happens.  I'm so glad you knew about this.  
> There's no telling how long it would have taken me to question the ruby interpreter implementation, and
> since it's FreeBSD, I never would have found it by googling.
> Thanks for hours (days?) of my life back.
> 
> Mark
> 

Just to follow up and close out the thread - Eric's recollection was spot on. 
We upgraded ruby on our FreeBSD server to the latest thing, and the problem completely disappeared.  Thanks again!
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Address already in use
  @ 2012-06-25 21:03  1%   ` Manuel Palenciano Guerrero
  0 siblings, 0 replies; 200+ results
From: Manuel Palenciano Guerrero @ 2012-06-25 21:03 UTC (permalink / raw)
  To: unicorn list

Hi,

First, thanks Eric, Jérémy and Aaron for replying. I really appreciate it.

Yes Eric, I can see the line... "inherited addr=/tmp/unicorn.app.sock fd=..."

here is the full log

-------------------------------------------------
I, [2012-06-21T11:40:44.282224 #29212]  INFO -- : inherited addr=/tmp/unicorn.sublimma_staging.sock fd=3
I, [2012-06-21T11:40:44.282480 #29212]  INFO -- : Refreshing Gem list
master process ready
worker=0 ready
worker=1 ready
reaped #<Process::Status: pid=28683,exited(0)> worker=0
reaped #<Process::Status: pid=28684,exited(0)> worker=1
master complete
E, [2012-06-21T11:40:46.386486 #29401] ERROR -- : adding listener failed addr=/tmp/unicorn.sublimma_staging.sock (in use)
E, [2012-06-21T11:40:46.386669 #29401] ERROR -- : retrying in 0.5 seconds (4 tries left)
E, [2012-06-21T11:40:46.887724 #29401] ERROR -- : adding listener failed addr=/tmp/unicorn.sublimma_staging.sock (in use)
E, [2012-06-21T11:40:46.887832 #29401] ERROR -- : retrying in 0.5 seconds (3 tries left)
E, [2012-06-21T11:40:47.388813 #29401] ERROR -- : adding listener failed addr=/tmp/unicorn.sublimma_staging.sock (in use)
E, [2012-06-21T11:40:47.388894 #29401] ERROR -- : retrying in 0.5 seconds (2 tries left)
E, [2012-06-21T11:40:47.889878 #29401] ERROR -- : adding listener failed addr=/tmp/unicorn.sublimma_staging.sock (in use)
E, [2012-06-21T11:40:47.889957 #29401] ERROR -- : retrying in 0.5 seconds (1 tries left)
E, [2012-06-21T11:40:48.390939 #29401] ERROR -- : adding listener failed addr=/tmp/unicorn.sublimma_staging.sock (in use)
E, [2012-06-21T11:40:48.391020 #29401] ERROR -- : retrying in 0.5 seconds (0 tries left)
E, [2012-06-21T11:40:48.892002 #29401] ERROR -- : adding listener failed addr=/tmp/unicorn.sublimma_staging.sock (in use)
/var/www/sublimma/staging/shared/bundle/ruby/1.8/gems/unicorn-4.3.0/lib/unicorn/socket_helper.rb:140:in `initialize': Address already in use - /tmp/unicorn.sublimma_staging.sock (Errno::EADDRINUSE)
-------------------------------------------------

my unicorn.rb: https://gist.github.com/2991110

and my production_init.sh: http://unicorn.bogomips.org/examples/init.sh

I was planning on trying the following...

adding the killing of the old_pid to the before_fork(), as in...

  old_pid = RAILS_ROOT + '/tmp/pids/unicorn.pid.oldbin'
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end

and making the UPGRADE case in the "init.sh" to just...

--------------------------------------------------
upgrade)
	sig USR2 && echo "Upgraded" && exit 0
	echo >&2 "Couldn't upgrade, starting '$CMD' instead"
	$CMD
	;;
--------------------------------------------------

Regards and thanks again !

Manuel P.

On Jun 25, 2012, at 10:28 PM, Eric Wong wrote:

> Manuel Palenciano Guerrero <mpalenciano@gmail.com> wrote:
>> Hello there,
>> 
>> I seem to have a problem with unix-sockets, and cannot see many people with the same situation when googling.
>> 
>> The problem is when upgrading (USR2 + QUIT) our applications. I get the following error very frequently but not always.
>> 
>> E, [2012-06-21T11:40:46.386486 #29401] ERROR -- : adding listener failed addr=/tmp/unicorn.app.sock (in use)
> 
> You should've seen an INFO message saying something like:
> 
>   inherited addr=/tmp/unicorn.app.sock fd=...
> 
> in your logs.
> 
> Can you share your unicorn config?  Are you using a before_exec hook
> at all and could that hook be clobbering ENV["UNICORN_FD"]?
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 1%]

* Re: unicorn + sleep = long wake up loading time
  2012-04-28  2:46  2% unicorn + sleep = long wake up loading time adam k
@ 2012-04-28  4:40  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-04-28  4:40 UTC (permalink / raw)
  To: unicorn list

adam k <adam.ffa@gmail.com> wrote:
> I'm running unicorn behind nginx. If the site isn't accessed for a
> long period of time, the next access of the site is loads very slowly,
> sometimes even a 404 error shows. Subsequent requests have a normal
> loading time until the site isn't accessed for a long period of time
> again. How do I stop this behavior?

unicorn is likely getting swapped out or a backend connection
(e.g. database connection) is getting timed out due to
inactivity.

* How much memory (RSS) is each worker using when the site is active?
* Is RSS noticeably lower when the site's been idle for a long time?
* How many unicorn worker processes?

(the above 3 questions also apply to nginx, but nginx usually uses
 much less memory than unicorn)

* How much physical RAM do you have?
* What is the timeout value of unicorn? (default is 60s)
* What else is running on the machine?
* Anything mentioned in the unicorn stderr logs, or your application logs?

The updatedb cronjob is one major culprit of causing apps to swap out,
but it could be any number of things (backup jobs, rootkit scanners,
prelink, ...).

Try setting up a cronjob run curl to hit an endpoint of your app
every few minutes.  Make sure that endpoint exercises whatever
non-local dependencies (hits the DB, etc...) your app has.

> I looked at the code for unicorn and it seems to happen when unicorn
> is looking to kill "lazy" workers and then if it chooses not to, it
> creates a sleep period based on a timeout + 1. I also read the
> documentation and searched the mailing list starting from 2011.

I think you're looking at the master process, which shouldn't
affect performance of the workers.  The workers wake up whenever
there's socket activity, but it can take longer if they're swapped
out.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* unicorn + sleep = long wake up loading time
@ 2012-04-28  2:46  2% adam k
  2012-04-28  4:40  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: adam k @ 2012-04-28  2:46 UTC (permalink / raw)
  To: mongrel-unicorn

Hey,

I'm running unicorn behind nginx. If the site isn't accessed for a
long period of time, the next access of the site is loads very slowly,
sometimes even a 404 error shows. Subsequent requests have a normal
loading time until the site isn't accessed for a long period of time
again. How do I stop this behavior?

I looked at the code for unicorn and it seems to happen when unicorn
is looking to kill "lazy" workers and then if it chooses not to, it
creates a sleep period based on a timeout + 1. I also read the
documentation and searched the mailing list starting from 2011.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: app error: Socket is not connected (Errno::ENOTCONN)
  @ 2012-04-27 19:47  2%         ` Matt Smith
  0 siblings, 0 replies; 200+ results
From: Matt Smith @ 2012-04-27 19:47 UTC (permalink / raw)
  To: Eric Wong; +Cc: mongrel-unicorn, George, Joel Nimety

> In production, I use a UNIX listener. (I am seeing no errors in production.)

(Again unicorn is not serving static files in production.)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Background jobs with #fork
  @ 2012-04-12 23:04  3%     ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-04-12 23:04 UTC (permalink / raw)
  To: unicorn list; +Cc: Patrik Wenger

Patrik Wenger <paddor@gmail.com> wrote:
> Thanks for the answers.

No problem.

> Isn't there another way to retrieve the right socket?

Actually, I think my proposed patch (in reply to Hongli) at
http://mid.gmane.org/20120412221022.GA20640@dcvr.yhbt.net
should fix your issue.

> > I can probably writeup a better explanation (perhaps on the usp.ruby
> > (Unix Systems Programming for Ruby mailing list) if this doesn't make
> > sense.

> Yeah I don't really understand this part. The "hanging" Unicorn worker
> can read another request because the client socket wasn't closed
> because it's still open in the child process? I would appreciate a
> better explanation, thank you.

Basically, fork() has a similar effect as dup() in that it creates
multiple references to the same kernel object (the client socket).

close() basically lowers the refcount of a kernel object, when the
refcount is zero, resources inside the kernel are freed.  When
the refcount of a kernel object reaches zero, a shutdown(SHUT_RDWR)
is implied.

This works for 99% of Rack apps since they don't fork() nor call dup()
on the client socket, so refcount==1 when unicorn calls close(), leading
to unicorn setting refcount=0 upon close() => everything is freed.

However, since the client socket increments refcount via fork(),
close() in the parent (unicorn worker) no longer implies
shutdown(SHUT_RDWR).



  parent timeline                  | child timeline
  ------------------------------------------------------------------
                                   |
  accept() -> sockfd created       | (child doesn't exist, yet)
  sockfd.refcount == 1             |
                                   |
  fork()                           | child exists, now
                                   |

    sockfd is shared by both processes now: sockfd.refcount == 2
    if either the child or parent forks again: sockfd.recount += 1

                                   |
  close() => sockfd.recount -= 1   | child continues running

    since sockfd.refcount == 1 at this point, the socket is still
    considerd "alive" by the kernel.  If the child calls close()
    (or exits), sockfd.refcount is decremented again (and now
    reaches zero).

Now, to write this as a commit message :>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 3%]

* Re: Background jobs with #fork
  2012-04-12 17:36  2% Background jobs with #fork paddor
@ 2012-04-12 20:39  0% ` Eric Wong
    0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2012-04-12 20:39 UTC (permalink / raw)
  To: unicorn list; +Cc: paddor

paddor <paddor@gmail.com> wrote:
> Hi
> 
> I've migrated from Passenger to Unicorn about a week ago. It's great.
> Great transparency and management, thanks for this great software!

:>

> A few of my Rails applications start background jobs using
> Kernel#fork. Of course, the ActiveRecord connections are closed and
> reopened again in the parent and child processes. The child process
> also does its job.

OK, that's good.

> Unfortunately, it seems that the parent (a Unicorn worker) waits for
> the child (background job) to finish before serving any new requests.
> Process.detach is done in the child. Process.setsid is not done. The
> child's STDOUT, STDERR and the Rails logger are redirected to their
> own files right after forking.

So you're only calling fork and not exec (or system/popen, right?)  It
may be the case that the client socket is kept alive in the background
process.

The client socket has the close-on-exec flag (FD_CLOEXEC) set, but
there's no close-on-fork flag, so you might have to find + close it
yourself.  Here's is a nasty workaround for the child process:

  ObjectSpace.each_object(Kgio::Socket) do |io|
    io.close unless io.closed?
  end

> The problem persists even when multiple workers are started. And the
> problem was not present in the old setup with Passenger.
> 
> My question: Does Unicorn somehow check/wait for child processes
> forked by the worker processes?

Unicorn workers do not explicitly wait on child processes themselves,
unicorn workers set: trap(:CHLD,"DEFAULT") after forking, even (the
unicorn master must handle SIGCHLD, of course)

The difference between nginx+unicorn and Passenger is probabably: nginx
relies on unicorn generating an EOF to signal the end-of-response (nginx
<-> unicorn uses HTTP/1.0), this I'm sure about.  I think Passenger uses a
protocol which can signal the end-of-request inline without relying on
an EOF on the socket (Hongli can correct me on this if I'm wrong).

However, nginx can still forward subsequent requests to the same unicorn
(even the same unicorn worker), because as far as the unicorn worker is
concerned (but not the OS), it's done with the original request.  It's
just the original request (perhaps the original client) is stuck
waiting for the background process to finish.

I can probably writeup a better explanation (perhaps on the usp.ruby
(Unix Systems Programming for Ruby mailing list) if this doesn't make
sense.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Background jobs with #fork
@ 2012-04-12 17:36  2% paddor
  2012-04-12 20:39  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: paddor @ 2012-04-12 17:36 UTC (permalink / raw)
  To: mongrel-unicorn

Hi

I've migrated from Passenger to Unicorn about a week ago. It's great.
Great transparency and management, thanks for this great software!

A few of my Rails applications start background jobs using
Kernel#fork. Of course, the ActiveRecord connections are closed and
reopened again in the parent and child processes. The child process
also does its job.

Unfortunately, it seems that the parent (a Unicorn worker) waits for
the child (background job) to finish before serving any new requests.
Process.detach is done in the child. Process.setsid is not done. The
child's STDOUT, STDERR and the Rails logger are redirected to their
own files right after forking.

Software used:
ruby 1.9.1p376
Rubygems 1.8.17
Linux 2.6.16.60-0.21-smp (SUSE 10.2)
unicorn 4.2.1
nginx 0.8.53

The problem persists even when multiple workers are started. And the
problem was not present in the old setup with Passenger.

My question: Does Unicorn somehow check/wait for child processes
forked by the worker processes?

Thanks in advance for your help.

-- 
paddor
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: FreeBSD jail and unicorn
       [not found]         ` <CB5014D7.892%philipp.bruell@skrill.com>
@ 2012-02-02 19:31  0%       ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2012-02-02 19:31 UTC (permalink / raw)
  To: unicorn list; +Cc: Philipp Bruell, Charles Hornberger

Philipp Bruell <Philipp.Bruell@skrill.com> wrote:
> On 01/02/2012 19:14, "Eric Wong" <normalperson@yhbt.net> wrote:
> >Philipp Bruell <Philipp.Bruell@skrill.com> wrote:

> The scripts behaves exactly like unicorn. The master received the QUIT and
> passes it to the child. The child also receives it, but don't exit. While
> the master is waiting for the child to exit, it consumes all the cpu
> cycles.

Interesting, I suspect it's some bad interaction with fork() causing
signal handlers/pthreads to go bad.  I expect the following simple
script to work flawlessly since it doesn't fork:

----------------------------------------
trap(:QUIT) { exit(0) }
puts "Ready for SIGQUIT on #$$"
sleep
----------------------------------------

> I don't have the option, to test without jail, on a different FreeBSD
> version nor a different architecture (and FreeBSD - on Mac OS X everything
> works perfect). But I tried ruby version 1.9.2 and that works! So I guess
> it's a bug with 1.9.3 on FreeBSD.

Can you report this as a bug to the Ruby core folks on
https://bugs.ruby-lang.org/ and also to whereever the FreeBSD hackers
take bug reports?  Somebody from one of those camps should be able
to resolve the issue.

The good thing is my small sample script is enough to reproduce the
issue, so it should be easy for an experienced FreeBSD hacker to
track down.

> I've attached the truss -f output of the child process of the test script.
> But the observation with truss made the problem disappear again :-(

It could be a timing or race condition issue.  I've had strace on linux
find/hide bugs because it slowed the program down enough.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: FreeBSD jail and unicorn
       [not found]     ` <CB4EBD2A.7DF%philipp.bruell@skrill.com>
@ 2012-02-01 18:14  0%   ` Eric Wong
       [not found]         ` <CB5014D7.892%philipp.bruell@skrill.com>
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2012-02-01 18:14 UTC (permalink / raw)
  To: unicorn list; +Cc: Philipp Bruell, Charles Hornberger

Philipp Bruell <Philipp.Bruell@skrill.com> wrote:
> First of all, thank you for your fast reply.

No problem, depends on the time of day of course :>

> The behaviour details Charles described are correct and we are using ruby
> version 1.9.3.
> 
> It's good that you've asked for the other signals. I've checked them in
> particular and it seems that it is a common signal handling problem. The
> process freaks out on each of them :-(
> 
> I've attached the output of truss -f for a QUIT signal. That signal took a
> quite long time to get processed (and took all CPU cycles), but finally
> worked.

I only saw the output from the master process there, nothing from the
worker.  It seems like the master is OK, but trying to kill the worker
is not.  I wonder if it's related to
https://bugs.ruby-lang.org/issues/5240

With the following script, can you try sending SIGQUIT to the parent
and see what happens?

------------------------------- 8< -----------------------------
pid = fork do
  r, w = IO.pipe
  trap(:QUIT) do
    puts "SIGQUIT received in child, exiting"
    w.close
  end
  r.read
end

trap(:QUIT) do
  puts "SIGQUIT received in parent, killing child"
  Process.kill(:QUIT, pid)
  p Process.waitpid2(pid)
  exit
end
sleep 1 # wait for child to setup sig handler
puts "Child ready on #{pid}, parent on #$$"
sleep
----------------------------------------------------------------
If the above fails, try with different variables:

* without a jail on the same FreeBSD version/release/patchlevel
* Ruby 1.9.2 (which has a different signal handling implementation)
* different FreeBSD version
* different architecture[1]

Mixing either signal handling or fork()-ing in the presence of threads
is tricky.  Ruby 1.9 uses a dedicated thread internally for signal
handling, I wouldn't be surprised if there's a bug lingering somewhere
in FreeBSD or Ruby...

Have you checked the FreeBSD mailing lists/bug trackers?  I don't recall
seeing anything other than the aforementioned bug in ruby-core...

[1] - I expect there's ASM involved in signal/threading implementation
      details, so there's a chance it's x86_64-specific...

> The output of USR2 signal is too long to attach it to a mail, but at a
> first sight, it repeats the following calls over and over again.

Don't send monster attachments, host it somewhere else so mail servers
won't reject it for wasting bandwidth.  The mailman limit on rubyforge
is apparently 256K (already huge IMHO).  Also, don't top post

> 24864: 
> thr_kill(0x18c32,0x1a,0x800a8edc0,0x18a86,0x7fffffbeaf80,0x80480c000) = 0
> (0x0)
> 24864: select(4,{3},0x0,0x0,{0.100000 })         = 1 (0x1)
> 24864: read(3,"!",1024)                          = 1 (0x1)

OK, so the signal is received correctly by the Ruby VM in the master.
I just don't see anything in the worker, but the master does attempt
to forward SIGQUIT to the worker.

> It also seems to me, that observing the processes with truss changes the
> behaviour a lot. During the observed USR2 the master process spawns a lot
> (about 30) of <defunct> processes. I never had this before.

Some processes react strangely to being traced.  Maybe there's something
better than truss nowadays?
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: FreeBSD jail and unicorn
  @ 2012-01-31 18:50  2% ` Charles Hornberger
  0 siblings, 0 replies; 200+ results
From: Charles Hornberger @ 2012-01-31 18:50 UTC (permalink / raw)
  To: Eric Wong, unicorn list; +Cc: Philipp Bruell, "wrote:"@dcvr.yhbt.net

On 1/31/12 7:39 PM, normalperson@yhbt.net wrote:
>Philipp Bruell <Philipp.Bruell@skrill.com> wrote:
>>
>> Does someone ran into the same problem? Does someone has an idea?
>
>Tatsuya Ono documented a workaround for jails here (see gist):
>http://mid.gmane.org/CAHBuKRj09FdxAgzsefJWotexw-7JYZGJMtgUp_dhjPz9VbKD6Q@m
>ail.gmail.com
>
>(http://unicorn.bogomips.org/KNOWN_ISSUES.html refers to this link, too)

Philipp's gone afk for the evening, so I'll take the liberty of replying
with what I know ...

We tried the fix mentioned above, and it didn't work. We also tried
switching to unix sockets; no joy. (Actually it worked once, then refused
to work again.)


>If that didn't work, maybe checking stderr.log will tell you something
>more.

Nothing shows up in stderr.log It's as if the master doesn't even get the
-USR2 signal. Or as if whatever it's sending to stderr is not actually
getting to the filesystem...

In any case, we don't see anything.

Any further ideas for how to debug would be much appreciated, and my
apologies in advance for mixing up any details; Philipp was doing the work
on this, not me.

-c

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Master repeatedly killing workers due to timeouts
  @ 2011-12-08 22:24  2%   ` Jonathan del Strother
  0 siblings, 0 replies; 200+ results
From: Jonathan del Strother @ 2011-12-08 22:24 UTC (permalink / raw)
  To: unicorn list

On 8 Dec 2011, at 20:23, Eric Wong <normalperson@yhbt.net> wrote:

> Jonathan del Strother <maillist@steelskies.com> wrote:
>> Hi,
>> We're using unicorn as a Rails server on Solaris, and it's been
>> running great for several months.  We've recently been having a few
>> problems and I'm at a loss what might cause it.  A number of times in
>> the past few days, our unicorn slaves keep timing out & the master
>> keeps restarting them.  unicorn.log looks something like :
> 
> Which versions of Ruby and Unicorn are you running?
> <snip>

Hi Eric, thanks for the comprehensive reply. As it happens, shortly after posting this, the problem started again...in the past it's been fairly sporadic, whereas this time it was extremely persistent so we were forced into tracking it down then & there. I ended up replacing unicorn with a single webrick instance, which would serve a few requests then hang...after a long debugging session we finally managed to track down the problem to a hung sphinx instance, which we use for our search engine.  Ruby would try to talk to that and just sit there without ever timing out. I'm going to go through the sphinx code tomorrow and see if I can figure out why that might occur.

Thanks for the help,
Jonathan
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: COW friendly GC and PostgreSQL
  2011-11-30 15:24  2% COW friendly GC and PostgreSQL Laas Toom
@ 2011-11-30 20:54  0% ` Jeremy Evans
  0 siblings, 0 replies; 200+ results
From: Jeremy Evans @ 2011-11-30 20:54 UTC (permalink / raw)
  To: unicorn list

On Wed, Nov 30, 2011 at 7:24 AM, Laas Toom <laas.toom@eenet.ee> wrote:
> Hello,
>
> I stumbled upon a weird issue when upgrading a Rails app and trying to run in on Unicorn!:
> http://stackoverflow.com/questions/8262803/unicorn-and-postgresql/
>
> The problem manifested itself via a few different error messages as if garbage/nil was returned from database instead of actual data and it only happened with more than one concurrent request (i.e under load test).
>
> This turned out to be an issue with COW-friendly GC that I had enabled (taken from the example unicorn.conf). Removing this from unicorn.conf made the app run smooth again.
>
> Is this something known or did I do something wrong?

If you didn't disconnect the database connection before forking, that
could be the cause of the problem, though you should have issues
regardless of whether the GC is CoW or not.  Did you just turn off
CoW, or did you turn off preload_app as well?

Looking at your config file, try adding a before_fork callback that
disconnects the database connection and things may work fine.

Jeremy
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* COW friendly GC and PostgreSQL
@ 2011-11-30 15:24  2% Laas Toom
  2011-11-30 20:54  0% ` Jeremy Evans
  0 siblings, 1 reply; 200+ results
From: Laas Toom @ 2011-11-30 15:24 UTC (permalink / raw)
  To: mongrel-unicorn

Hello,

I stumbled upon a weird issue when upgrading a Rails app and trying to run in on Unicorn!:
http://stackoverflow.com/questions/8262803/unicorn-and-postgresql/

The problem manifested itself via a few different error messages as if garbage/nil was returned from database instead of actual data and it only happened with more than one concurrent request (i.e under load test).

This turned out to be an issue with COW-friendly GC that I had enabled (taken from the example unicorn.conf). Removing this from unicorn.conf made the app run smooth again.

Is this something known or did I do something wrong? 

Best regards,
Laas
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Re: Should USR2 always work?
  @ 2011-11-23  5:31  3%       ` Lawrence Pit
  0 siblings, 0 replies; 200+ results
From: Lawrence Pit @ 2011-11-23  5:31 UTC (permalink / raw)
  To: Eric Wong; +Cc: unicorn list


> I brought up a "bring-your-own-executable" deployment a few months ago
> but haven't heard any feedback.  Here it is for Bundler:
> 
>   http://mid.gmane.org/20110819022316.GA2951@dcvr.yhbt.net


I tried this out by going back to a previous version of bundler, then deployed, unfortunately doesn't seem to work for me:

  executing ["/srv/ec/current/script/unicorn_byoe", "-D", "-E", "staging", "-c", "/srv/ec/current/config/unicorn/staging.rb"] (in /srv/ec/releases/20111123052840)
  /opt/ree/bin/ruby: no such file to load -- bundler/setup (LoadError)

Sending QUIT and then start anew works fine. Upgraded back to latest, tried again, and again no luck. QUIT + start is the only thing that works then.


Tried a slightly modified version of unicorn_byoe, I'm using rails:

#!/opt/ree/bin/ruby
require File.expand_path("../../config/boot.rb", __FILE__)
# Gem.bin_path(gem_name, executable_name, *version_requirements)
load Gem.bin_path("unicorn", "unicorn_rails")   



Cheers,
Lawrence


_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 3%]

* Re: Peformance up - using OobGC & GC.disable
  @ 2011-10-12 16:00  2%           ` Tatsuya Ono
  0 siblings, 0 replies; 200+ results
From: Tatsuya Ono @ 2011-10-12 16:00 UTC (permalink / raw)
  To: unicorn list

Yes, you are right, Eric. The usage of memory stops increasing at a
certain point. Besides I do not see any significant page I/O with it.

I give the patch go for our live service without the UnicornKiller. I
will report if we experience any issues occurred in the wild.

Thanks Yuichi again for submitting the patch and sharing your knowledge.

By the way, I tested this with Rails 2.3/Ruby 1.8.7/FreeBSD 8.2


Tatsuya

On 11 October 2011 00:03, Tatsuya Ono <ononoma@gmail.com> wrote:
> Thanks Eric for the feedback.
>
> I actually had read that email and I think I understand it. But what I
> am experiencing seems a different story. Our rails app uses around
> 250MB memory usually. After applying this patch and calling
> GC.disabled on after_fork, the usage of memory increases on every
> request and goes up to 1GB easily.
>
> However, yes, I must say that I need to test more carefully. Let me
> come back later. I am going to have some stress test and monitor if
> Unicorn introduces swapping on VM with this solution. Hopefully I can
> do it tomorrow or later this week.
>
>
> Tatsuya
>
> On 10 October 2011 22:53, Eric Wong <normalperson@yhbt.net> wrote:
>> Tatsuya Ono <ononoma@gmail.com> wrote:
>>> I don't actually understand is why GC.disable solution could introduce
>>> more memory leak. If I simplify the problem, the code is something
>>> like bellow:
>>>
>>> ---------------
>>> GC.disable
>>> (do something)
>>> GC.enable
>>> GC.start
>>> ---------------
>>>
>>> When the code block finishes, I expect that memory size should be
>>> (almost) equal with the case GC is enabled at begging. But it doesn't
>>> seems so from our experience.
>>>
>>> Do anyone know why there could be significant difference on memory
>>> usage because of timing of GC? It might be a question on Ruby rather
>>> than Unicorn, though, I thought even just sharing my experience could
>>> be worth to someone here.
>>
>> Basically, the free(3) function in the C standard library does not
>> guarantee memory is released back to the kernel (speed vs memory usage
>> tradeoff).
>>
>> There was discussion of this on the usp.ruby mailing list starting at
>> Message-ID: 20110914234917.GA2480@dcvr.yhbt.net
>>
>> usp.ruby archives are at http://bogomips.org/usp.ruby/archives/2011.mbox.gz
>> _______________________________________________
>> Unicorn mailing list - mongrel-unicorn@rubyforge.org
>> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
>> Do not quote signatures (like this one) or top post when replying
>>
>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 2%]

* Sending ABRT to timeout-errant process before KILL
@ 2011-09-08  9:04  1% J. Austin Hughey
  0 siblings, 0 replies; 200+ results
From: J. Austin Hughey @ 2011-09-08  9:04 UTC (permalink / raw)
  To: mongrel-unicorn

Hello there -

I've recently been working with a customer in my capacity as a support engineer at Engine Yard who's having some trouble with Unicorn.  Basically, they're finding their application being force-killed once it reaches the default timeout.  Rather than simply increasing the timeout, we're trying to find out _why_ their application is being killed.  Unfortunately we can't quite do that because the application is being force-killed without warning, making it so that the customer's logging can't actually be written to the disk. (This is an intermittent issue as opposed to something that happens 100% of the time.)

In discussing the matter internally and with our customer, I came up with a simple monkey patch to Unicorn that _sort of_ works, but I'm having some trouble with it once the number of Unicorn workers goes beyond one.  I originally limited it to just one worker because I wanted to limit the possibility that multiple workers could cause problems in figuring things out -- turns out I was right.

I'm going to show the patch in two ways: 1) inline, at the bottom of this post, and 2) by link to GitHub:
https://github.com/jaustinhughey/unicorn/blob/abort_prior_to_kill_workers/lib/unicorn/http_server.rb#L438

The general idea is that I'd like to have some way to "warn" the application when it's about to be killed.  I've patched murder_lazy_workers to send an abort signal via kill_worker, sleep for 5 seconds, then look and see if the process still exists by using Process.getpgid.  If so, it sends the original kill command, and if not, a rescue block kicks in to prevent the raised error from Process.getpgid from making things explode.

I've created a simulation app, built on Rails 3.0, that uses a generic "posts" controller to simulate a long-running request.  Instead of just throwing a straight-up sleep 65 in there, I have it running through sleeping 1 second on a decrementing counter, and doing that 65 times.  The reason is because, assuming I've read the code correctly, even with my "skip sleeping workers" commented line below, it'll skip over the process, thus rendering my simulation of a long-running process invalid.  However, clarification on this point is certainly welcome.  You can see the app here: https://github.com/jaustinhughey/unicorn_test/blob/master/app/controllers/posts_controller.rb

The problem I'm running into, and where I could use some help, is when I increase the number of Unicorn workers from one to two.  When running only one Unicorn worker, I can access my application's posts_controller's index action, which has the intentionally long-running code.  At that time I tail -f unicorn.log and production.log.  Those two logs look like this with one Unicorn worker:

WITH ONE UNICORN WORKER:
========================

production.log:
---------------
Sleeping 1 second (65 to go)…
 ... continued ...
Sleeping 1 second (7 to go)...
Sleeping 1 second (6 to go)...
Sleeping 1 second (5 to go)...
Caught Unicorn kill exception!
Sleeping 1 second (4 to go)...
Sleeping 1 second (3 to go)...
Sleeping 1 second (2 to go)...
Sleeping 1 second (1 to go)...
Completed 500 Internal Server Error in 65131ms

NoMethodError (undefined method `query_options' for nil:NilClass):
  app/controllers/posts_controller.rb:32:in `index'

(I think the NoMethodError issue above is due to me calling a disconnect on ActiveRecord in the Signal.trap block, so I think that can be safely ignored.)

As you can see, the Signal.trap block inside the aforementioned posts_controller is working in this case.  Corresponding log entries in unicorn.log concur:

unicorn.log:
------------
worker=0 ready
master process ready
[2011-09-08 00:31:01 PDT] worker=0 PID: 28921 timeout hit, sending ABRT to process 28921 then sleeping 5 seconds...
[2011-09-08 00:31:06 PDT] worker=0 PID:28921 timeout (61s > 60s), killing
reaped #<Process::Status: pid 28921 SIGKILL (signal 9)> worker=0
worker=0 ready

So with one worker, everything seems cool.  But with two workers?


WITH TWO UNICORN WORKERS:
=========================

production.log:
---------------
Sleeping 1 second (8 to go)...
Sleeping 1 second (7 to go)...
Sleeping 1 second (6 to go)...
Sleeping 1 second (5 to go)...
Sleeping 1 second (4 to go)...
Sleeping 1 second (3 to go)...
Sleeping 1 second (2 to go)...
Sleeping 1 second (1 to go)...
Rendered posts/index.html.erb within layouts/application (13.2ms)
Completed 200 OK in 65311ms (Views: 16.9ms | ActiveRecord: 0.5ms)

Note that there is no notice that the ABRT signal was trapped, nor is there a NoMethodError (likely caused by disconnecting from the database) as above.  Odd.

unicorn.log:
------------
Nothing.  No new data whatsoever.

The only potential clue I can see at this point would be a start-up message in unicorn.log.  After increasing the number of Unicorn workers to two, I examined unicorn.log again and found this:

master complete
I, [2011-09-08T00:34:40.499437 #29572]  INFO -- : unlinking existing socket=/var/run/engineyard/unicorn_ut.sock
I, [2011-09-08T00:34:40.499888 #29572]  INFO -- : listening on addr=/var/run/engineyard/unicorn_ut.sock fd=5
I, [2011-09-08T00:34:40.504542 #29572]  INFO -- : Refreshing Gem list
worker=0 ready
master process ready
[2011-09-08 00:34:49 PDT] worker=1 PID: 29582 timeout hit, sending ABRT to process 29582 then sleeping 5 seconds...
[2011-09-08 00:34:50 PDT] worker=1 PID:29582 timeout (1315467289s > 60s), killing
reaped #<Process::Status: pid 29582 SIGIOT (signal 6)> worker=1
worker=1 ready

So it looks like Worker 1 is hitting a strange/false timeout of 1315467289 seconds, which isn't really possible as it wasn't even running 1315467289 seconds prior to that (which equates to roughly 41 years ago if my math is right).

---

Needless to say, I'm a bit stumped at this point, and would sincerely appreciate another point of view on this.  Am I going about this all wrong?  Is there a better approach I should consider?  And if I'm on the right track, how can I get this to work regardless of how many Unicorn workers are running?

Thank you very much for any assistance you can provide!


-- INLINE VERSION OF PATCH --

diff --git a/lib/unicorn/http_server.rb b/lib/unicorn/http_server.rb
index 78d80b4..8a2323f 100644
--- a/lib/unicorn/http_server.rb
+++ b/lib/unicorn/http_server.rb
@@ -429,6 +429,11 @@ class Unicorn::HttpServer
     proc_name 'master (old)'
   end
 
+  # A custom formatted timestamp for debugging
+  def custom_timestamp
+    return Time.now.strftime("[%Y-%m-%d %T %Z]")
+  end
+
   # forcibly terminate all workers that haven't checked in in timeout seconds.  The timeout is implemented using an unlinked File
   def murder_lazy_workers
     t = @timeout
@@ -436,16 +441,40 @@ class Unicorn::HttpServer
     now = Time.now.to_i
     WORKERS.dup.each_pair do |wpid, worker|
       tick = worker.tick
-      0 == tick and next # skip workers that are sleeping
+
+      # REMOVE THE FOLLOWING COMMENT WHEN TESTING PRODUCTION
+# 0 == tick and next # skip workers that are sleeping
+      # ^ needs to be active, commented here for simulation purposes
+
       diff = now - tick
       tmp = t - diff
       if tmp >= 0
         next_sleep < tmp and next_sleep = tmp
         next
       end
-      logger.error "worker=#{worker.nr} PID:#{wpid} timeout " \
-                   "(#{diff}s > #{t}s), killing"
-      kill_worker(:KILL, wpid) # take no prisoners for timeout violations
+
+
+      # Send an ABRT signal to Unicorn and wait 5 seconds before attempting an
+      # actual kill, if and only if the process is still running.
+
+      begin
+        # Send the ABRT signal.
+        logger.debug "#{custom_timestamp} worker=#{worker.nr} PID: #{wpid} timeout hit, sending ABRT to process #{wpid} then sleeping 5 seconds..."
+        kill_worker(:ABRT, wpid)
+
+        sleep 5
+
+        # Now see if the process still exists after being given five
+        # seconds to terminate on its own, and if so, do a hard kill.
+        if Process.getpgid(wpid)
+          logger.error "#{custom_timestamp} worker=#{worker.nr} PID:#{wpid} timeout " \
+                       "(#{diff}s > #{@timeout}s), killing"
+          kill_worker(:KILL, wpid) # take no prisoners for timeout violations
+        end
+      rescue Errno::ESRCH => e
+        # No process identified - maybe it exited on its own?
+        logger.debug "#{custom_timestamp} worker=#{worker.nr} PID: #{wpid} responded to ABRT on its own and no longer exists. (Received message: #{e})"
+      end
     end
     next_sleep
   end

-- END INLINE VERSION OF PATCH --

--
J. Austin Hughey
Application Support Engineer - Engine Yard
www.engineyard.com | jhughey@engineyard.com

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply related	[relevance 1%]

* Re: Unicorn for Rails development mode?
  @ 2011-08-28 23:14  2% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2011-08-28 23:14 UTC (permalink / raw)
  To: unicorn list

Jay Levitt <jlevitt@tiptap.com> wrote:
> I'm tired of the bugginess of Webrick, so I want to upgrade to
> something modern for developing Rails apps on my Mac, and I'm a bit
> out of the loop.  Is Unicorn a drop-in replacement these days in
> development mode? Does it have advantages or disadvantages compared to
> Thin?  I don't even know what capabilities I should care about, but
> some obvious ones are:

unicorn should be a drop-in replacement, you shouldn't need a
unicorn-specific config file for development.

The "unicorn_rails"[1] command was designed Rails 1.x/2.x users, while
"unicorn" is a better fit for Rails 3 (and all Rack frameworks),
but either works for Rails 3

Thin is a great server and I've had no issues with Webrick for
development, either.

You don't have to worry about slow clients hitting Unicorn in
development, so you can forgo nginx for development.

> - Doesn't stop logging after a while (hi, Webrick)

This is not a problem I've heard of with either server.  It /may/ be an
issue with the Rails buffered logger implementation, though.  The Ruby
standard library Logger used by Unicorn does no buffering.

> - Logs to STDOUT or STDERR

This is the default, and Unicorn explicitly disables output buffering
on both of these.

> - Reloads everything that ought to be reloaded on each request

Set RAILS_ENV=development in your environment.

> - Works well with 1.9.2

Yes, I regularly test against 1.8.7, 1.9.2, 1.9.3dev and trunk.

> Any advice? I'd think this would be a FAQ, but I haven't found any
> discussions on the topic.

I don't do much Rails development, but everything /should/ work fine.  I
will never officially support non-Free systems, but from what I've heard
it works fine on the ones names after fruits.

Others on this list can hopefully chime in, too.



[1] - There's some confusion that was the result of "unicorn_rails"
      being an automatic compatibility layer for old Rails.  If I
      could to do it all over again, I'd leave "unicorn_rails" out
      and force folks to setup the compatibility layer themselves
      to learn how Rack works.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Strange quit behavior
  @ 2011-08-23 16:49  2%                           ` Alex Sharp
  0 siblings, 0 replies; 200+ results
From: Alex Sharp @ 2011-08-23 16:49 UTC (permalink / raw)
  To: unicorn list

On Tuesday, August 23, 2011 at 12:12 AM, Eric Wong wrote:
> Did you send any signals to 18862 while you were making this strace?
Not while, no. I had already sent a USR2 signal to this process (the old master), and then I attached strace to it. 

I'll try sending another USR2 signal next time while strace is attached. 
> >  I went ahead and ran strace with the same flags on the *new* master,
> > and saw a bunch of output that looked bundler-related:
> > https://gist.github.com/138344b5b19ec6ba1a4c
> > 
> > Even more bizarre, eventually the process started successfully :-/ Is
> > it possible this had something to do with strace de-taching?
> 
> That looks like pretty normal "require" behavior. strace would slow
>  down requires, a lot.
> 
> So this was with preload_app=true? While you're debugging problems,
> I suggest keeping preload_app=false and worker_problems=1 to minimize
> the variables.
Ok, I'll change those and report back. I'm guessing you meant worker_processes (not problems)? 
> 
> > You can see this in the unicorn.stderr.log file I included in the
> > gist. Check out these two lines in particular, which occur 25 minutes
> >  apart:
> > 
> > I, [2011-08-23T02:15:08.396868 #22169] INFO -- : Refreshing Gem list
> > I, [2011-08-23T02:40:16.621210 #22925] INFO -- : worker=1 spawned pid=22925
> 
> Wow, it takes 25 minutes to load your application? strace makes the
>  application /much/ slower, so I can actually believe it takes that long.
No, my mistake. Loading the application only takes about 10 seconds, and I only had strace attached to this process for a few seconds (less than 10). My point here was to show that the new master just spun for a good 25 minutes (presumably trying to load files over and over again), and then, seemingly out of nowhere, the new master came up and spawned the new workers. 
Next time I'll try to get attached with strace earlier and record more output.

> 
>  Loading large applications is very slow under Ruby 1.9.2, there's some
> pathological load performance issues fixed in 1.9.3.
> 
Yep, I've read about those, and I've seen Xavier's patch, but I don't think that's the issue here (though, it appears that's why the files attempting to be loaded in the strace output do not exist). Under normal circumstances, loading the app takes about 10 seconds and doesn't peg the cpu while doing it.
> 
> So you're saying /without/ strace, CPU usage _stays_ at 100% and _never_
> goes down?
Correct. 
> 
> 
> Generally, can you reproduce this behavior on a plain (empty) Rails
>  application with no extra gems?
> 
Good idea, I'll try that next.

- Alex 
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Strange quit behavior
  @ 2011-08-18 23:13  2%                 ` Alex Sharp
    0 siblings, 1 reply; 200+ results
From: Alex Sharp @ 2011-08-18 23:13 UTC (permalink / raw)
  To: unicorn list

Ok, thanks a lot for the for the patch attempt and subsequent in-depth
explanation. I know this thread is long-lived and a bit confusing,
mostly because I've seen two different sets of scenarios that yield
the same result, which is that unicorn does not restart.

The first scenario (and the one that started this thread) was that
unicorn actually receives the signal, spawns a new master, and that
process pegs the cpu and spins into eternity. The second scenario is
that the old master seems to completely ignore the USR2 signal the
first time, and when sent again, properly restarts. In both scenarios
the old master and workers continue to serve the old code.

I thought that setting the Unicorn::HttpServer::START_CTX[0] in my
unicorn config had solved the first scenario (pegged cpu), but I found
a new occurrence of it today, and I now have some new strace output
for this scenario (strace -v):

select(4, [3], NULL, NULL, {60, 727229}) = 0 (Timeout)
wait4(-1, 0x7ffffd70f72c, WNOHANG, NULL) = 0
clock_gettime(CLOCK_REALTIME, {1313708081, 720757662}) = 0
fstat(8, {st_dev=makedev(202, 1), st_ino=39806, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/18-14:32:10, st_mtime=2011/08/18-14:32:10,
st_ctime=2011/08/18-22:54:06}) = 0
clock_gettime(CLOCK_REALTIME, {1313708081, 721131305}) = 0
fstat(10, {st_dev=makedev(202, 1), st_ino=45370, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/18-14:32:10, st_mtime=2011/08/18-14:32:10,
st_ctime=2011/08/18-22:54:05}) = 0
clock_gettime(CLOCK_REALTIME, {1313708081, 721290800}) = 0
select(4, [3], NULL, NULL, {565, 34005}) = 0 (Timeout)
wait4(-1, 0x7ffffd70f72c, WNOHANG, NULL) = 0
clock_gettime(CLOCK_REALTIME, {1313708646, 853870540}) = 0
fstat(8, {st_dev=makedev(202, 1), st_ino=39806, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/18-14:32:10, st_mtime=2011/08/18-14:32:10,
st_ctime=2011/08/18-22:59:06}) = 0
clock_gettime(CLOCK_REALTIME, {1313708646, 854102750}) = 0
fstat(10, {st_dev=makedev(202, 1), st_ino=45370, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/18-14:32:10, st_mtime=2011/08/18-14:32:10,
st_ctime=2011/08/18-23:04:05}) = 0
clock_gettime(CLOCK_REALTIME, {1313708646, 854260655}) = 0
select(4, [3], NULL, NULL, {598, 630876}

With respect to the second scenario (ignoring signals), I'm going to
recommend we table that issue for now, as we're currently running on a
version of ubuntu (11.10) which has a known signal trapping bug with
ruby 1.9.2-p180, and downgrading to 10.04 may solve that problem.
Granted, when I've observed this in the past with other libraries, the
process becomes completely unresponsive, whereas the behavior in
unicorn is more intermittent. Either way, we are in the process of
downgrading our servers to ubuntu 10.04, so let's not waste anymore
time trying to debug something that may well be occurring due to a
kernel bug. If we continue to see this after the downgrade to 10.04,
I'll report back and we can keep digging.

Again, my apologies for the confusion between the two scenarios, and
thanks for all your help.

- Alex
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Strange quit behavior
  @ 2011-08-17  4:26  1%           ` Alex Sharp
    0 siblings, 1 reply; 200+ results
From: Alex Sharp @ 2011-08-17  4:26 UTC (permalink / raw)
  To: unicorn list

On Fri, Aug 5, 2011 at 1:07 AM, Eric Wong <normalperson@yhbt.net> wrote:
> Is this Unicorn 3.x?  Which 3.x version exactly?  Maybe give 4.0.1
> or the 4.0.2 git prerelease a try, too.

We're using version 3.6.2 with ruby 1.9.2-p180 on ubuntu 11.x. I know
the kernel on this version of ubuntu has a know signal trapping bug,
but I don't think that's what happening here. The processes respond
after I send the restart signal to them again (USR2 + QUIT).

> Can I get more? (until the next select() call, at least).  I'd like to
> see the timeout argument that gets passed to the next select().

Here's some more output. This is from the old master though, not a new
master that is pegging the CPU. In this instance it's almost like the
old master just ignores the signal.

select(4, [3], NULL, NULL, {8, 129984}) = 0 (Timeout)
wait4(-1, 0x7fff16b82e4c, WNOHANG, NULL) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 259408364}) = 0
fstat(12, {st_dev=makedev(202, 1), st_ino=20373, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/12-23:14:20, st_mtime=2011/08/12-23:14:20,
st_ctime=2011/08/17-04:22:21}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 259775504}) = 0
fstat(13, {st_dev=makedev(202, 1), st_ino=20381, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/12-23:14:20, st_mtime=2011/08/12-23:14:20,
st_ctime=2011/08/17-04:22:17}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 259936816}) = 0
fstat(14, {st_dev=makedev(202, 1), st_ino=20382, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/12-23:14:21, st_mtime=2011/08/12-23:14:21,
st_ctime=2011/08/17-04:22:19}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 260086380}) = 0
fstat(15, {st_dev=makedev(202, 1), st_ino=20185, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/12-23:14:21, st_mtime=2011/08/12-23:14:21,
st_ctime=2011/08/17-04:22:17}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 260235797}) = 0
fstat(16, {st_dev=makedev(202, 1), st_ino=20255, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/12-23:14:21, st_mtime=2011/08/12-23:14:21,
st_ctime=2011/08/17-04:22:19}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 260384849}) = 0
fstat(17, {st_dev=makedev(202, 1), st_ino=20383, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/12-23:14:21, st_mtime=2011/08/12-23:14:21,
st_ctime=2011/08/17-04:22:19}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 260534792}) = 0
fstat(18, {st_dev=makedev(202, 1), st_ino=20384, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/12-23:14:21, st_mtime=2011/08/12-23:14:21,
st_ctime=2011/08/17-04:22:19}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 260684278}) = 0
fstat(10, {st_dev=makedev(202, 1), st_ino=20196, st_mode=S_IFREG,
st_nlink=0, st_uid=1001, st_gid=1001, st_blksize=4096, st_blocks=0,
st_size=0, st_atime=2011/08/16-16:33:46, st_mtime=2011/08/16-16:33:46,
st_ctime=2011/08/17-04:22:19}) = 0
clock_gettime(CLOCK_REALTIME, {1313554942, 260833725}) = 0
select(4, [3], NULL, NULL, {8, 976047}

> If you see select() with very small timeout, use "strace -v" to get the
> st_ctime from fstat()s...
>
> I could have a math bug in murder_lazy_workers (please read/review the
> logic in that method, I haven't noticed issues myself).
>
> I made some tweaks to the master sleep strategy within the past year to
> reduce wakeups during idle hours.  This is intended to go with Ruby
> 1.9.3 which will have /much/ better thread wakeup strategy that reduces
> power consumption during idle times.
>
>> The first line was when the master was idle, and then I threw a few
>> requests at it.
>
> Are all workers responding as expected and not dying?

The old workers appear to be serving requests.

- Alex
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 1%]

* Unicorn in the jail
@ 2011-08-03 16:41  2% Tatsuya Ono
  0 siblings, 0 replies; 200+ results
From: Tatsuya Ono @ 2011-08-03 16:41 UTC (permalink / raw)
  To: mongrel-unicorn

I ran into a problem when sending USR2 signal to master process which
is listening a TCP port. It shows the following error in log.

err: adding listener failed addr=0.0.0.0:3010 (in use)

I found similar error reported on this mailing list and yes, the host
I got the error was in the FreeBSD jail.
http://rubyforge.org/pipermail/mongrel-unicorn/2009-December/000205.html
http://rubyforge.org/pipermail/mongrel-unicorn/2009-December/000212.html

I dug into the problem and found some interesting thing. I want to
share it and the solution here.

* Why does it happen in the Jail?
When you bind 0.0.0.0 address to the socket on a Jail host, Jail
doesn't bind 0.0.0.0. Instead of it, it binds to the IP address of the
host such as 10.100.1.50, 192.168.1.50, etc.
Then the problem happens in Unicorn::HttpServer#inherit_listeners!
when it gets signal like USR2.
https://github.com/defunkt/unicorn/blob/406b8b0e2ed6e5be34d8ec3cd4b16048233c2856/lib/unicorn/http_server.rb#L703-731

Here you get 10.100.1.50 (for example) as inherited address from
ENV['UNICORN_FD'] but 0.0.0.0. So Unicorn tries to bind to 0.0.0.0
(again) at line 728. Then it fails because it is actually in use.

* How to solve the issue?
Thankfully unicorn.conf is ruby script so that I could write a logic
for Jail hosts without specifying IP address of those hosts (which we
want to avoid since we deploy it multiple servers). I share the
configuration file.
https://gist.github.com/1122965

Then Unicorn flies in the jail happily.

Tatsuya Ono
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Strange quit behavior
  2011-08-02 21:53  0% ` Eric Wong
@ 2011-08-02 22:46  0%   ` James Cox
  0 siblings, 0 replies; 200+ results
From: James Cox @ 2011-08-02 22:46 UTC (permalink / raw)
  To: Eric Wong, unicorn list

On Tue, Aug 2, 2011 at 17:53, Eric Wong <normalperson@yhbt.net> wrote:
> James Cox <james@imaj.es> wrote:
>> Hey,
>>
>> So here are some tasks for managing unicorn:
>>
>> https://gist.github.com/1121076
>
> Can we ignore the :restart task?   It's a bit fragile since it doesn't
> wait for the old process to die (SIGTERM means
> kill-as-quickly-as-possible, but given a loaded system it can still take
> some time).

We mostly restart (what surprise). What pattern works best here for
that? (speed of deploy is important, but definitely assume a long boot
time)


>
>> I've found that it's very unreliable for quitting / terminating
>> unicorn and restarting with new code. When doing rapid deployments
>> particularly, i've found that i have to go in and kill -9 the master
>> process, and start again.
>
> If you SIGQUIT/SIGTERM before the app is loaded, the signal could
> be ignored.  This behavior should probably change...
>
>> any thoughts on why it seems ineffective?
>>
>> Thanks.
>
> Which version of Unicorn are you using?  I recall some minor tweaks
> to avoid/minimize race conditions over the years so maybe some
> are fixed.
>
gem 'unicorn' - so whatever seems up to date. My lock says 4.0.1

>> PS: here's the unicorn config:
>>
>> https://gist.github.com/0dd07c5ad00c56d161c7
>
> Avoid the top piece of the before_fork hook to TTOU workers, it's
> needlessly complex for most deployments unless you're really
> memory-constrained.
>

So what should that look like? all but that nr-workers stuff? can i
just remove the before fork? and what would you say is a super good
unicorn config to start from?

thanks!
james
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Re: Strange quit behavior
  2011-08-02 20:34  0% ` Alex Sharp
@ 2011-08-02 21:54  0%   ` Eric Wong
    0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2011-08-02 21:54 UTC (permalink / raw)
  To: unicorn list

Alex Sharp <ajsharp@gmail.com> wrote:
> > I've found that it's very unreliable for quitting / terminating
> > unicorn and restarting with new code. When doing rapid deployments
> > particularly, i've found that i have to go in and kill -9 the master
> > process, and start again.
> 
> We've noticed some of this behavior as well. We've seen the new master
> start spinning and consume 100% cpu (according to top). The old master
> and workers are still running and working, but the new master just
> hangs, and we have to kill -9.

Can you try to strace (or equivalent) the old master to see what's using
100% CPU?

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: Strange quit behavior
  2011-08-02 20:09  2% Strange quit behavior James Cox
  2011-08-02 20:34  0% ` Alex Sharp
@ 2011-08-02 21:53  0% ` Eric Wong
  2011-08-02 22:46  0%   ` James Cox
  1 sibling, 1 reply; 200+ results
From: Eric Wong @ 2011-08-02 21:53 UTC (permalink / raw)
  To: unicorn list; +Cc: James Cox

James Cox <james@imaj.es> wrote:
> Hey,
> 
> So here are some tasks for managing unicorn:
> 
> https://gist.github.com/1121076

Can we ignore the :restart task?   It's a bit fragile since it doesn't
wait for the old process to die (SIGTERM means
kill-as-quickly-as-possible, but given a loaded system it can still take
some time).

> I've found that it's very unreliable for quitting / terminating
> unicorn and restarting with new code. When doing rapid deployments
> particularly, i've found that i have to go in and kill -9 the master
> process, and start again.

If you SIGQUIT/SIGTERM before the app is loaded, the signal could
be ignored.  This behavior should probably change...

> any thoughts on why it seems ineffective?
> 
> Thanks.

Which version of Unicorn are you using?  I recall some minor tweaks
to avoid/minimize race conditions over the years so maybe some
are fixed.

> PS: here's the unicorn config:
> 
> https://gist.github.com/0dd07c5ad00c56d161c7

Avoid the top piece of the before_fork hook to TTOU workers, it's
needlessly complex for most deployments unless you're really
memory-constrained.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: Strange quit behavior
  2011-08-02 20:09  2% Strange quit behavior James Cox
@ 2011-08-02 20:34  0% ` Alex Sharp
  2011-08-02 21:54  0%   ` Eric Wong
  2011-08-02 21:53  0% ` Eric Wong
  1 sibling, 1 reply; 200+ results
From: Alex Sharp @ 2011-08-02 20:34 UTC (permalink / raw)
  To: unicorn list

> I've found that it's very unreliable for quitting / terminating
> unicorn and restarting with new code. When doing rapid deployments
> particularly, i've found that i have to go in and kill -9 the master
> process, and start again.

We've noticed some of this behavior as well. We've seen the new master
start spinning and consume 100% cpu (according to top). The old master
and workers are still running and working, but the new master just
hangs, and we have to kill -9. Our solution was to add the following
to our unicorn config, which *seems* to have solved the problem:

Unicorn::HttpServer::START_CTX[0] =
"#{path_to_app}/shared/bundle/ruby/1.9.1/bin/unicorn_rails"

This is outlined a bit here: http://unicorn.bogomips.org/Sandbox.html.
I'm not really sure why this seems to have fixed the problem -- all I
can tell you is that we haven't seen it since adding this line to the
config.

- Alex
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Strange quit behavior
@ 2011-08-02 20:09  2% James Cox
  2011-08-02 20:34  0% ` Alex Sharp
  2011-08-02 21:53  0% ` Eric Wong
  0 siblings, 2 replies; 200+ results
From: James Cox @ 2011-08-02 20:09 UTC (permalink / raw)
  To: unicorn list

Hey,

So here are some tasks for managing unicorn:

https://gist.github.com/1121076

I've found that it's very unreliable for quitting / terminating
unicorn and restarting with new code. When doing rapid deployments
particularly, i've found that i have to go in and kill -9 the master
process, and start again.

any thoughts on why it seems ineffective?

Thanks.

PS: here's the unicorn config:

https://gist.github.com/0dd07c5ad00c56d161c7

James
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Unicorn vs Apache
  2011-07-11 16:07  2% Unicorn vs Apache Matt Smith
@ 2011-07-11 18:45  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2011-07-11 18:45 UTC (permalink / raw)
  To: unicorn list

Matt Smith <matthewcalebsmith@gmail.com> wrote:
> Hello all,
> 
> I have always deploys rails apps with unicorn and nginx as a reverse
> proxy in the past. However, I am working with a new firm and they would
> like to use Apache with mod_pagespeed in front of unicorn.
> 
> We will be deploying a rails 3.1 app with streaming. To me,
> mod_pagespeed does not seem like a magic bullet, as it appears to be a
> collection of best practices which have to be rewritten as a page is
> being served.

note: mod_pagespeed is totally independent if Rails 3.1 streaming.

Keep in mind the most important feature of nginx Unicorn relies on (full
request/response buffering) also defeats Rails 3.1 streaming because of
how it's currently implemented.

> Why not code with the best practices in mind as to not
> have to scan and yet again reprocess the content?

My thoughts exactly :)  But implementing those best practices
isn't cheap, either.  If you have to run mod_pagespeed, the safest
configuration would be:

  nginx <-> mod_pagespeed <-> unicorn

Of course that would introduce latency.  Maybe there's Rack middleware
that works like mod_pagespeed...

> Searching on the web, I find few entries about mod_page speed and rails,
> and relatively few entries about apache with unicorn.

Apache + Unicorn is still unsupported since (as far as anybody knows),
it doesn't fully buffer responses and requests to completely isolate
Unicorn from the harmful effects of slow clients.

> To me it seems that using best practices, with appropriate use of
> caching, and nginx would be a better solution than apache with
> mod_pagespeed.

I agree.

> Questions:
> 
> 1) Does nginx provide any necessary services to unicorn, that apache
> cannot provide?

nginx provides full request + response buffering (all uploads, and large
response bodies that can't fit into socket buffers).  nginx also
provides inexpensive handling of idle keepalive connections; so you
can have a lot of idle connections for little memory usage.

Apache 2.x mpm_event /should/ be as good *if* (and only if) it an
provide the full request/response buffering; but I don't have any
experience with it.  mpm_worker can be fairly efficient with Linux+NPTL
if you can use smaller pthreads stack sizes (and are using 64-bit).

> 2) If apache can provide all necessary services, does it do them as well
> as nginx.

Buffering + keepalive maintenance is the most important thing I care
about, and as far as I know, Apache doesn't do that as well as nginx.

> 3) We would like the fastest user experience possible. Does apache with
> mod_pagespeed hold any weight here over nginx?

The full request/response buffering of nginx also hurts latency a
little, so the user experience is slightly worse under low load.
However, nginx does the best job of keeping things going under high
load.

> 4) Part of a fast user experience is how fast you get the page, correct?
> Seems like nginx has the upper hand here, due to nginx's nio vs apache's
> threading.

Not sure what "nio" means... non-blocking I/O?  Apache can use mpm_event
which is like the non-blocking + event loop nginx uses, but AFAIK the
buffering of requests responses is not like what nginx implements.

> 5) Would it make since to put nginx in front of apache w/mod_pagespeed
> (or visa versa), to use the applicable mod_pagespeed filters? Or is this
> a bad idea to begin with?

nginx <-> mod_pagespeed <-> unicorn should be alright, but of course not
ideal because of the extra copying + latency involved.

For non-Ruby/Rack apps, I've always recommended nginx sit in front of
any Apache 1.3 or 2.x+mpm_prefork apps that are exposed to slow clients,
too.

> 6) Is there anything else I am missing? Are there any specific resources
> I need to look at?

If you're willing to take a risk with Rainbows!, I posted some notes
about making it work with Rails 3.1 streaming here:

http://thread.gmane.org/gmane.comp.lang.ruby.rainbows.general/229

Keep in mind nobody I know of uses Rainbows! in production; but
it /should/ be able to work without nginx (or any proxy).

> 7) Should we go with nginx or apache? (Opinions ok.)

nginx remains the only supported reverse proxy for Unicorn at this
moment.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Unicorn vs Apache
@ 2011-07-11 16:07  2% Matt Smith
  2011-07-11 18:45  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Matt Smith @ 2011-07-11 16:07 UTC (permalink / raw)
  To: mongrel-unicorn

Hello all,

I have always deploys rails apps with unicorn and nginx as a reverse
proxy in the past. However, I am working with a new firm and they would
like to use Apache with mod_pagespeed in front of unicorn.

We will be deploying a rails 3.1 app with streaming. To me,
mod_pagespeed does not seem like a magic bullet, as it appears to be a
collection of best practices which have to be rewritten as a page is
being served. Why not code with the best practices in mind as to not
have to scan and yet again reprocess the content?

Searching on the web, I find few entries about mod_page speed and rails,
and relatively few entries about apache with unicorn.

To me it seems that using best practices, with appropriate use of
caching, and nginx would be a better solution than apache with
mod_pagespeed.

Questions:

1) Does nginx provide any necessary services to unicorn, that apache
cannot provide?

2) If apache can provide all necessary services, does it do them as well
as nginx.

3) We would like the fastest user experience possible. Does apache with
mod_pagespeed hold any weight here over nginx?

4) Part of a fast user experience is how fast you get the page, correct?
Seems like nginx has the upper hand here, due to nginx's nio vs apache's
threading.

5) Would it make since to put nginx in front of apache w/mod_pagespeed
(or visa versa), to use the applicable mod_pagespeed filters? Or is this
a bad idea to begin with?

6) Is there anything else I am missing? Are there any specific resources
I need to look at?

7) Should we go with nginx or apache? (Opinions ok.)

Much thanks,

Matt Smith



_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: problem setting multiple cookies
  2011-06-22  4:18  1%                       ` Jason Su
@ 2011-06-22  6:02  0%                         ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2011-06-22  6:02 UTC (permalink / raw)
  To: unicorn list

Jason Su <jason@lookbook.nu> wrote:
> Hey Eric,
> 
> Actually I think I am using OldRails -- don't know what it is or why
> I'm doing it, but this is in my config.ru:

It might be because you were on Rails 2.2 or earlier and upgraded?

> map '/' do
>   if RAILS_ENV != 'production'
>     begin
>       require 'rack/bug'
>       use Rack::Bug, :password => nil
>     rescue LoadError
>     end
> 
>     use Rails::Rack::Static
>   end
> 
>   run Unicorn::App::OldRails.new
> end
> 
> Should I not be using OldRails?

OldRails was for Rails <= 2.2, so you should probably try this instead:

  run ActionController::Dispatcher.new

This should use the pure Rack backend that avoids the old CGI
and CGIWrapper code which Rails <=2.2 relied on.

> I'm also trying to upgrade to Rails 2.3.11 again. Here's the error
> that's stopping me:
> 
> You have a nil object when you didn't expect it!
> You might have expected an instance of Array.
> The error occurred while evaluating nil.split You have a nil object
> when you didn't expect it!

<snip>

> I found this, which I think is somehow related to the fix:
> https://gist.github.com/826692
> 
> Do you see anything that looks familiar?

I haven't seen it before, but it looks to be on the right track.

OldRails uses the CGIWrapper code which is based on the original
code in Mongrel, too.

If you avoid CGIWrapper, you should be able to avoid the
workarounds/issues with it like the above gist.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: problem setting multiple cookies
  @ 2011-06-22  4:18  1%                       ` Jason Su
  2011-06-22  6:02  0%                         ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jason Su @ 2011-06-22  4:18 UTC (permalink / raw)
  To: unicorn list

Hey Eric,

Actually I think I am using OldRails -- don't know what it is or why
I'm doing it, but this is in my config.ru:

map '/' do
  if RAILS_ENV != 'production'
    begin
      require 'rack/bug'
      use Rack::Bug, :password => nil
    rescue LoadError
    end

    use Rails::Rack::Static
  end

  run Unicorn::App::OldRails.new
end

Should I not be using OldRails?



I'm also trying to upgrade to Rails 2.3.11 again. Here's the error
that's stopping me:

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.split You have a nil object
when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.split
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/actionpack-2.3.11/lib/action_controller/cgi_process.rb:52:in
`dispatch_cgi'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/actionpack-2.3.11/lib/action_controller/dispatcher.rb:101:in
`dispatch_cgi'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/actionpack-2.3.11/lib/action_controller/dispatcher.rb:27:in
`dispatch'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/app/old_rails.rb:22:in
`call'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/app/old_rails/static.rb:56:in
`call'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/rack-1.1.2/lib/rack/urlmap.rb:47:in
`call'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/rack-1.1.2/lib/rack/urlmap.rb:41:in
`each'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/rack-1.1.2/lib/rack/urlmap.rb:41:in
`call'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:545:in
`process_client'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/oob_gc.rb:60:in
`process_client'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:623:in
`worker_loop'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:621:in
`each'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:621:in
`worker_loop'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:509:in
`spawn_missing_workers'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:507:in
`fork'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:507:in
`spawn_missing_workers'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:503:in
`each'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:503:in
`spawn_missing_workers'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:516:in
`maintain_worker_count'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn/http_server.rb:166:in
`start'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/lib/unicorn.rb:30:in
`run'
/Users/kittyflat/.rvm/rubies/ruby-1.8.7-p334/lib/ruby/gems/1.8/gems/unicorn-3.6.2/bin/unicorn_rails:208
/Users/kittyflat/.rvm/gems/ruby-1.8.7-p334/bin/unicorn_rails:19:in `load'
/Users/kittyflat/.rvm/gems/ruby-1.8.7-p334/bin/unicorn_rails:19

I found this, which I think is somehow related to the fix:
https://gist.github.com/826692

Do you see anything that looks familiar?

Thanks,
jason
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 1%]

* Re: problem setting multiple cookies
  2011-06-21  3:38  2%               ` Jason Su
@ 2011-06-21 18:29  0%                 ` Eric Wong
    0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2011-06-21 18:29 UTC (permalink / raw)
  To: unicorn list

Jason Su <jason@lookbook.nu> wrote:
> I'm using unicorn 3.6.2
> 
> What I was trying to say is that the JoinCookie middleware didn't do
> anything. headers["Set-Cookie"] is a string, and the _test_session
> cookie was always being set correctly. The problem happens only when I
> try to set multiple cookies myself.

Yeah, you're not getting an Array (at least not at that stage) as I
semi-hoped so JoinCookie wouldn't solve it.

> Here's the output of p headers in JoinCookie again
> 
> {"X-Runtime"=>"92", "Content-Type"=>"text/html",
> "Content-Length"=>"1", "Set-Cookie"=>"first=; domain=.test.com;
> path=/; expires=Thu, 01-Jan-1970 00:00:00 GMTsecond=1;
> domain=.test.com; path=/; expires=Mon, 21-Jun-2021 03:35:30
> GMT\n_test_session=1804126e6bad8cf4b25602b74218f003; domain=.test.com;
> path=/; HttpOnly", "Cache-Control"=>"no-cache"}
> 
> Where is JoinCookie is getting it's headers from?

app.call(env) is what dispatches your Rails app, so the headers are what
Rails (and middlewares further down the stack) sets.

Any luck with looking at Rails changes that might impact this?

At this point it's clear it's a Rails/Rack issue (and for an old
version, at that) and not something that can be done with Unicorn.

Maybe you can fix whatever the Rails v2.3.8..v2.3.11 upgrade broke
in your app, but whatever's broken is broken at the application layer
and not in Unicorn.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: problem setting multiple cookies
  2011-06-21  3:15  0%             ` Eric Wong
@ 2011-06-21  3:38  2%               ` Jason Su
  2011-06-21 18:29  0%                 ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jason Su @ 2011-06-21  3:38 UTC (permalink / raw)
  To: unicorn list

I'm using unicorn 3.6.2

What I was trying to say is that the JoinCookie middleware didn't do
anything. headers["Set-Cookie"] is a string, and the _test_session
cookie was always being set correctly. The problem happens only when I
try to set multiple cookies myself.

Here's the output of p headers in JoinCookie again

{"X-Runtime"=>"92", "Content-Type"=>"text/html",
"Content-Length"=>"1", "Set-Cookie"=>"first=; domain=.test.com;
path=/; expires=Thu, 01-Jan-1970 00:00:00 GMTsecond=1;
domain=.test.com; path=/; expires=Mon, 21-Jun-2021 03:35:30
GMT\n_test_session=1804126e6bad8cf4b25602b74218f003; domain=.test.com;
path=/; HttpOnly", "Cache-Control"=>"no-cache"}

Where is JoinCookie is getting it's headers from?


On Mon, Jun 20, 2011 at 8:15 PM, Eric Wong <normalperson@yhbt.net> wrote:
> Jason Su <jason@lookbook.nu> wrote:
>> Here's what I get in the response, with the _test_session cookie line:
>>
>> Set-Cookie:first=; domain=.test.com; path=/; expires=Thu, 01-Jan-1970
>> 00:00:00 GMTsecond=1; domain=.test.com; path=/; expires=Mon,
>> 21-Jun-2021 02:40:27 GMT
>> _test_session=40658e37c9e8b39c5f8389e09682c47b; domain=.test.com;
>> path=/; HttpOnly
>>
>> It doesn't add a second Set-Cookie: line, but the line break seems to work...
>
> Huh?  Which version of unicorn are you using?  The /\n/ splitting in
> headers has been in the earliest releases of unicorn...
>
>> 2.3.11 is giving me a ton of trouble.. I've tried it before.. and I
>> just tried again with little luck... Is there anything that I can just
>> grab from the commits to make it work on 2.3.8?
>
> Look at the individual commits in rails.git.  I just used
>
>  git log -p v2.3.8..v2.3.11 -- actionpack
>
> in rails.git to find those for you.   I just looked for things that
> would manipulate cookies/headers.
>
> --
> Eric Wong
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying
>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: problem setting multiple cookies
  2011-06-21  2:48  2%           ` Jason Su
@ 2011-06-21  3:15  0%             ` Eric Wong
  2011-06-21  3:38  2%               ` Jason Su
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2011-06-21  3:15 UTC (permalink / raw)
  To: unicorn list

Jason Su <jason@lookbook.nu> wrote:
> Here's what I get in the response, with the _test_session cookie line:
> 
> Set-Cookie:first=; domain=.test.com; path=/; expires=Thu, 01-Jan-1970
> 00:00:00 GMTsecond=1; domain=.test.com; path=/; expires=Mon,
> 21-Jun-2021 02:40:27 GMT
> _test_session=40658e37c9e8b39c5f8389e09682c47b; domain=.test.com;
> path=/; HttpOnly
> 
> It doesn't add a second Set-Cookie: line, but the line break seems to work...

Huh?  Which version of unicorn are you using?  The /\n/ splitting in
headers has been in the earliest releases of unicorn...

> 2.3.11 is giving me a ton of trouble.. I've tried it before.. and I
> just tried again with little luck... Is there anything that I can just
> grab from the commits to make it work on 2.3.8?

Look at the individual commits in rails.git.  I just used

  git log -p v2.3.8..v2.3.11 -- actionpack

in rails.git to find those for you.   I just looked for things that
would manipulate cookies/headers.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: problem setting multiple cookies
  @ 2011-06-21  2:48  2%           ` Jason Su
  2011-06-21  3:15  0%             ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jason Su @ 2011-06-21  2:48 UTC (permalink / raw)
  To: unicorn list

Hey Eric,

I called p headers in JoinCookie before manipulating the headers at
all, so what you saw was probably already set correctly by rails. I'm
not setting the _test_session cookie myself.

Here's what I get in the response, with the _test_session cookie line:

Set-Cookie:first=; domain=.test.com; path=/; expires=Thu, 01-Jan-1970
00:00:00 GMTsecond=1; domain=.test.com; path=/; expires=Mon,
21-Jun-2021 02:40:27 GMT
_test_session=40658e37c9e8b39c5f8389e09682c47b; domain=.test.com;
path=/; HttpOnly

It doesn't add a second Set-Cookie: line, but the line break seems to work...

2.3.11 is giving me a ton of trouble.. I've tried it before.. and I
just tried again with little luck... Is there anything that I can just
grab from the commits to make it work on 2.3.8?

Thanks,
Jason
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: problem setting multiple cookies
  @ 2011-06-21  1:35  2%         ` Jason Su
    1 sibling, 0 replies; 200+ results
From: Jason Su @ 2011-06-21  1:35 UTC (permalink / raw)
  To: unicorn list

Sorry to post again, but the response headers were captured after I
cleared my cookies, so it's not consistent with the first output.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: problem setting multiple cookies
  @ 2011-06-21  0:25  2%   ` Jason Su
    0 siblings, 1 reply; 200+ results
From: Jason Su @ 2011-06-21  0:25 UTC (permalink / raw)
  To: unicorn list

Hey Eric,

I'm using Rails 2.3.8, and Rack 1.1.2

I'm setting cookies like this:

cookies[:user_id] = { :domain => ".#{domain}", :value => "#{user.id}",
:expires => 10.years.from_now }

How can I get the response to show up correctly, with individual
Set-Cookie lines for each?

Thanks again,

Jason



On Mon, Jun 20, 2011 at 5:06 PM, Eric Wong <normalperson@yhbt.net> wrote:
> Jason Su <jason@lookbook.nu> wrote:
>> Hey guys,
>>
>> When I try to set multiple cookies in one request, the 'Set-Cookies'
>> header in the http response doesn't look right -- all the cookies are
>> set without any line breaks.
>>
>> Looks like this:
>>
>> Set-Cookie:first=; domain=.domain.com; path=/; expires=Thu,
>> 01-Jan-1970 00:00:00 GMTsecond=1; domain=.domain.com; path=/;
>> expires=Sun, 20-Jun-2021 23:34:30 GMT
>
> Which versions of Rails and Rack are you using?  How is your app
> setting cookies?
>
> Older versions of Rack may have specified a different delimiter (or an
> Array).  Current versions only use "\n" inside a string, so it should be
> something like:
>
>  headers["Set-Cookie"] = "first=33; path=/\n" \
>                          "second=; path=/\n" \
>                          "third=; path=/"
>
> Using Rack::Utils.set_cookie_header! (or a Rails-level wrapper) is
> recommended, though.
>
>> Should look more like this, which is what I got from a fresh Rails project test:
>>
>> Set-Cookie:first=33; path=/
>> second=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
>> third=69; path=/
>
> The response should have 3 individual Set-Cookie lines:
>
>  Set-Cookie: first=33; path=/
>  Set-Cookie: second=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
>  Set-Cookie: third=69; path=/
>
>> In the first example, neither cookies (first/second) are set. The
>> second example, all 3 cookies are set.
>>
>> I think it's happening somewhere here, but I can't figure out what's
>> going on... http://bogomips.org/unicorn.git/tree/lib/unicorn/cgi_wrapper.rb
>
> The code that does this transformation of the Rack header to the
> client socket is here:
>
> http://bogomips.org/unicorn.git/tree/lib/unicorn/http_response.rb
>
> cgi_wrapper.rb is only for Rails <= 2.2, where it is called before
> http_response.rb
>
> --
> Eric Wong
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying
>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: workers not utilizing multiple CPUs
       [not found]               ` <BANLkTikC5D+0pUDRDgvQbzu=dpwmdKNY=A@mail.gmail.com>
@ 2011-06-01  6:51  2%             ` Nate Clark
  0 siblings, 0 replies; 200+ results
From: Nate Clark @ 2011-06-01  6:51 UTC (permalink / raw)
  To: unicorn list

Thanks for the responses, all.

Eric, you were right, our load was not enough. We had just started on
load testing our app, and I think we started with too many app servers
and not enough load. Once we cranked up the load and used fewer
instances, we're now definitely seeing all CPU cores being utilized. I
was not aware that the kernel would optimize like you described.

Once we did start seeing heavier load, our collectd data and htop were
reporting usage on the virtual cores correctly.
Thanks again, very happy with the results so far,

Nate

On Wed, Jun 1, 2011 at 2:35 PM, Nate Clark <nate@pivotallabs.com> wrote:
>
> Thanks for the responses, all.
> Eric, you were right, our load was not enough. We had just started on load testing our app, and I think we started with too many app servers and not enough load. Once we cranked up the load and used fewer instances, we're now definitely seeing all CPU cores being utilized. I was not aware that the kernel would optimize like you described.
> Once we did start seeing heavier load, our collectd data and htop were reporting usage on the virtual cores correctly.
> Thanks again, very happy with the results so far,
> Nate
> On Tue, May 31, 2011 at 11:55 PM, Clifton King <cliftonk@gmail.com> wrote:
>>
>> Thanks Eric, I had expected that to be the case (we are under light
>> load as of now).
>>
>> On Tue, May 31, 2011 at 10:48 AM, Eric Wong <normalperson@yhbt.net> wrote:
>> > Clifton King <cliftonk@gmail.com> wrote:
>> >> We experience the same problem. I believe the problem has more to do
>> >> with the kernel CPU scheduler than anything else. If you figure put a
>> >> reliable way to spread the load, I'd like to hear it.
>> >
>> > Load not being spread is /not/ a problem unless there are requests that
>> > get stuck in the listen queue.
>> >
>> > If no requests are actually stuck in the queue (light load), the kernel
>> > is right to put requests into the most recently used worker since it can
>> > get better CPU cache behavior this way.
>> >
>> >
>> > == The real problem
>> >
>> > Under high loads (many cores, fast responses), Unicorn currently uses
>> > more resources because of non-blocking accept() + select().  This isn't
>> > a noticeable problem for most machines (1-16 cores).
>> >
>> > Future versions of Unicorn may take advantage of /blocking/ accept()
>> > optimizations under Linux.  Rainbows! already lets you take advantage
>> > of this behavior if you meet the following requirements:
>> >
>> > * Ruby 1.9.x under Linux
>> > * only one listen socket (if worker_connections == 1 under Rainbows!)
>> > * use ThreadPool|XEpollThreadPool|XEpollThreadSpawn|XEpoll
>> >
>> > I haven't had a chance to benchmark any of this on very big machines so
>> > I have no idea how well it actually works compared to Unicorn, only how
>> > well it works in theory :)
>> >
>> >
>> > Blocking accept() under Ruby 1.9.x + Linux should distribute load evenly
>> > across workers in all situations, even in the non-busy cases where load
>> > distribution doesn't matter (your case :).
>> >
>> > [1] - http://rainbows.rubyforge.org/Rainbows/XEpollThreadPool.html
>> >
>> > --
>> > Eric Wong
>> > _______________________________________________
>> > Unicorn mailing list - mongrel-unicorn@rubyforge.org
>> > http://rubyforge.org/mailman/listinfo/mongrel-unicorn
>> > Do not quote signatures (like this one) or top post when replying
>> >
>> _______________________________________________
>> Unicorn mailing list - mongrel-unicorn@rubyforge.org
>> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
>> Do not quote signatures (like this one) or top post when replying
>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: leading/trailing linear whitespace in headers
  2011-05-30  7:02  2%       ` Lawrence Pit
@ 2011-05-31  4:49  0%         ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2011-05-31  4:49 UTC (permalink / raw)
  To: Lawrence Pit; +Cc: unicorn list

Lawrence Pit <lawrence.pit@gmail.com> wrote:
> Cheered too early after all, just got one again:

Can you ever reproduce this with nginx in front?  Does anybody else
on any *BSD platform get this?

Btw, please don't waste my bandwidth top posting, I'd rather people
not quote at all than top post, thanks!

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: leading/trailing linear whitespace in headers
  2011-05-27  7:32  0%     ` Lawrence Pit
@ 2011-05-30  7:02  2%       ` Lawrence Pit
  2011-05-31  4:49  0%         ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Lawrence Pit @ 2011-05-30  7:02 UTC (permalink / raw)
  To: unicorn list


Cheered too early after all, just got one again:

E, [2011-05-30T16:59:36.005000 #7351] ERROR -- : worker=0 PID:7352 
timeout (60.004931s > 60s), killing
E, [2011-05-30T16:59:36.058276 #7351] ERROR -- : reaped 
#<Process::Status: pid=7352,signaled(SIGKILL=9)> worker=0
I, [2011-05-30T16:59:36.059122 #7351]  INFO -- : worker=0 spawning...
I, [2011-05-30T16:59:36.062990 #8505]  INFO -- : worker=0 spawned pid=8505
I, [2011-05-30T16:59:36.063395 #8505]  INFO -- : Refreshing Gem list
=> ruby 1.8.7 (i686-darwin10.4.0) [2010-04-19] Ruby Enterprise Edition
worker=0 ready



Cheers,
Lawrence

> Hi Eric,
>
> I thought I'll wait a few days before replying just to make sure: 
> believe it or not, since I upgraded 4 days ago to kgio 2.4.1 not once 
> did I run into this issue. While before that unicorn would timeout at 
> least once every hour, sometimes a dozen times in a row.
>
> I don't run through nginx on my dev machine, just straight into 
> unicorn_rails using mostly Chrome 11 and FF 4. I use:
>
> ruby 1.8.7 (2010-04-19 patchlevel 253) [i686-darwin10.4.0], MBARI 
> 0x6770, Ruby Enterprise Edition 2010.02
>
>
>
> Cheers,
> Lawrence
>
>> Hi Eric,
>>> Since I upgraded from unicorn 1 to 3, Unicorn will freeze for 60
>>> seconds at random moments, and then restart. Only on my dev machine.
>>> Not in 1 project, but in all my projects that use unicorn. I get no
>>> log output, it just freezes for 60 seconds. Strangely this happens
>>> significantly more often with using Chrome as a client than with
>>> Firefox.
>> Even through nginx?  Which Ruby version is it?
>>
>> The bug I'm referring to in this thread is only for /request/ headers,
>> and I don't think any common HTTP clients send trailing whitespace.
>>
>>> So I hope this will fix that, though I have no idea if it's related
>>> in any way.  I was thinking it may also have to do with unicorn not
>>> officially supporting Mac OS X.
>> I'm sure you're not the only OS X Unicorn user, perhaps somebody else
>> knows what's going on...
>>
>> If it's kgio-related, but I think Jeremy fixed all the issues under
>> OpenBSD with kgio 2.4.0 and some of that probably helped other
>> *BSD-based systems.  I've been meaning to setup a VM on FreeBSD
>> again but haven't had the time.
>>
>

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: leading/trailing linear whitespace in headers
  2011-05-21  5:57  2%   ` Eric Wong
@ 2011-05-27  7:32  0%     ` Lawrence Pit
  2011-05-30  7:02  2%       ` Lawrence Pit
  0 siblings, 1 reply; 200+ results
From: Lawrence Pit @ 2011-05-27  7:32 UTC (permalink / raw)
  To: unicorn list

Hi Eric,

I thought I'll wait a few days before replying just to make sure: 
believe it or not, since I upgraded 4 days ago to kgio 2.4.1 not once 
did I run into this issue. While before that unicorn would timeout at 
least once every hour, sometimes a dozen times in a row.

I don't run through nginx on my dev machine, just straight into 
unicorn_rails using mostly Chrome 11 and FF 4. I use:

ruby 1.8.7 (2010-04-19 patchlevel 253) [i686-darwin10.4.0], MBARI 
0x6770, Ruby Enterprise Edition 2010.02



Cheers,
Lawrence

> Hi Eric,
>> Since I upgraded from unicorn 1 to 3, Unicorn will freeze for 60
>> seconds at random moments, and then restart. Only on my dev machine.
>> Not in 1 project, but in all my projects that use unicorn. I get no
>> log output, it just freezes for 60 seconds. Strangely this happens
>> significantly more often with using Chrome as a client than with
>> Firefox.
> Even through nginx?  Which Ruby version is it?
>
> The bug I'm referring to in this thread is only for /request/ headers,
> and I don't think any common HTTP clients send trailing whitespace.
>
>> So I hope this will fix that, though I have no idea if it's related
>> in any way.  I was thinking it may also have to do with unicorn not
>> officially supporting Mac OS X.
> I'm sure you're not the only OS X Unicorn user, perhaps somebody else
> knows what's going on...
>
> If it's kgio-related, but I think Jeremy fixed all the issues under
> OpenBSD with kgio 2.4.0 and some of that probably helped other
> *BSD-based systems.  I've been meaning to setup a VM on FreeBSD
> again but haven't had the time.
>

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: leading/trailing linear whitespace in headers
  @ 2011-05-21  5:57  2%   ` Eric Wong
  2011-05-27  7:32  0%     ` Lawrence Pit
  0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2011-05-21  5:57 UTC (permalink / raw)
  To: unicorn list

Lawrence Pit <lawrence.pit@gmail.com> wrote:
> Hi Eric,
> 
> Since I upgraded from unicorn 1 to 3, Unicorn will freeze for 60
> seconds at random moments, and then restart. Only on my dev machine.
> Not in 1 project, but in all my projects that use unicorn. I get no
> log output, it just freezes for 60 seconds. Strangely this happens
> significantly more often with using Chrome as a client than with
> Firefox.

Even through nginx?  Which Ruby version is it?

The bug I'm referring to in this thread is only for /request/ headers,
and I don't think any common HTTP clients send trailing whitespace.

> So I hope this will fix that, though I have no idea if it's related
> in any way.  I was thinking it may also have to do with unicorn not
> officially supporting Mac OS X.

I'm sure you're not the only OS X Unicorn user, perhaps somebody else
knows what's going on...

If it's kgio-related, but I think Jeremy fixed all the issues under
OpenBSD with kgio 2.4.0 and some of that probably helped other
*BSD-based systems.  I've been meaning to setup a VM on FreeBSD
again but haven't had the time.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Unicorn error
  2011-04-07 21:11  1% Unicorn error Ernesto Rocha
@ 2011-04-07 22:04  0% ` Clifton King
  0 siblings, 0 replies; 200+ results
From: Clifton King @ 2011-04-07 22:04 UTC (permalink / raw)
  To: unicorn list

Are permissions set correctly in all of your application folders?

/usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/util.rb:19:in `chown': Operation not permitted -
/home/user/project/shared/log/unicorn.stderr.log (Errno::EPERM)
       from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-

Looks to me you may need to do a chown -r app_user /app_directory/
where app_user is the user unicorn is running on. Does unicorn work
with sudo?

On Thu, Apr 7, 2011 at 4:11 PM, Ernesto Rocha <ernestorrocha@gmail.com> wrote:
>
> Hi guys,
>
> These days I'm experimenting a strange behavior from Unicorn. Now the issue is,
> when I try to access a page it's always times out, so, on browser I see:
>
> Nginx - "bad gateway - timed out" page error
>
> When i start unicorn with -d parameter I only see:
>
>
> {:daemonize=>false,
>  :app=>
>  #<Proc:0x00007feee244d2d8@/usr/local/rvm/gems/ruby-1.8.7-
> p302@project/gems/unicorn-3.5.0/bin/unicorn_rails:135>,
>  :unicorn_options=>
>  {:config_file=>"/home/user/project/current/config/unicorn.rb",
>   :listeners=>[]}}
> Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
> p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/cache
> Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
> p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/pids
> Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
> p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/sessions
> Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
> p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/sockets
>
>
> And at unicorn.stderr.log which is about 2gb now with these lines repeated over
> and over again:
>
> worker=0 spawning...
> worker=1 spawning...
> worker=0 spawned pid=30557
> /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/util.rb:19:in `chown': Operation not permitted -
> /home/user/project/shared/log/unicorn.stderr.log (Errno::EPERM)
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/util.rb:19:in `chown_logs'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/util.rb:18:in `each_object'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/util.rb:18:in `chown_logs'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/worker.rb:31:in `user'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:561:in `init_worker_process'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:578:in `worker_loop'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:495:in `spawn_missing_workers'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:492:in `fork'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:492:in `spawn_missing_workers'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:488:in `each'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:488:in `spawn_missing_workers'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:502:in `maintain_worker_count'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/http_server.rb:161:in `start'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn.rb:13:in `run'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/bin/unicorn_rails:208
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/bin/unicorn_rails:19:in
> `load'
>        from /usr/local/rvm/gems/ruby-1.8.7-p302@project/bin/unicorn_rails:19
> master process ready
> worker=1 spawned pid=30558
> /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
> 3.5.0/lib/unicorn/util.rb:19:in `chown': Operation not permitted -
> /home/user/project/shared/log/unicorn.stderr.log (Errno::EPERM)
> .... and on....
>
> $ rails -v
> Rails 3.0.6
>
> Using rack (1.2.2)
>
> $unicorn -v
> unicorn v3.5.0
>
> Ubuntu 10.10
>
> $ nginx -v
> nginx version: nginx/0.7.67
>
> I don't know what more information you may need. But, that's it. I don't have
> any idea to fix this. Any thought ?
>
> Thansk,
> Ernesto
>
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying
>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[relevance 0%]

* Unicorn error
@ 2011-04-07 21:11  1% Ernesto Rocha
  2011-04-07 22:04  0% ` Clifton King
  0 siblings, 1 reply; 200+ results
From: Ernesto Rocha @ 2011-04-07 21:11 UTC (permalink / raw)
  To: mongrel-unicorn


Hi guys,

These days I'm experimenting a strange behavior from Unicorn. Now the issue is, 
when I try to access a page it's always times out, so, on browser I see:

Nginx - "bad gateway - timed out" page error

When i start unicorn with -d parameter I only see:


{:daemonize=>false,
 :app=>
  #<Proc:0x00007feee244d2d8@/usr/local/rvm/gems/ruby-1.8.7-
p302@project/gems/unicorn-3.5.0/bin/unicorn_rails:135>,
 :unicorn_options=>
  {:config_file=>"/home/user/project/current/config/unicorn.rb",
   :listeners=>[]}}
Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/cache
Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/pids
Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/sessions
Exception `Errno::EEXIST' at /usr/local/rvm/rubies/ruby-1.8.7-
p302/lib/ruby/1.8/fileutils.rb:243 - File exists - tmp/sockets


And at unicorn.stderr.log which is about 2gb now with these lines repeated over 
and over again:

worker=0 spawning...
worker=1 spawning...
worker=0 spawned pid=30557
/usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/util.rb:19:in `chown': Operation not permitted - 
/home/user/project/shared/log/unicorn.stderr.log (Errno::EPERM)
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/util.rb:19:in `chown_logs'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/util.rb:18:in `each_object'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/util.rb:18:in `chown_logs'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/worker.rb:31:in `user'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:561:in `init_worker_process'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:578:in `worker_loop'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:495:in `spawn_missing_workers'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:492:in `fork'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:492:in `spawn_missing_workers'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:488:in `each'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:488:in `spawn_missing_workers'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:502:in `maintain_worker_count'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/http_server.rb:161:in `start'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn.rb:13:in `run'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/bin/unicorn_rails:208
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/bin/unicorn_rails:19:in 
`load'
	from /usr/local/rvm/gems/ruby-1.8.7-p302@project/bin/unicorn_rails:19
master process ready
worker=1 spawned pid=30558
/usr/local/rvm/gems/ruby-1.8.7-p302@project/gems/unicorn-
3.5.0/lib/unicorn/util.rb:19:in `chown': Operation not permitted - 
/home/user/project/shared/log/unicorn.stderr.log (Errno::EPERM)
.... and on....

$ rails -v
Rails 3.0.6

Using rack (1.2.2)

$unicorn -v
unicorn v3.5.0

Ubuntu 10.10

$ nginx -v
nginx version: nginx/0.7.67

I don't know what more information you may need. But, that's it. I don't have 
any idea to fix this. Any thought ?

Thansk,
Ernesto

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 1%]

* Re: [RFC/PATCH] Bundler/Sandbox documentation updates
  2011-03-09 10:39  2% ` Lawrence Pit
@ 2011-03-10  0:52  0%   ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2011-03-10  0:52 UTC (permalink / raw)
  To: unicorn list

Lawrence Pit <lawrence.pit@gmail.com> wrote:
> Hi Eric,
>
> I tested this out:

Thanks for taking your time to test.

> - in the before_exec and after_fork I log PATH GEM_HOME GEM_PATH  
> BUNDLE_GEMFILE for inspection after deployment
>
> - deploy latest release, make sure to shutdown unicorn, then start cleanly
>
> - prune all releases, except the last one
>
> - deploy latest release again (which only sets the BUNDLE_GEMFILE env var)
>
> I've tried this a few times, and everytime I get something like this:
>
> https://gist.github.com/331b0decab62fd58483c

Yikes, it looks like the UNICORN_FD env is getting clobbered somehow.
Not sure what could be causing that.

> If I revert back to setting the GEM_HOME, GEM_PATH and PATH in the  
> before_exec it works fine.

Which Bundler version is this with?

> PS. When deploying I use USR2 followed by QUIT
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: [RFC/PATCH] Bundler/Sandbox documentation updates
  @ 2011-03-09 10:39  2% ` Lawrence Pit
  2011-03-10  0:52  0%   ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Lawrence Pit @ 2011-03-09 10:39 UTC (permalink / raw)
  To: unicorn list

Hi Eric,

I tested this out:

- in the before_exec and after_fork I log PATH GEM_HOME GEM_PATH 
BUNDLE_GEMFILE for inspection after deployment

- deploy latest release, make sure to shutdown unicorn, then start cleanly

- prune all releases, except the last one

- deploy latest release again (which only sets the BUNDLE_GEMFILE env var)

I've tried this a few times, and everytime I get something like this:

https://gist.github.com/331b0decab62fd58483c

If I revert back to setting the GEM_HOME, GEM_PATH and PATH in the 
before_exec it works fine.


PS. When deploying I use USR2 followed by QUIT


Cheers,
Lawrence

> I started playing around with Bundler 1.0.10 today and noticed it's
> quite different than previous versions (based on my limited experiences)
> and the out-of-the-box experience is pretty good regarding (lack of)
> ENV pollution.
>
> Can any more experienced Bundler (and possibly Capistrano) users
> comment on the below changes and see if they make sense?
>
> Thanks in advance!
>
> diff --git a/Sandbox b/Sandbox
> index d101106..46dfb91 100644
> --- a/Sandbox
> +++ b/Sandbox
> @@ -45,11 +45,20 @@ This is no longer be an issue as of bundler 0.9.17
>
>   ref: http://mid.gmane.org/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com
>
> +=== BUNDLE_GEMFILE for Capistrano users
> +
> +You may need to set or reset the BUNDLE_GEMFILE environment variable in
> +the before_exec hook:
> +
> +        before_exec do |server|
> +          ENV["BUNDLE_GEMFILE"] = "/path/to/app/current/Gemfile"
> +        end
> +
>   === Other ENV pollution issues
>
> -You may need to set or reset BUNDLE_GEMFILE, GEM_HOME, GEM_PATH and PATH
> -environment variables in the before_exec hook as illustrated by
> -http://gist.github.com/534668
> +If you're using an older Bundler version (0.9.x), you may need to set or
> +reset GEM_HOME, GEM_PATH and PATH environment variables in the
> +before_exec hook as illustrated by http://gist.github.com/534668
>
>   == Isolate
>

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* license is NOT changing
@ 2011-02-16 19:11  2% Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2011-02-16 19:11 UTC (permalink / raw)
  To: mongrel-unicorn

Since Ruby 1.9.3dev is under the 2-clause BSD License (+ extra terms)
now, I've clarified Unicorn will be remaining under the same terms as
Ruby 1.8 and Mongrel 1.1.5[1].

I have zero interest (nor rights to relicense) the code in Unicorn to a
BSD license.

For the record, I'm personally a huge fan and proponent of the GPL,
but again I can't relicense existing code without the permission
of everybody that ever contributed to Mongrel/Unicorn, either.

While I am more than aware there are several reasons people aren't
comfortable with Unicorn, but the GPL should NOT be one of those
reasons.

Thanks for reading.


[1] avoiding confusion with Mongrel2 which is also under a BSD license

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Unicorn and HAProxy, 500 Internal errors after checks
  @ 2010-12-01 17:14  2%   ` Pierre
  0 siblings, 0 replies; 200+ results
From: Pierre @ 2010-12-01 17:14 UTC (permalink / raw)
  To: unicorn list

Dear Eric,

On Wed, Dec 1, 2010 at 5:52 PM, Eric Wong <normalperson@yhbt.net> wrote:
> Hi Pierre, HAProxy should be configured to send proper HTTP checks and
> not just TCP connection checks, the problem will go away then.

I understood this could be fixed this way and we will probably do that
soon. However, I think this is also the responsibility of Unicorn not
to reply anything when there's no request or at least log the error
somewhere :)

> Also, I
> can not recommend HAProxy unless you're certain all your clients are on
> a LAN and can be trusted to never trickle uploads nor reading large
> responses.

While I understand that uploads are very complicated to handle on the
stack (even nginx can be confused at upload sometimes), HAProxy proved
it was very good at managing tons of connections and high volume
traffic from the Internet. All the more so as it allows a very high
level of redundancy at a very small cost that cannot be achieved
simply otherwise. Do you have any pointers about your worrying
non-recommendation of HAProxy ?

As far as I'm concerned, I've used HAProxy in worldwide web context of
website serving up to 25 Millions pages with no problem, ever.

Thanks again for your support, :)
-- 
Pierre <oct@fotopedia.com>
Server Shepherd at http://www.fotopedia.com/
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: [ANN] unicorn 3.0.0 - disable rewindable input!
  @ 2010-11-22  1:21  2%         ` Jeremy Evans
  0 siblings, 0 replies; 200+ results
From: Jeremy Evans @ 2010-11-22  1:21 UTC (permalink / raw)
  To: unicorn list

On Sun, Nov 21, 2010 at 3:49 PM, Eric Wong <normalperson@yhbt.net> wrote:
> Jeremy Evans <jeremyevans0@gmail.com> wrote:
>> On Sat, Nov 20, 2010 at 8:05 PM, Eric Wong <normalperson@yhbt.net> wrote:
>> > Michael Guterl <mguterl@gmail.com> wrote:
>> >> We've been using Unicorn 1.x very successfully for some time with a
>> >> Rails 2.3 application.  Would you recommend upgrading to Unicorn 3.x?
>> >
>> > Yes if you're running a recent Linux (>= 2.6.28), it might help somewhat
>> > with performance (but may not be noticeable with a particular app).
>> > I haven't done extensive benchmarking, but the reduction of syscalls
>> > should help a bit, and newer versions will exploit some of that more.
> <snip>
>> > If you're willing to help iron out portability bugs to other kernels,
>> > then please upgrade and report back :)
>>
>> Just an anecdotal data point here, using OpenBSD-current.  Moving from
>> Unicorn 1.0 to 2.0 sped up the scaffolding_extensions integration test
>> suite by a factor of 3 (~10 seconds to ~3 seconds).  I'm guessing this
>> is due mainly to the use of kgio.  Hoping to upgrade to Unicorn 3.0
>> today.
>
> Wow!  I didn't expect that kind of performance improvement![1]  What
> is this test doing?  I'm actually suspicious there could be a bug
> somewhere in Unicorn or kgio causing wrong results :)
>
> Let us know how 3.0 goes, thanks!
>
> [1] - dalli reported 10-20%, which is closer to what I expected
>      (1-10%).

Looks like it must have been some other change that caused this speed
up, as after testing again with 1.x, I'm getting about the same in
terms of performance.  Sorry for the misinformation.

Jeremy
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* [PATCH] avoid unlinking actively listening sockets
  @ 2010-10-04  4:22  4%   ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2010-10-04  4:22 UTC (permalink / raw)
  To: unicorn list

While we've always unlinked dead sockets from nuked/leftover
processes, blindly unlinking them can cause unnecessary failures
when an active process is already listening on them.  We now
make a simple connect(2) check to ensure the socket is not in
use before unlinking it.

Thanks to Jordan Ritter for the detailed bug report leading to
this fix.

ref: http://mid.gmane.org/8D95A44B-A098-43BE-B532-7D74BD957F31@darkridge.com
---

  Eric Wong <normalperson@yhbt.net> wrote:
  > A simpler check would be to use connect(2) (but not make any HTTP request)
  > to see if the socket is alive.  Patch coming.

  s/simpler/better/

  Also pushed out to "master".  I guess a 1.1.4 release with this fix
  only is on the way since there isn't much else to release, yet.

 lib/unicorn/socket_helper.rb    |   10 ++++-
 t/t0011-active-unix-socket.sh   |   79 +++++++++++++++++++++++++++++++++++++++
 test/unit/test_socket_helper.rb |    9 ++++-
 3 files changed, 95 insertions(+), 3 deletions(-)
 create mode 100644 t/t0011-active-unix-socket.sh

diff --git a/lib/unicorn/socket_helper.rb b/lib/unicorn/socket_helper.rb
index 9a155e1..1d03eab 100644
--- a/lib/unicorn/socket_helper.rb
+++ b/lib/unicorn/socket_helper.rb
@@ -111,8 +111,14 @@ module Unicorn
       sock = if address[0] == ?/
         if File.exist?(address)
           if File.socket?(address)
-            logger.info "unlinking existing socket=#{address}"
-            File.unlink(address)
+            begin
+              UNIXSocket.new(address).close
+              # fall through, try to bind(2) and fail with EADDRINUSE
+              # (or succeed from a small race condition we can't sanely avoid).
+            rescue Errno::ECONNREFUSED
+              logger.info "unlinking existing socket=#{address}"
+              File.unlink(address)
+            end
           else
             raise ArgumentError,
                   "socket=#{address} specified but it is not a socket!"
diff --git a/t/t0011-active-unix-socket.sh b/t/t0011-active-unix-socket.sh
new file mode 100644
index 0000000..6f9ac53
--- /dev/null
+++ b/t/t0011-active-unix-socket.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+. ./test-lib.sh
+t_plan 11 "existing UNIX domain socket check"
+
+read_pid_unix () {
+	x=$(printf 'GET / HTTP/1.0\r\n\r\n' | \
+	    socat - UNIX:$unix_socket | \
+	    tail -1)
+	test -n "$x"
+	y="$(expr "$x" : '\([0-9]\+\)')"
+	test x"$x" = x"$y"
+	test -n "$y"
+	echo "$y"
+}
+
+t_begin "setup and start" && {
+	rtmpfiles unix_socket unix_config
+	rm -f $unix_socket
+	unicorn_setup
+	grep -v ^listen < $unicorn_config > $unix_config
+	echo "listen '$unix_socket'" >> $unix_config
+	unicorn -D -c $unix_config pid.ru
+	unicorn_wait_start
+	orig_master_pid=$unicorn_pid
+}
+
+t_begin "get pid of worker" && {
+	worker_pid=$(read_pid_unix)
+	t_info "worker_pid=$worker_pid"
+}
+
+t_begin "fails to start with existing pid file" && {
+	rm -f $ok
+	unicorn -D -c $unix_config pid.ru || echo ok > $ok
+	test x"$(cat $ok)" = xok
+}
+
+t_begin "worker pid unchanged" && {
+	test x"$(read_pid_unix)" = x$worker_pid
+	> $r_err
+}
+
+t_begin "fails to start with listening UNIX domain socket bound" && {
+	rm $ok $pid
+	unicorn -D -c $unix_config pid.ru || echo ok > $ok
+	test x"$(cat $ok)" = xok
+	> $r_err
+}
+
+t_begin "worker pid unchanged (again)" && {
+	test x"$(read_pid_unix)" = x$worker_pid
+}
+
+t_begin "nuking the existing Unicorn succeeds" && {
+	kill -9 $unicorn_pid $worker_pid
+	while kill -0 $unicorn_pid
+	do
+		sleep 1
+	done
+	check_stderr
+}
+
+t_begin "succeeds in starting with leftover UNIX domain socket bound" && {
+	test -S $unix_socket
+	unicorn -D -c $unix_config pid.ru
+	unicorn_wait_start
+}
+
+t_begin "worker pid changed" && {
+	test x"$(read_pid_unix)" != x$worker_pid
+}
+
+t_begin "killing succeeds" && {
+	kill $unicorn_pid
+}
+
+t_begin "no errors" && check_stderr
+
+t_done
diff --git a/test/unit/test_socket_helper.rb b/test/unit/test_socket_helper.rb
index bbce359..c6d0d42 100644
--- a/test/unit/test_socket_helper.rb
+++ b/test/unit/test_socket_helper.rb
@@ -101,7 +101,14 @@ class TestSocketHelper < Test::Unit::TestCase
 
   def test_bind_listen_unix_rebind
     test_bind_listen_unix
-    new_listener = bind_listen(@unix_listener_path)
+    new_listener = nil
+    assert_raises(Errno::EADDRINUSE) do
+      new_listener = bind_listen(@unix_listener_path)
+    end
+    assert_nothing_raised do
+      File.unlink(@unix_listener_path)
+      new_listener = bind_listen(@unix_listener_path)
+    end
     assert UNIXServer === new_listener
     assert new_listener.fileno != @unix_listener.fileno
     assert_equal sock_name(new_listener), sock_name(@unix_listener)
-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply related	[relevance 4%]

* Re: Unicorn doesn't reload the app after the HUP signal
  2010-10-02  0:57  2% Unicorn doesn't reload the app after the HUP signal Rubén Dávila
@ 2010-10-02  8:13  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2010-10-02  8:13 UTC (permalink / raw)
  To: unicorn list

Rubén Dávila <ruben.grunge84@gmail.com> wrote:
> Hi folks, I've experimented a problem while deploying my app, I've
> sent the HUP signal to the master process, I've checked everything is
> ok: new master and workers are spawned and the old ones are
> killed(I've checked the PIDs), but the new code deployed isn't
> reflected in the living site, so I've to stop and start again unicorn
> in order to see the new changes, here is my unicorn config:
> 
> rails_env = ENV['RAILS_ENV'] || 'production'
> worker_processes (rails_env == 'production' ? 3 : 1)
> preload_app true
> timeout 30
> listen File.expand_path(File.join(__FILE__,
> "../../tmp/sockets/unicorn.sock")), :backlog => 2048

Hi Rubén,

Are you using cap or something else that makes symlinks during
deployment?  It could've been started in an old release directory while
it didn't have the "current" symlink pointing to it.  You can work
around this by putting:

  working_directory "/path/to/app/current"

in your config file.

<snip>

> I'm using unicorn 1.1.2 and rack 1.0.1
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Unicorn doesn't reload the app after the HUP signal
@ 2010-10-02  0:57  2% Rubén Dávila
  2010-10-02  8:13  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Rubén Dávila @ 2010-10-02  0:57 UTC (permalink / raw)
  To: mongrel-unicorn

Hi folks, I've experimented a problem while deploying my app, I've
sent the HUP signal to the master process, I've checked everything is
ok: new master and workers are spawned and the old ones are
killed(I've checked the PIDs), but the new code deployed isn't
reflected in the living site, so I've to stop and start again unicorn
in order to see the new changes, here is my unicorn config:

rails_env = ENV['RAILS_ENV'] || 'production'
worker_processes (rails_env == 'production' ? 3 : 1)
preload_app true
timeout 30
listen File.expand_path(File.join(__FILE__,
"../../tmp/sockets/unicorn.sock")), :backlog => 2048

if GC.respond_to?(:copy_on_write_friendly=)
  GC.copy_on_write_friendly = true
end

before_fork do |server, worker|
  old_pid = RAILS_ROOT + '/tmp/pids/unicorn.pid.oldbin'
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

after_fork do |server, worker|
  ActiveRecord::Base.establish_connection
  begin
    worker.user('peruautos', 'www-data') if Process.euid == 0
  rescue => e
    if RAILS_ENV == 'development'
      STDERR.puts "couldn't change user, oh well"
    else
      raise e
    end
  end
end

I'm using unicorn 1.1.2 and rack 1.0.1

Regards.
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: Unicorn fails to restart gracefully on capistrano deploy
  2010-09-15  6:40  2% Unicorn fails to restart gracefully on capistrano deploy Eirik Dentz Sinclair
@ 2010-09-15 21:52  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2010-09-15 21:52 UTC (permalink / raw)
  To: unicorn list

Eirik Dentz Sinclair <eirik@efficiency20.com> wrote:
> Lawrence, Jamie
> 
> Thanks very much for your responses.
> 
> The before_exec block in Lawrence's gist was just what was needed. Not sure how many others are using Capistrano and bundler, but seems like that before_exec block would be a good addition to this page: http://unicorn.bogomips.org/Sandbox.html
> 
> Thanks again for the help.

Thanks for confirming the fix.  I've updated the Sandbox document
with the following patch.

Patches/pull-requests to the mailing list for documentation are very
much appreciated, especially for Bundler and maybe other things I don't
use myself.  Lets try to treat the in-source documentation much like a
wiki, except anybody who grabs the source also has the latest up-to-date
docs ready for offline reading (and readable without a web browser :)

>From 1a75966a5d1a1f6307ed3386e2f91a28bbb72ca0 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Wed, 15 Sep 2010 14:42:54 -0700
Subject: [PATCH] doc: update Sandbox document for Bundler

Thanks to Lawrence Pit, Jamie Wilkinson, and Eirik Dentz Sinclair.

ref: mid.gmane.org/4C8986DA.7090603@gmail.com
ref: mid.gmane.org/5F1A02DB-CBDA-4302-9E26-8050C2D72433@efficiency20.com
---
 Sandbox |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/Sandbox b/Sandbox
index d2f7590..d101106 100644
--- a/Sandbox
+++ b/Sandbox
@@ -24,6 +24,9 @@ this:
 Then use HUP to reload, and then continue with the USR2+QUIT upgrade
 sequence.
 
+Environment variable pollution when exec-ing a new process (with USR2)
+is the primary issue with sandboxing tools such as Bundler and Isolate.
+
 == Bundler
 
 === Running
@@ -42,6 +45,12 @@ This is no longer be an issue as of bundler 0.9.17
 
 ref: http://mid.gmane.org/8FC34B23-5994-41CC-B5AF-7198EF06909E@tramchase.com
 
+=== Other ENV pollution issues
+
+You may need to set or reset BUNDLE_GEMFILE, GEM_HOME, GEM_PATH and PATH
+environment variables in the before_exec hook as illustrated by
+http://gist.github.com/534668
+
 == Isolate
 
 === Running
-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply related	[relevance 0%]

* Unicorn fails to restart gracefully on capistrano deploy
@ 2010-09-15  6:40  2% Eirik Dentz Sinclair
  2010-09-15 21:52  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Eirik Dentz Sinclair @ 2010-09-15  6:40 UTC (permalink / raw)
  To: mongrel-unicorn

Lawrence, Jamie

Thanks very much for your responses.

The before_exec block in Lawrence's gist was just what was needed. Not sure how many others are using Capistrano and bundler, but seems like that before_exec block would be a good addition to this page: http://unicorn.bogomips.org/Sandbox.html

Thanks again for the help.

-Eirik

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: javascript disappears
  2010-08-18 13:20  2%           ` Jimmy Soho
@ 2010-08-18 23:34  0%             ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2010-08-18 23:34 UTC (permalink / raw)
  To: unicorn list

Jimmy Soho <jimmy.soho@gmail.com> wrote:
> PS. II: While I trying out various things I found that if I modified
> the before_exec hook, then deployed that code, send USR2, then it
> executes the before_exec hook of the old master, not the new master.
> So I then had to send a USR2 a second time to see the new code in
> before_exec taking effect. Is that intentional behaviour?

Yes.  USR2 doesn't reload the config file before by default.  Send a HUP
before USR2 if you want it to reload the config file.  Just keep in mind
if you add any new dependencies on your new deploy and HUP, things could
break, too, especially if you're using "preload_app true".

> PS. III: Haven't tested it yet, but in case a USR2 fails, and unicorn
> reverts back to the old master, does the old master then have the new
> environment variable values? Or does it run the before_exec hook again
> for the old master?

The old master doesn't get affected by environment (or any other local)
changes in before_exec.  before_exec only runs in the new master
process, so it can only affect the new master process (unless it sends
signals to the old master).

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* Re: javascript disappears
  @ 2010-08-18 13:20  2%           ` Jimmy Soho
  2010-08-18 23:34  0%             ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jimmy Soho @ 2010-08-18 13:20 UTC (permalink / raw)
  To: unicorn list

> Aha!  I believe there's an option for Bundler to use the /srv/app/shared
> directory with Capistrano.  That's probably your best option.

I am using the shared bundler gems option, I use the example floating
around the internets:

run("cd #{release_path} && bundle install vendor/bundler_gems
--without development test --disable-shared-gems")

> You can also try force the environment variables with the
> before_exec hook in your config file:
>
>  before_exec do |server|
>    ENV['GEM_HOME'] = '/srv/app/current/vendor/bundler_gems'
>  end

After a few hours hacking I've come to the conclusion that's the only
way it can work, for now.

Having delved into bundler I think Bundler.setup needs to change. I'll
file an issue for that with them.


PS. I: for those interested, this is now the unicorn-conf.rb file I'm
using, so I can now happily prune all old releases:

http://gist.github.com/534668


PS. II: While I trying out various things I found that if I modified
the before_exec hook, then deployed that code, send USR2, then it
executes the before_exec hook of the old master, not the new master.
So I then had to send a USR2 a second time to see the new code in
before_exec taking effect. Is that intentional behaviour?


PS. III: Haven't tested it yet, but in case a USR2 fails, and unicorn
reverts back to the old master, does the old master then have the new
environment variable values? Or does it run the before_exec hook again
for the old master?



Jimmy
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

* Re: javascript disappears
  2010-08-17 23:38  3% javascript disappears Jimmy Soho
@ 2010-08-18  1:14  0% ` Eric Wong
    0 siblings, 1 reply; 200+ results
From: Eric Wong @ 2010-08-18  1:14 UTC (permalink / raw)
  To: unicorn list

Jimmy Soho <jimmy.soho@gmail.com> wrote:
> Hi All,
> 
> When I deploy new code I always go through the USR2 - QUIT process.
> However, after a few deployments it seems our base javascript file is
> missing. I've experienced this a few times now. We use rails 2.3.8,
> and have something like this in our layouts file:
> 
> <%= javascript_include_tag 'webtoolkit', 'jquery', 'jquery.metadata',
> 'jquery.url_utils', 'jquery.form', 'jquery.hotkeys',
> 'jquery.livequery', 'application', :cache => 'base'  %>
> 
> When it's missing it doesn't matter how often I kill with USR2 / QUIT,
> it won't appear. If I stop the unicorn master process completely and
> then start again, then it does create the base.js file again.
> 
> Anyone experienced this as well?  Anyone know what could be the reason
> for this?

Hi Jimmy,   Are you deploying with Capistrano and you originally started
Unicorn in a working_directory that no longer exists on the filesystem?

Under Linux, you can check the working directory of any process
with `ls -l /proc/$PID/cwd`

That's the only thing I can think of at the moment.

In your other email:
> I get the feeling that memory from the previous master/workers somehow
> isn't released and is used by the new unicorn master and/or worker
> processes.

Shouldn't be possible unless your OS is broken.  A more likely
explanation is that Unicorn process is pinned to the working
directory of an old deploy.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* javascript disappears
@ 2010-08-17 23:38  3% Jimmy Soho
  2010-08-18  1:14  0% ` Eric Wong
  0 siblings, 1 reply; 200+ results
From: Jimmy Soho @ 2010-08-17 23:38 UTC (permalink / raw)
  To: mongrel-unicorn

Hi All,

When I deploy new code I always go through the USR2 - QUIT process.
However, after a few deployments it seems our base javascript file is
missing. I've experienced this a few times now. We use rails 2.3.8,
and have something like this in our layouts file:

<%= javascript_include_tag 'webtoolkit', 'jquery', 'jquery.metadata',
'jquery.url_utils', 'jquery.form', 'jquery.hotkeys',
'jquery.livequery', 'application', :cache => 'base'  %>

When it's missing it doesn't matter how often I kill with USR2 / QUIT,
it won't appear. If I stop the unicorn master process completely and
then start again, then it does create the base.js file again.

Anyone experienced this as well?  Anyone know what could be the reason for this?



Jimmy
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 3%]

* Re: SIGWINCH
  2010-07-13  4:25  2% SIGWINCH Lawrence Pit
@ 2010-07-13  4:34  0% ` Eric Wong
  0 siblings, 0 replies; 200+ results
From: Eric Wong @ 2010-07-13  4:34 UTC (permalink / raw)
  To: unicorn list

Lawrence Pit <lawrence.pit@gmail.com> wrote:
> Hi,
>
> I followed the procedure to replace a running unicorn. Works fine,  
> except for one thing: after I decide to back out, ie I send a HUP to the  
> old master followed by a QUIT to the new master, then:
>
> 1. the new master does die, and the old master does work again
>
> 2. the "old" master keeps running with a process name of "master (old)"
>
> 3. the "old" master keeps running with a pid file named unicorn.pid.oldbin
>
> Because the file is now named unicorn.pid.oldbin a later attempt to  
> deploy using scripts is made rather difficult as they assume a pid file  
> named unicorn.pid.
>
> How could I get unicorn to rename the pid file back to unicorn.pid ?  I  
> see some code in method +reap_all_workers+ that suggests it would rename  
> the pid file back to its original, but for some reason it doesn't for  
> me. Am I missing a step?

Hi Lawrence,

Are you using a config file to specify pid or --pid from the
command-line?  The config file should be more reliable.  I'll take a
deeper look at it in a bit.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 0%]

* SIGWINCH
@ 2010-07-13  4:25  2% Lawrence Pit
  2010-07-13  4:34  0% ` SIGWINCH Eric Wong
  0 siblings, 1 reply; 200+ results
From: Lawrence Pit @ 2010-07-13  4:25 UTC (permalink / raw)
  To: unicorn list

Hi,

I followed the procedure to replace a running unicorn. Works fine, 
except for one thing: after I decide to back out, ie I send a HUP to the 
old master followed by a QUIT to the new master, then:

1. the new master does die, and the old master does work again

2. the "old" master keeps running with a process name of "master (old)"

3. the "old" master keeps running with a pid file named unicorn.pid.oldbin

Because the file is now named unicorn.pid.oldbin a later attempt to 
deploy using scripts is made rather difficult as they assume a pid file 
named unicorn.pid.

How could I get unicorn to rename the pid file back to unicorn.pid ?  I 
see some code in method +reap_all_workers+ that suggests it would rename 
the pid file back to its original, but for some reason it doesn't for 
me. Am I missing a step?


PS. Using unicorn 1.1.1.



Cheers,
Lawrence
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[relevance 2%]

Results 1-200 of ~290   | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2010-07-13  4:25  2% SIGWINCH Lawrence Pit
2010-07-13  4:34  0% ` SIGWINCH Eric Wong
2010-08-17 23:38  3% javascript disappears Jimmy Soho
2010-08-18  1:14  0% ` Eric Wong
2010-08-18  2:23       ` Jimmy Soho
2010-08-18  2:45         ` Eric Wong
2010-08-18  3:39           ` Jimmy Soho
2010-08-18  7:14             ` Eric Wong
2010-08-18 13:20  2%           ` Jimmy Soho
2010-08-18 23:34  0%             ` Eric Wong
2010-09-15  6:40  2% Unicorn fails to restart gracefully on capistrano deploy Eirik Dentz Sinclair
2010-09-15 21:52  0% ` Eric Wong
2010-10-02  0:57  2% Unicorn doesn't reload the app after the HUP signal Rubén Dávila
2010-10-02  8:13  0% ` Eric Wong
2010-10-02 16:38     Problem with binding UNIX listeners before checking PID Jordan Ritter
2010-10-04  4:17     ` Eric Wong
2010-10-04  4:22  4%   ` [PATCH] avoid unlinking actively listening sockets Eric Wong
2010-11-20  2:47     [ANN] unicorn 3.0.0 - disable rewindable input! Eric Wong
2010-11-20 17:50     ` Michael Guterl
2010-11-21  4:05       ` Eric Wong
2010-11-21 16:29         ` Jeremy Evans
2010-11-21 23:49           ` Eric Wong
2010-11-22  1:21  2%         ` Jeremy Evans
2010-12-01 11:59     Unicorn and HAProxy, 500 Internal errors after checks Pierre
2010-12-01 16:52     ` Eric Wong
2010-12-01 17:14  2%   ` Pierre
2011-02-16 19:11  2% license is NOT changing Eric Wong
2011-03-08  4:40     [RFC/PATCH] Bundler/Sandbox documentation updates Eric Wong
2011-03-09 10:39  2% ` Lawrence Pit
2011-03-10  0:52  0%   ` Eric Wong
2011-04-07 21:11  1% Unicorn error Ernesto Rocha
2011-04-07 22:04  0% ` Clifton King
2011-05-21  3:05     leading/trailing linear whitespace in headers Eric Wong
2011-05-21  5:14     ` Lawrence Pit
2011-05-21  5:57  2%   ` Eric Wong
2011-05-27  7:32  0%     ` Lawrence Pit
2011-05-30  7:02  2%       ` Lawrence Pit
2011-05-31  4:49  0%         ` Eric Wong
2011-05-31  9:02     workers not utilizing multiple CPUs Nate Clark
2011-05-31 12:10     ` Lawrence Pit
2011-05-31 12:20       ` Nate Clark
2011-05-31 14:07         ` Clifton King
2011-05-31 15:48           ` Eric Wong
2011-05-31 15:55             ` Clifton King
     [not found]               ` <BANLkTikC5D+0pUDRDgvQbzu=dpwmdKNY=A@mail.gmail.com>
2011-06-01  6:51  2%             ` Nate Clark
2011-06-20 23:40     problem setting multiple cookies Jason Su
2011-06-21  0:06     ` Eric Wong
2011-06-21  0:25  2%   ` Jason Su
2011-06-21  0:46         ` Eric Wong
2011-06-21  1:34           ` Jason Su
2011-06-21  1:35  2%         ` Jason Su
2011-06-21  2:15             ` Eric Wong
2011-06-21  2:48  2%           ` Jason Su
2011-06-21  3:15  0%             ` Eric Wong
2011-06-21  3:38  2%               ` Jason Su
2011-06-21 18:29  0%                 ` Eric Wong
2011-06-22  1:01                       ` Jason Su
2011-06-22  1:59                         ` Eric Wong
2011-06-22  4:18  1%                       ` Jason Su
2011-06-22  6:02  0%                         ` Eric Wong
2011-07-11 16:07  2% Unicorn vs Apache Matt Smith
2011-07-11 18:45  0% ` Eric Wong
2011-08-02 20:09  2% Strange quit behavior James Cox
2011-08-02 20:34  0% ` Alex Sharp
2011-08-02 21:54  0%   ` Eric Wong
2011-08-05  4:09         ` Alex Sharp
2011-08-05  4:12           ` Alex Sharp
2011-08-05  8:07             ` Eric Wong
2011-08-17  4:26  1%           ` Alex Sharp
2011-08-17  9:22                 ` Eric Wong
2011-08-17 20:13                   ` Eric Wong
2011-08-18 23:13  2%                 ` Alex Sharp
2011-08-19  1:53                       ` Eric Wong
2011-08-19  9:42                         ` Alex Sharp
2011-08-23  2:59                           ` Alex Sharp
2011-08-23  7:12                             ` Eric Wong
2011-08-23 16:49  2%                           ` Alex Sharp
2011-08-02 21:53  0% ` Eric Wong
2011-08-02 22:46  0%   ` James Cox
2011-08-03 16:41  2% Unicorn in the jail Tatsuya Ono
2011-08-28 13:35     Unicorn for Rails development mode? Jay Levitt
2011-08-28 23:14  2% ` Eric Wong
2011-09-08  9:04  1% Sending ABRT to timeout-errant process before KILL J. Austin Hughey
2011-10-04  7:10     Peformance up - using OobGC & GC.disable secondlife
2011-10-04 22:53     ` Eric Wong
2011-10-06  6:22       ` secondlife
2011-10-10 17:05         ` Tatsuya Ono
2011-10-10 21:53           ` Eric Wong
2011-10-10 23:03             ` Tatsuya Ono
2011-10-12 16:00  2%           ` Tatsuya Ono
2011-11-21 23:14     Should USR2 always work? Laurens Pit
2011-11-22  1:16     ` Eric Wong
2011-11-22  1:55       ` Lawrence Pit
2011-11-22  3:00         ` Eric Wong
2011-11-23  5:31  3%       ` Lawrence Pit
2011-11-30 15:24  2% COW friendly GC and PostgreSQL Laas Toom
2011-11-30 20:54  0% ` Jeremy Evans
2011-12-08 18:19     Master repeatedly killing workers due to timeouts Jonathan del Strother
2011-12-08 20:23     ` Eric Wong
2011-12-08 22:24  2%   ` Jonathan del Strother
2012-01-31 18:39     FreeBSD jail and unicorn Eric Wong
2012-01-31 18:50  2% ` Charles Hornberger
2012-01-31 19:05     Eric Wong
     [not found]     ` <CB4EBD2A.7DF%philipp.bruell@skrill.com>
2012-02-01 18:14  0%   ` Eric Wong
     [not found]         ` <CB5014D7.892%philipp.bruell@skrill.com>
2012-02-02 19:31  0%       ` Eric Wong
2012-04-12 17:36  2% Background jobs with #fork paddor
2012-04-12 20:39  0% ` Eric Wong
2012-04-12 22:41       ` Patrik Wenger
2012-04-12 23:04  3%     ` Eric Wong
2012-04-27 14:36     app error: Socket is not connected (Errno::ENOTCONN) Joel Nimety
2012-04-27 18:59     ` Eric Wong
2012-04-27 19:12       ` Joel Nimety
2012-04-27 19:32         ` Eric Wong
2012-04-27 19:45           ` Matt Smith
2012-04-27 19:47  2%         ` Matt Smith
2012-04-28  2:46  2% unicorn + sleep = long wake up loading time adam k
2012-04-28  4:40  0% ` Eric Wong
2012-06-25 13:02     Address already in use Manuel Palenciano Guerrero
2012-06-25 20:28     ` Eric Wong
2012-06-25 21:03  1%   ` Manuel Palenciano Guerrero
2012-07-17  0:33     Any signal other than -9 causes full CPU utilization by master unicorn process on FreeBSD Mark Mccraw
2012-07-17  2:05     ` Eric Wong
2012-07-17 11:56       ` Mark Mccraw
2012-07-17 21:23  2%     ` Mark Mccraw
2012-07-17 22:17  0%       ` Eric Wong
2012-08-07  7:26  1% SIGUSR2 from symlinked working directory Danial Pearce
     [not found]     <mailman.0.1345509654.31187.mongrel-unicorn@rubyforge.org>
2012-08-21  0:44     ` Unused Unicorn processes Konstantin Gredeskoul
2012-08-21  9:11       ` Eric Wong
2012-08-22 18:16  2%     ` Konstantin Gredeskoul
2012-10-09  0:39     Is a client uploading a file a slow client from unicorn's point of view? Jimmy Soho
2012-10-09  1:58     ` Eric Wong
2012-10-09  6:31       ` Laas Toom
2012-10-09 20:03         ` Eric Wong
2012-10-09 23:06           ` Laas Toom
2012-10-09 23:54  2%         ` Eric Wong
2012-10-10  6:59  2%           ` Laas Toom
2012-10-29 17:44     Combating nginx 499 HTTP responses during flash traffic scenario Tom Burns
2012-10-29 18:45     ` Eric Wong
2012-10-29 21:53       ` Eric Wong
2012-10-30 20:40         ` Tom Burns
2012-10-30 21:37  2%       ` Eric Wong
2012-11-02 17:59             ` Tom Burns
2012-11-02 19:38               ` Eric Wong
2012-11-03 22:45                 ` Tom Burns
2012-11-05 11:48                   ` Eric Wong
2012-11-06  3:16                     ` Tom Burns
2012-11-06 21:23                       ` Eric Wong
2012-11-29 15:52  1%                     ` Tom Burns
2012-11-29 20:41  0%                       ` Eric Wong
2012-11-07 16:42  2% select(): Interrupted system call from curb when stopping unicorn Graham Bleach
2012-11-07 21:11  0% ` Eric Wong
2012-11-13  0:34  2% Testing Unicorn on Rubinius mike
2012-11-28 13:32     When a client terminates a connection Andrew Stewart
2012-11-29 17:09     ` Iñaki Baz Castillo
2012-11-29 23:04       ` Eric Wong
2012-11-30  9:15  2%     ` Andrew Stewart
2012-11-30 19:26  0%       ` Eric Wong
2012-12-03 14:42  0%         ` Andrew Stewart
2012-11-30 17:20  2% using unicorn with logging-rails gem Yoav Aner
2012-11-30 19:15  0% ` Eric Wong
2012-11-30 19:59  0%   ` Yoav Aner
     [not found]       ` <CA+mA6PgTGcyLy2Dua9a6gDWhRwsQLtHEyk17fhBE8DW1TkO5EA@mail.gmail.com>
2012-11-30 20:05         ` Eric Wong
2012-11-30 20:17  3%       ` Yoav Aner
2012-11-30 20:55  0%         ` Eric Wong
     [not found]     <CA+c-+BYTxv+5mQ2MKeKVYEoxgPbi2QjwY6Oak-R_g65GhixEzg@mail.gmail.com>
2012-12-05  5:19     ` Fwd: Issue starting unicorn with non-ActiveRecord Rails app Peter Hall
2012-12-05  7:48       ` Daniel Condomitti
2012-12-05  9:56  2%     ` Peter Hall
2012-12-06 19:48     Unicorn fails to install even though it's already installed and running Mac Martine
2012-12-06 20:23     ` Eric Wong
2012-12-06 21:11       ` Mac Martine
2012-12-06 21:54  2%     ` Eric Wong
2013-01-14 15:42     Increassing timeouts Manuel Palenciano Guerrero
2013-01-15 19:44     ` Eric Wong
2013-07-09 15:15       ` Troex Nevelin
2013-08-03  6:32  2%     ` Eric Wong
2013-01-24  8:42     SIGSEGV at shutdown (was: Re: your mail) Charles Hornberger
2013-01-24  9:52     ` Eric Wong
2013-01-24 10:27  2%   ` Charles Hornberger
2013-01-25 10:52     No middleware without touching RACK_ENV Lin Jen-Shin (godfat)
2013-01-25 18:39     ` Eric Wong
2013-01-28 14:43  1%   ` Lin Jen-Shin (godfat)
2013-01-28 23:21  2%     ` Eric Wong
2013-01-29  2:31  0%       ` Lin Jen-Shin (godfat)
2013-03-09 16:02     Unicorn hangs on POST request Tom Pesman
2013-03-09 21:02     ` Eric Wong
2013-03-10 16:22       ` Tom Pesman
2013-03-11 19:49         ` Eric Wong
2013-03-11 22:20           ` Tom Pesman
2013-03-11 23:01             ` Eric Wong
2013-04-02 11:55               ` Tom Pesman
2013-04-02 17:24                 ` Eric Wong
2013-04-02 20:25                   ` Tom Pesman
2013-04-02 22:36                     ` Eric Wong
2013-04-03  4:56  2%                   ` Lin Jen-Shin (godfat)
2013-04-03 11:38  0%                     ` Eric Wong
2013-04-25  8:02     Why doesn't SIGTERM quit gracefully? Andreas Falk
2013-04-25  8:51     ` Eric Wong
2013-04-25 11:02       ` Andreas Falk
2013-04-25 18:09  2%     ` Eric Wong
2013-04-25 20:45  0%       ` Andreas Falk
2013-08-20 14:47  2% A barrage of unexplained timeouts nick
2013-08-20 16:37     ` Eric Wong
2013-08-20 17:27  2%   ` nick
2013-08-20 17:40         ` Eric Wong
2013-08-20 18:11  2%       ` nick
2013-08-20 18:49  2%         ` Eric Wong
2013-08-20 20:03  2%           ` nick
2013-08-20 20:42  0%             ` Eric Wong
2013-08-20 21:19  0%               ` nick
2013-09-29 20:13  2% More " nick
2013-10-23 22:55  2% pid file handling issue Michael Fischer
2013-10-24  0:53  0% ` Eric Wong
2013-11-01 14:12     [PATCH] construct listener_fds Hash in 1.8 compatible way Ernest W. Durbin III
2013-11-01 16:50     ` Eric Wong
2013-11-01 17:04       ` Ernest W. Durbin III
2013-11-01 18:54         ` Eric Wong
2013-11-01 19:00  2%       ` Ernest W. Durbin III
2013-11-05 14:46     Handling closed clients Andrew Hobson
2013-11-05 17:20     ` Eric Wong
     [not found]       ` <m2iow6k7nk.fsf@macdaddy.atl.damballa>
2013-11-05 20:51         ` Eric Wong
     [not found]           ` <m21u2sjpc9.fsf@macdaddy.atl.damballa>
2013-11-07 16:48  0%         ` Eric Wong
2013-11-07 20:22  2%           ` [PATCH] stream_input: avoid IO#close on client disconnect Eric Wong
2013-11-26 13:30     What does it mean for the unicorn process to be bound to a terminal? Rodrigo Rosenfeld Rosas
2013-11-26 18:04     ` Eric Wong
2013-11-26 18:35       ` Rodrigo Rosenfeld Rosas
2013-11-26 19:21  2%     ` Eric Wong
2013-12-09  9:52  1% [PATCH] rework master-to-worker signaling to use a pipe Eric Wong
2013-12-10  0:22  0% ` Sam Saffron
2014-01-11  7:40  2% [ANN] unicorn 4.8.0 - big internal changes, but compatible Eric Wong
2014-01-28 19:49     Different behavior with pid files and SIGUSR2 Michael Graff
2014-01-28 20:03  2% ` Eric Wong
2014-01-28 20:08  0%   ` Michael Graff
2014-02-24 12:33  2% Stucks DB connections Ilya Bazylchuk
2014-02-24 18:25  0% ` Eric Wong
2014-04-26  3:20     [PATCH] tests: switch to minitest Ken Dreyer
2014-04-26  6:07  2% ` Eric Wong
2014-05-07  8:05     [ANN] unicorn 4.8.3 - the end of an era Eric Wong
2014-05-07  9:30     ` Jérémy Lecour
2014-05-07  9:46       ` Eric Wong
2014-05-07 20:33         ` Michael Fischer
2014-05-07 21:25  3%       ` Eric Wong
2014-07-03 14:40     Log reopening broken on Rails 4 with config.autoflush_log = false? Cedric Maion
2014-07-03 17:24     ` Eric Wong
2014-07-04  9:13  2%   ` Cedric Maion
2014-07-24 16:27     Old unicorn workers still receives requests? Bráulio Bhavamitra
2014-07-24 17:04  2% ` Eric Wong
2014-07-24 17:22  0%   ` Bráulio Bhavamitra
2014-08-02  7:51     Please move to github Gary Grossman
2014-08-02  8:50     ` Eric Wong
2014-08-02 19:07  1%   ` Gary Grossman
2014-08-02 20:15  0%     ` Eric Wong
2014-08-04 18:12  1% Weird Unicorn Timeout Issues (Hibernation problem?) Tony Devlin
2014-08-04 18:39  0% ` Eric Wong
2014-08-04 18:41  0%   ` Kapil Israni
2014-08-04 18:48  2%     ` Eric Wong
2014-08-04 18:45  0%   ` Tony Devlin
2014-08-04 18:55  0%   ` Daniel Condomitti
2014-08-04 19:34         ` Eric Wong
2014-08-04 20:24           ` Tony Devlin
2014-08-04 20:44             ` Eric Wong
2014-08-04 20:46               ` Eric Wong
2014-08-05 14:46  2%             ` Tony Devlin
2014-09-03 10:13     fork() errors lead to a completely dead unicorn Jonathan del Strother
2014-09-03 17:11     ` Eric Wong
2014-09-07 10:12  2%   ` Jonathan del Strother
2014-10-03 11:34     Master hooks needed Bráulio Bhavamitra
2014-10-03 12:22     ` Eric Wong
2014-10-04  0:53       ` Bráulio Bhavamitra
2014-10-04  1:22         ` Eric Wong
2014-10-04  1:35           ` Bráulio Bhavamitra
2014-10-04  1:57  6%         ` Eric Wong
2014-10-04  2:04  0%           ` Bráulio Bhavamitra
2014-10-09 12:24     Reserved workers not as webservers Bráulio Bhavamitra
2014-10-09 16:45     ` Michael Fischer
2014-10-09 17:14       ` Devin Ben-Hur
2014-10-09 17:34         ` Eric Wong
2014-10-09 18:06           ` Bráulio Bhavamitra
2014-10-09 18:15             ` Eric Wong
2014-10-11  3:35  2%           ` Bráulio Bhavamitra
2014-10-13  0:10  0%             ` Bráulio Bhavamitra
2014-10-24 17:33     Having issue with Unicorn Imdad
2014-10-24 17:45     ` Eric Wong
2014-10-24 18:02       ` Imdad
2014-10-24 18:13         ` Eric Wong
2014-10-24 18:28           ` Imdad
2014-10-24 18:34             ` Eric Wong
2014-10-24 18:39               ` Imdad
2014-10-24 19:13                 ` Eric Wong
2014-10-24 19:29                   ` Imdad
2014-10-24 19:41  3%                 ` Eric Wong
2014-10-24 19:58  0%                   ` Imdad
2014-10-24 20:06                         ` Eric Wong
2014-10-24 20:09                           ` Imdad
2014-10-24 20:17                             ` Eric Wong
2014-10-24 20:35                               ` Imdad
2014-10-24 20:40                                 ` Eric Wong
2014-10-24 20:45                                   ` Imdad
2014-10-24 20:58                                     ` Eric Wong
2014-10-24 21:24  2%                                   ` Imdad
2014-11-12 19:46     Issue with Unicorn: Big latency when getting a request Roberto Cordoba del Moral
2014-11-13  7:12     ` Roberto Cordoba del Moral
2014-11-13 21:03       ` Eric Wong
2014-11-14  6:46         ` Roberto Cordoba del Moral
2014-11-14  7:19  2%       ` Eric Wong
2014-11-14  9:09  2%         ` Roberto Cordoba del Moral
2014-11-14  9:32  1%           ` Roberto Cordoba del Moral
2014-12-30 16:38     Unicorn workers timing out intermittently Aaron Price
2014-12-30 18:12  0% ` Eric Wong
2014-12-30 20:02  2%   ` Aaron Price
2015-02-10 17:06  4% [PATCH] ISSUES: add section for bugs in other projects Eric Wong
2015-02-18  9:36  2% [PATCH] explain 11 byte magic number for self-pipe Eric Wong
2015-03-12  1:04     On USR2, new master runs with same PID Kevin Yank
2015-03-12  1:45     ` Eric Wong
2015-03-12  6:26  1%   ` Kevin Yank
2015-03-12  6:45  0%     ` Eric Wong
2015-03-20  1:58  2%       ` Kevin Yank
2015-06-06  1:58     [PATCH 0/2] eliminate generic ivars from HttpRequest class Eric Wong
2015-06-06  1:58  2% ` [PATCH 1/2] move the socket into Rack env for hijacking Eric Wong
2015-07-01 16:08  1% Unicorn returns blank page after no use Farjad Adamjee
2015-09-29  6:38     Request to follow SemVer/mention it in homepage Pirate Praveen
2015-09-29  7:36     ` Eric Wong
2015-09-29  8:00       ` Pirate Praveen
2015-09-29 19:36  3%     ` Eric Wong
2015-09-30 16:04  0%       ` Pirate Praveen
     [not found]     <20151117-unicorn-5.0.1-rele@sed>
2015-11-17 22:48  2% ` [ANN] unicorn 5.0.1 - Rack HTTP server for fast clients and *nix Eric Wong
2016-01-27 23:16  2% [ANN] unicorn 5.1.0.pre1 " Eric Wong
2016-03-07 23:08     Systemd socket inheritance fails with “not a socket file descriptor” Amir Yalon
2016-03-08  3:31     ` Eric Wong
2016-03-08  7:45       ` Amir Yalon
2016-03-08 17:39         ` Eric Wong
2016-03-08 20:32           ` Amir Yalon
2016-03-09  3:51  2%         ` Eric Wong
2016-03-09 13:19  0%           ` Systemd socket inheritance fails with “not a socket file descriptor” [take2] Christos Trochalakis
2016-03-09 14:06               ` Systemd socket inheritance fails with “not a socket file descriptor” Christos Trochalakis
2016-03-12 23:19  2%             ` Amir Yalon
2016-03-17  0:27  0%               ` [PATCH] doc: reference --keep-file-descriptors for "bundle exec" Eric Wong
2016-04-01  0:43  2% [ANN] unicorn 5.1.0 - Rack HTTP server for fast clients and *nix Eric Wong
     [not found]     <CAL-rKu6CZ731c=uHyZ8+Fg2fhC30e-3-J26XODacUK=YrfX+5Q@mail.gmail.com>
2016-06-07 13:41  0% ` [PATCH] `unicorn upgrade` script resilience against exceptions Eric Wong
2016-06-07 15:17  0%   ` Jesper Rønn-Jensen
2017-02-22 12:02     check_client_connection using getsockopt(2) Simon Eskildsen
2017-02-22 18:33     ` Eric Wong
2017-02-22 20:09       ` Simon Eskildsen
2017-02-23  1:42  2%     ` Eric Wong
2017-02-23  2:42  0%       ` Simon Eskildsen
2017-02-28 23:00  7% [PATCH] t0011-active-unix-socket.sh: fix race condition in test Eric Wong
2017-03-08 18:44     [PATCH] Add worker_exec configuration option Jeremy Evans
2017-03-08 20:02  2% ` Eric Wong
2017-03-09 19:41       ` [PATCH] Add worker_exec configuration option V2 Jeremy Evans
2017-03-10 21:19         ` Eric Wong
2017-03-11  5:26           ` Jeremy Evans
2017-03-11  7:18  2%         ` Eric Wong
2017-03-20 20:32  4% [PATCH] doc: remove private email support address Eric Wong
2017-04-04 14:08     after_worker_exit on murder Simon Eskildsen
2017-04-05  1:19     ` Eric Wong
2017-04-05 10:55  2%   ` Simon Eskildsen
2017-04-05 18:33  2%     ` Eric Wong
2017-05-23 19:29  2% Master Process Reaping Worker with No message Aakash Gupta
2017-09-14  8:25  2% Bug, probably related to Unicoen Felix Yasnopolski
2017-09-14  9:15  2% ` Eric Wong
2017-12-04 23:42     Auto scaling workers with unicorn Sam Saffron
2017-12-05  1:51  2% ` Eric Wong
2018-02-24  8:48     [PATCH] Send SIGTERM before SIGKILL on timeout Fumiaki MATSUSHIMA
2018-02-24 15:15  2% ` Eric Wong
2018-02-27  4:03  0%   ` Fumiaki Matsushima
2018-09-13 19:20     Support default_middleware configurator method Jeremy Evans
2018-09-13 22:34     ` Eric Wong
2018-09-14  0:00       ` Jeremy Evans
2018-09-14 10:56         ` Eric Wong
2018-09-14 15:03           ` Jeremy Evans
2018-09-19  7:39  2%         ` Eric Wong
2019-05-26  5:24  1% yet-another-horribly-named-server as an nginx alternative Eric Wong
2019-12-11 16:24     tmpio.rb and taint mode Terry Scheingeld
2019-12-11 23:16  2% ` Eric Wong
2019-12-16 22:34     Traffic priority with Unicorn Bertrand Paquet
2019-12-17  5:12  3% ` Eric Wong
2019-12-18 22:06  0%   ` Bertrand Paquet
2020-01-14  7:46  1% [PATCH] doc: s/bogomips.org/yhbt.net/g Eric Wong
2020-01-14  7:46  1% ` Eric Wong
2020-04-15  5:06     Sustained queuing on one listener can block requests from other listeners Stan Hu
2020-04-15  5:26  4% ` Eric Wong
2020-04-16  5:46  5%   ` Stan Hu
2020-04-16  6:59  5%     ` Eric Wong
2020-04-16  7:24           ` Stan Hu
2020-04-16  9:24  4%         ` [PATCH] prevent single listener from monopolizing a worker Eric Wong
2020-07-16 10:05     [PATCH] Add early hints support Jean Boussier
2020-07-16 10:50     ` Eric Wong
2020-07-16 11:41       ` Jean Boussier
2020-07-16 12:16  2%     ` Eric Wong
2020-11-26 11:59     [RFC] http_response: ignore invalid header response characters Eric Wong
2021-01-06 17:53  0% ` Eric Wong
2021-01-13 23:20  1%   ` Sam Sanoop
2021-02-26 11:15  3% ` Eric Wong
     [not found]     <F6712BF3-A4DD-41EE-8252-B9799B35E618@github.com>
     [not found]     ` <20210311030250.GA1266@dcvr>
     [not found]       ` <7F6FD017-7802-4871-88A3-1E03D26D967C@github.com>
2021-03-12  9:41  1%     ` Potential Unicorn vulnerability Eric Wong
2021-03-29 19:06     Bus Error with Unicorn 6.0 on OpenBSD/adJ 6.8 with Ruby 3.0 vtamara
2021-03-30  1:58     ` Eric Wong
2021-03-30  4:00       ` Jeremy Evans
2021-04-04 11:06         ` vtamara
2021-04-04 16:43  2%       ` Jeremy Evans
2021-04-04 20:03  0%         ` Eric Wong
2021-04-07 21:19  0%           ` Bus Error with Unicorn 6.0 on OpenBSD-current (6.9) with Ruby 3.0 in minimal rails application vtamara
2021-10-01  3:09     [PATCH 0/6] reduce thundering herds on Linux 4.5+ Eric Wong
2021-10-01  3:09  5% ` [PATCH 5/6] worker_loop: get rid of select() avoidance hack Eric Wong
2022-07-05 20:05     [PATCH] Master promotion with SIGURG (CoW optimization) Jean Boussier
2022-07-06  2:33  1% ` Eric Wong
2022-07-06  7:40  0%   ` Jean Boussier
2022-07-07 10:23  0%     ` Eric Wong
     [not found]           ` <CANPRWbHTNiEcYq5qhN6Kio8Wg9a+2gXmc2bAcB2oVw4LZv8rcw@mail.gmail.com>
2022-07-08  0:30             ` Eric Wong
2022-07-08  6:22  3%           ` Jean Boussier
2022-09-21 22:16  0%             ` Eric Wong
2023-06-05 10:32  1% [PATCH 00-23/23] start porting tests to Perl5 Eric Wong
2023-09-10 20:14  2% [PATCH] doc: various updates ahead of the release Eric Wong
2023-09-16 20:46  0% ` ideal.water4095
2023-09-30 23:54  2%   ` [PATCH] README: fix wording Eric Wong
2024-03-23 19:45  1% [PATCH 0/4] a small pile of patches Eric Wong

Code repositories for project(s) associated with this public inbox

	https://yhbt.net/unicorn.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).