about summary refs log tree commit homepage
path: root/test
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2013-10-24 02:37:00 +0000
committerEric Wong <e@80x24.org>2013-10-24 02:43:36 +0000
commitc9ad7f3a7b83e990fd2fc731ec796eec4ed4130b (patch)
tree7373b003cf5c113fc45a73c4e85b3cf9c15474ec /test
parent6b545cb2bf4fb3e4ec0ad5050e19647815520dfd (diff)
downloadyahns-c9ad7f3a7b83e990fd2fc731ec796eec4ed4130b.tar.gz
Walking ObjectSpace should be acceptable for this test (due to MT FD
allocation).  Using check_client_connection saves us a bunch of time
when recovering from an overload scenario.
Diffstat (limited to 'test')
-rw-r--r--test/test_client_expire.rb73
1 files changed, 62 insertions, 11 deletions
diff --git a/test/test_client_expire.rb b/test/test_client_expire.rb
index 8ee914b..9e2ac54 100644
--- a/test/test_client_expire.rb
+++ b/test/test_client_expire.rb
@@ -60,7 +60,8 @@ class TestClientExpire < Testcase
     s = get_tcp_client(host, port)
     req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
     s.write(req)
-    str = Timeout.timeout(20) { s.readpartial(666) }
+    str = ""
+    Timeout.timeout(20) { str << s.readpartial(666) until str =~ /\r\n\r\n/ }
     assert_match(%r{keep-alive}, str)
     sleep 2
     abe = tmpfile(%w(abe .err))
@@ -79,46 +80,96 @@ class TestClientExpire < Testcase
     quit_wait(pid)
   end
 
+  # test EMFILE handling
   def test_client_expire_desperate
-    skip "disabled since FD counts vary heavily in an MT process"
     err = @err
     cfg = Yahns::Config.new
     host, port = @srv.addr[3], @srv.addr[1]
     cfg.instance_eval do
       GTL.synchronize do
         h = { "Content-Length" => "0" }
-        app(:rack, lambda { |e| [ 200, h, [] ]}) do
+        queue { worker_threads 1 }
+        ru = lambda { |e|
+          sleep(0.01) unless e["PATH_INFO"] == "/_"
+          [ 200, h, [] ]
+        }
+        app(:rack, ru) do
           listen "#{host}:#{port}", sndbuf: 2048, rcvbuf: 2048
           client_timeout 1
+          check_client_connection true
         end
         client_expire_threshold 1.0
       end
-      logger(Logger.new(err.path))
+      stderr_path err.path
     end
-    srv = Yahns::Server.new(cfg)
     pid = fork do
-      Process.setrlimit :NOFILE, 512, 1024
       ENV["YAHNS_FD"] = @srv.fileno.to_s
-      srv.start.join
+      keep = { $stderr => true, $stdout => true, $stdin => true, @srv => true }
+      ObjectSpace.each_object(IO) do |obj|
+        next if keep[obj]
+        begin
+          obj.close unless obj.closed?
+        rescue IOError # could be uninitialized
+        end
+      end
+      Yahns::Server.new(cfg).start.join
+      exit!($!.nil?)
     end
     f = get_tcp_client(host, port)
+    f.write "G"
     s = get_tcp_client(host, port)
     req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
     s.write(req)
-    str = Timeout.timeout(20) { s.readpartial(666) }
+    str = ""
+    Timeout.timeout(20) { str << s.readpartial(666) until str =~ /\r\n\r\n/ }
     assert_match(%r{keep-alive}, str)
-    sleep 3
-    system "ab -c 1000 -n 10000 -k http://#{host}:#{port}/ 2>&1"
+    sleep 1
 
+    # ignore errors, just beat the crap out of the process
+    nr = Process.getrlimit(:NOFILE)[0] # 1024 is common
+    assert_operator nr, :>, 666, "increase RLIM_NOFILE (ulimit -n)"
+    nr -= 50
+    opts = { out: "/dev/null", err: "/dev/null", close_others: true }
+    begin
+      pids = 2.times.map do
+        fork do
+          exec(*%W(ab -c #{nr} -n 9999999 -v1 -k http://#{host}:#{port}/), opts)
+        end
+      end
+
+      re1 = %r{consider raising open file limits}
+      re2 = %r{dropping (\d+) of \d+ clients for timeout=\d+}
+      Timeout.timeout(30) do
+        n = 0
+        begin
+          buf = File.read(err.path)
+          if buf =~ re1 && buf =~ re2
+            n += $1.to_i
+            break if n >= 2
+          end
+        end while sleep(0.01)
+      end
+    ensure
+      # don't care for ab errors, they're likely
+      pids.each do |_pid|
+        Process.kill(:KILL, _pid)
+        Process.waitpid2(_pid)
+      end
+    end
     [ f, s ].each do |io|
       assert_raises(Errno::EPIPE,Errno::ECONNRESET) do
         req.each_byte { |b| io.write(b.chr) }
       end
       io.close
     end
+
+    # make sure the server still works
+    res = Net::HTTP.start(host, port) { |h| h.get("/_") }
+    assert_equal 200, res.code.to_i
+
     errs = File.readlines(err.path).grep(/ERROR/)
     File.truncate(err.path, 0) # avoid error on teardown
-    re = %r{consider raising open file limits} # - accept, consider raising open file limits}
+    re = %r{consider raising open file limits}
     assert_equal errs.grep(re), errs
   ensure
     quit_wait(pid)