Shotgun visits every mapping in config.ru regardless the request
ENV
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]
shotgun-0.9.1
rack-1.6.1
thin-1.6.3
Simple config.ru
map '/' do
p "root"
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["root"]]}
end
map "/assets" do
p "assets"
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["assets"]]}
end
map "/admin" do
p "admin"
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["admin"]]}
end
Run shotgun server
$ shotgun
Hit the server:
$ curl http://localhost:9393/
root
Server log output
== Shotgun/Thin on http://127.0.0.1:9393/
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on 127.0.0.1:9393, CTRL+C to stop
"root"
"assets"
"admin"
127.0.0.1 - - [15/Jun/2015:18:52:52 +0300] "GET / HTTP/1.1" 200 - 0.0008
This is really nasty when I use shotgun with sinatra and sprockets: every asset request hits whole application and slow down page loading a lot
Is it by design?
+1 also seeing this
Do you see the same behavior when running the server without Shotgun?
No, I don't
@djanowski Nope
Locally using rackup page responses max out at:
~ 300 ms
Using shotgun they all take:
2.5 secs or more
We currently have 16 routes mapped to controllers using the map method.
@djanowski - any ideas on this? I don't mind taking a look.
Ping
Same here. I was trying sprockets with sinatra and couldn't figure out until now what was the problem. It was compiling assets for each request. Then I used simple rackup and worked normally.
Shotgun with sprockets is currently impossible. Any update?
I don't think this is an issue with Shotgun itself. This must just be how Rack maps work - loading all the Rack apps when config.ru executes (which Shotgun does after forking for each request).
➜ rackup -s webrick
"root"
"assets"
"admin"
[2020-10-03 13:49:41] INFO WEBrick 1.6.1
[2020-10-03 13:49:41] INFO ruby 2.7.1 (2020-03-31) [x86_64-linux]
[2020-10-03 13:49:41] INFO WEBrick::HTTPServer#start: pid=188442 port=9292
I suppose Shotgun could be made to (optionally?) load the config.ru before forking, but that could have unintended consequences to do with the forking - at GreenSync, unrelated to Shotgun, we've found this with Sequel Postgres database connections not carrying over to a fork. Or maybe it could even clone the Rack app before each request. But actually, either approach would defeat the code-reloading purpose of Shotgun!
Perhaps Shotgun could interpret map itself somehow, to not execute branches that don't match the request?
I've just had a fiddle with making Rack::Builder#map lazy, and it works! There would probably be some side-effects from laziness, so I wouldn't make it default. And I reckon what I have wouldn't be working for nested map yet.
# config.ru:
puts 'config.ru evaluation started due to request'
module LazyRackMapForShotgun
def self.call(path:, app:, run:)
puts "LazyRackMapForShotgun: #{path}"
shotgun_request_path?(path) ? app : null_app(run)
end
private
def self.shotgun_request_path?(path)
# TODO: Make this work for nested map
path == shotgun_env['PATH_INFO']
end
def self.shotgun_env
ObjectSpace
.each_object(Shotgun::Loader)
.map { |loader| loader.instance_variable_get(:@env) }
.compact[0]
end
def self.null_app(run)
@@missing_lazy_map_app ||= run.call(
# This shouldn't get called, but just in case:
proc do |env|
[
500,
{ "Content-Type" => "text/html" },
["Could not find app for Shotgun lazy Rack::Builder#map monkeypatch"],
]
end
)
end
end
class ::Rack::Builder
alias map_orig map
def map(path, &block)
map_orig(path, &LazyRackMapForShotgun.call(path: path, app: block, run: method(:run)))
end
end
map '/' do
puts "root"
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["root"]]}
end
map "/assets" do
puts "assets"
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["assets"]]}
end
map "/admin" do
puts "admin"
run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["admin"]]}
end
puts 'config.ru evaluated'
Running with the monkey-patch disabled then enabled:
➜ shotgun -p 3000 --server webrick
== Shotgun/WEBrick on http://127.0.0.1:3000/
[2020-10-03 15:27:37] INFO WEBrick 1.6.0
[2020-10-03 15:27:37] INFO ruby 2.7.1 (2020-03-31) [x86_64-linux]
[2020-10-03 15:27:37] INFO WEBrick::HTTPServer#start: pid=213572 port=3000
config.ru evaluation started due to request
config.ru evaluated
root
assets
admin
127.0.0.1 - - [03/Oct/2020:15:27:38 +1000] "GET /assets HTTP/1.1" 200 - 0.0005
^C
➜ shotgun -p 3000 --server webrick
== Shotgun/WEBrick on http://127.0.0.1:3000/
[2020-10-03 15:28:20] INFO WEBrick 1.6.0
[2020-10-03 15:28:20] INFO ruby 2.7.1 (2020-03-31) [x86_64-linux]
[2020-10-03 15:28:20] INFO WEBrick::HTTPServer#start: pid=213874 port=3000
config.ru evaluation started due to request
LazyRackMapForShotgun: /
LazyRackMapForShotgun: /assets
LazyRackMapForShotgun: /admin
config.ru evaluated
assets
127.0.0.1 - - [03/Oct/2020:15:28:22 +1000] "GET /assets HTTP/1.1" 200 - 0.0004