Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrument requests count #2106

Merged
merged 5 commits into from Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions History.md
Expand Up @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions lib/puma/cluster.rb
Expand Up @@ -94,7 +94,7 @@ def term?

def ping!(status)
@last_checkin = Time.now
captures = status.match(/{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads": (?<max_threads>\d*) }/)
captures = status.match(/{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads": (?<max_threads>\d*), "requests_count": (?<requests_count>\d*) }/)
@last_status = captures.names.inject({}) do |hash, key|
hash[key.to_sym] = captures[key].to_i
hash
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions lib/puma/server.rb
Expand Up @@ -36,6 +36,7 @@ class Server

attr_reader :thread
attr_reader :events
attr_reader :requests_count
attr_accessor :app

attr_accessor :min_threads
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions lib/puma/single.rb
Expand Up @@ -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

Expand Down
52 changes: 47 additions & 5 deletions test/test_cli.rb
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}"
Expand Down
8 changes: 8 additions & 0 deletions test/test_web_server.rb
Expand Up @@ -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
Expand Down