From 484c8f1735221645ab7f4d0a46295ea82d523dd1 Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Sat, 25 Jan 2020 15:48:59 +0100 Subject: [PATCH 1/4] Instrument requests count --- lib/puma/cluster.rb | 5 +++-- lib/puma/server.rb | 5 +++++ lib/puma/single.rb | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/puma/cluster.rb b/lib/puma/cluster.rb index 97ee976ba9..989828fb33 100644 --- a/lib/puma/cluster.rb +++ b/lib/puma/cluster.rb @@ -94,7 +94,7 @@ def term? def ping!(status) @last_checkin = Time.now - captures = status.match(/{ "backlog":(?\d*), "running":(?\d*), "pool_capacity":(?\d*), "max_threads": (?\d*) }/) + captures = status.match(/{ "backlog":(?\d*), "running":(?\d*), "pool_capacity":(?\d*), "max_threads": (?\d*), "requests_count": (?\d*) }/) @last_status = captures.names.inject({}) do |hash, key| hash[key.to_sym] = captures[key].to_i hash @@ -296,7 +296,8 @@ def worker(index, master) r = server.running || 0 t = server.pool_capacity || 0 m = server.max_threads || 0 - payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m} }\n! + rc = server.requests_count || 0 + payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n! io << payload rescue IOError Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue diff --git a/lib/puma/server.rb b/lib/puma/server.rb index 5b5bc33529..d532e258cc 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -36,6 +36,7 @@ class Server attr_reader :thread attr_reader :events + attr_reader :requests_count attr_accessor :app attr_accessor :min_threads @@ -85,6 +86,8 @@ def initialize(app, events=Events.stdio, options={}) @mode = :http @precheck_closing = true + + @requests_count = 0 end attr_accessor :binder, :leak_stack_on_error, :early_hints @@ -626,6 +629,8 @@ def default_server_port(env) # # Finally, it'll return +true+ on keep-alive connections. def handle_request(req, lines) + @requests_count +=1 + env = req.env client = req.io diff --git a/lib/puma/single.rb b/lib/puma/single.rb index e7a9c48248..6689703150 100644 --- a/lib/puma/single.rb +++ b/lib/puma/single.rb @@ -20,6 +20,7 @@ def stats running: @server.running || 0, pool_capacity: @server.pool_capacity || 0, max_threads: @server.max_threads || 0, + requests_count: @server.requests_count || 0, } end From 261246adfdee9cb1dbac2fed42d47d4c726897d7 Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Mon, 10 Feb 2020 21:46:01 +0100 Subject: [PATCH 2/4] Fix existing tests --- test/test_cli.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_cli.rb b/test/test_cli.rb index a4e8b4c0c3..d3aa8793bc 100644 --- a/test/test_cli.rb +++ b/test/test_cli.rb @@ -57,7 +57,7 @@ def test_control_for_tcp body = s.read s.close - assert_match(/{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":0,"running":0,"pool_capacity":16,"max_threads":16}/, body.split(/\r?\n/).last) + assert_match(/{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":0,"running":0,"pool_capacity":16,"max_threads":16,"requests_count":0}/, body.split(/\r?\n/).last) ensure cli.launcher.stop @@ -91,9 +91,9 @@ def test_control_for_ssl body = http.request(req).body end - expected_stats = /{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":0,"running":0,"pool_capacity":16,"max_threads":16}/ + expected_stats = /{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":0,"running":0,"pool_capacity":16,"max_threads":16,"requests_count":0}/ assert_match(expected_stats, body.split(/\r?\n/).last) - assert_equal([:started_at, :backlog, :running, :pool_capacity, :max_threads], Puma.stats.keys) + assert_equal([:started_at, :backlog, :running, :pool_capacity, :max_threads, :requests_count], Puma.stats.keys) ensure cli.launcher.stop @@ -136,7 +136,7 @@ def test_control_clustered body = s.read s.close - assert_match(/\{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","workers":2,"phase":0,"booted_workers":2,"old_workers":0,"worker_status":\[\{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","pid":\d+,"index":0,"phase":0,"booted":true,"last_checkin":"[^"]+","last_status":\{"backlog":0,"running":2,"pool_capacity":2,"max_threads":2\}\},\{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","pid":\d+,"index":1,"phase":0,"booted":true,"last_checkin":"[^"]+","last_status":\{"backlog":0,"running":2,"pool_capacity":2,"max_threads":2\}\}\]\}/, body.split("\r\n").last) + assert_match(/\{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","workers":2,"phase":0,"booted_workers":2,"old_workers":0,"worker_status":\[\{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","pid":\d+,"index":0,"phase":0,"booted":true,"last_checkin":"[^"]+","last_status":\{"backlog":0,"running":2,"pool_capacity":2,"max_threads":2,"requests_count":0\}\},\{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","pid":\d+,"index":1,"phase":0,"booted":true,"last_checkin":"[^"]+","last_status":\{"backlog":0,"running":2,"pool_capacity":2,"max_threads":2,"requests_count":0\}\}\]\}/, body.split("\r\n").last) ensure if UNIX_SKT_EXIST && HAS_FORK cli.launcher.stop @@ -171,7 +171,7 @@ def test_control body = s.read s.close - assert_match(/{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":0,"running":0,"pool_capacity":16,"max_threads":16}/, body.split("\r\n").last) + assert_match(/{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":0,"running":0,"pool_capacity":16,"max_threads":16,"requests_count":0}/, body.split("\r\n").last) ensure if UNIX_SKT_EXIST cli.launcher.stop From b6cd92cef1b8df41b033b9803928214447364b51 Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Mon, 10 Feb 2020 21:48:02 +0100 Subject: [PATCH 3/4] Add history --- History.md | 1 + 1 file changed, 1 insertion(+) diff --git a/History.md b/History.md index 3d06bb008e..91d737993f 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ * Configuration: `environment` is read from `RAILS_ENV`, if `RACK_ENV` can't be found (#2022) * `Puma.stats` now returns a Hash instead of a JSON string (#2086) * `GC.compact` is called before fork if available (#2093) + * Add `requests_count` to workers stats. (#2106) * Bugfixes * Your bugfix goes here (#Github Number) From 063b7c8738826d5e1a157f0a741381dc02fcefcb Mon Sep 17 00:00:00 2001 From: Yoann Lecuyer Date: Mon, 10 Feb 2020 22:13:19 +0100 Subject: [PATCH 4/4] add new test --- test/test_cli.rb | 42 +++++++++++++++++++++++++++++++++++++++++ test/test_web_server.rb | 8 ++++++++ 2 files changed, 50 insertions(+) diff --git a/test/test_cli.rb b/test/test_cli.rb index d3aa8793bc..8f2298ca00 100644 --- a/test/test_cli.rb +++ b/test/test_cli.rb @@ -202,6 +202,48 @@ def test_control_stop t.join if UNIX_SKT_EXIST end + def test_control_requests_count + tcp = UniquePort.call + cntl = UniquePort.call + url = "tcp://127.0.0.1:#{cntl}/" + + cli = Puma::CLI.new ["-b", "tcp://127.0.0.1:#{tcp}", + "--control-url", url, + "--control-token", "", + "test/rackup/lobster.ru"], @events + + t = Thread.new do + cli.run + end + + wait_booted + + s = TCPSocket.new "127.0.0.1", cntl + s << "GET /stats HTTP/1.0\r\n\r\n" + body = s.read + s.close + + assert_match(/{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":\d+,"running":\d+,"pool_capacity":\d+,"max_threads":\d+,"requests_count":0}/, body.split(/\r?\n/).last) + + # send real requests to server + 3.times do + s = TCPSocket.new "127.0.0.1", tcp + s << "GET / HTTP/1.0\r\n\r\n" + body = s.read + s.close + end + + s = TCPSocket.new "127.0.0.1", cntl + s << "GET /stats HTTP/1.0\r\n\r\n" + body = s.read + s.close + + assert_match(/{"started_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z","backlog":\d+,"running":\d+,"pool_capacity":\d+,"max_threads":\d+,"requests_count":3}/, body.split(/\r?\n/).last) + ensure + cli.launcher.stop + t.join + end + def test_control_thread_backtraces skip UNIX_SKT_MSG unless UNIX_SKT_EXIST url = "unix://#{@tmp_path}" diff --git a/test/test_web_server.rb b/test/test_web_server.rb index 5e9daa7fa0..b93442fa91 100644 --- a/test/test_web_server.rb +++ b/test/test_web_server.rb @@ -38,6 +38,14 @@ def test_simple_server assert @tester.ran_test, "Handler didn't really run" end + def test_requests_count + assert_equal @server.requests_count, 0 + 3.times do + hit(["http://127.0.0.1:#{@server.connected_port}/test"]) + end + assert_equal @server.requests_count, 3 + end + def test_trickle_attack socket = do_test(VALID_REQUEST, 3) assert_match "hello", socket.read