-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Replace rack-mount with new router #1276
Conversation
e4d1a14
to
33fdcbd
Compare
This is excellent work. I am excited for getting this onto Grape master soon. It's really important to build some benchmarks and see how much improvement this is if at all, I think we owe this to the Grape community for switching something as essential as the router. |
33fdcbd
to
d3aaade
Compare
All required tests has been successful now. |
👍 excited to see benchmarks |
1f0876a
to
3a25cca
Compare
I've been tring to improve performance of the new router, and then I'd like to report a few points and hear your opinions. BenchmarkFirst of all, I want you to know that the performance of rack-mount is excellent. Please look at the following benchmark code and its results. Sample codeEnvironment: Mac OS X, Ruby2.3 require 'rack'
require 'grape'
require 'benchmark'
require 'objspace'
require 'memprof2'
def memory_utilization
Memprof2.start
yield
profiler = Memprof2.new
profiler.configure
mem = profiler.collect_info.values.inject(&:+)
Memprof2.stop
puts "Memory Utilization: #{mem/(1024.0 * 1024.0)}MB"
end
req1 = Rack::MockRequest.env_for('/100')
req2 = Rack::MockRequest.env_for('/users/1234', method: 'PUT')
req3 = Rack::MockRequest.env_for('/sources/hoge/aiueohogefuga', method: 'POST')
req4 = Rack::MockRequest.env_for('/sources/hoge/aiueohogefuga', method: 'PUT')
app = Class.new(Grape::API)
memory_utilization do
500.times do |n|
app.get("/#{n}") { n.to_s }
end
app.get('/') { 'hello' }
app.put('/users/:id') { 'hello' }
app.post('/sources', anchor: false) { 'hello' }
app.call(req1)
app.call(req2)
app.call(req3)
app.call(req4)
end
Benchmark.bm do |x|
x.report do
app.call(req1)
end
x.report do
app.call(req2)
end
x.report do
app.call(req3)
end
x.report do
app.call(req4)
end
end rack-mount
new router
|
Do I read this correctly that the new router consumes half the memory and performs 30% faster (except for that last row which seems like an outlier for both?). I think you want to make a lot of those "http" calls, at the very least those routers could have a cold start. |
The new router consumes half the memory, but I think its performance is ambiguous. Okay, I tried to calculate benchmark for calling a lot of requests. require 'rack'
require 'grape'
require 'benchmark'
require 'benchmark/ips'
require 'objspace'
require 'memprof2'
def memory_utilization
Memprof2.start
yield
profiler = Memprof2.new
profiler.configure
mem = profiler.collect_info.values.inject(&:+)
Memprof2.stop
puts "Memory Utilization: #{mem/(1024.0 * 1024.0)}MB"
end
req1 = Rack::MockRequest.env_for('/100')
req2 = Rack::MockRequest.env_for('/users/1234', method: 'PUT')
req3 = Rack::MockRequest.env_for('/sources/hoge/aiueohogefuga', method: 'POST')
req4 = Rack::MockRequest.env_for('/sources/hoge/aiueohogefuga', method: 'PUT')
app = Class.new(Grape::API)
memory_utilization do
500.times do |n|
app.get("/#{n}") { n.to_s }
end
app.get('/') { 'hello' }
app.put('/users/:id') { 'hello' }
app.post('/sources', anchor: false) { 'hello' }
app.call(req1)
app.call(req2)
app.call(req3)
app.call(req4)
end
Benchmark.ips do |x|
[req1, req2, req3, req4].each do |req|
x.report { app.call(req) }
end
end Also, the results are: rack-mount
new router
|
3a25cca
to
0597dd8
Compare
@@ -244,7 +244,7 @@ module Grape | |||
allow(path).to receive(:uses_specific_format?) { true } | |||
allow(path).to receive(:settings) { { format: :json } } | |||
|
|||
expect(path.path_with_suffix).to eql('/the/path(.json)') | |||
expect(path.path_with_suffix).to eql('/the/path(.:format)') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please explain this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, this is my bad. Fixed.
I will run this against a production codebase to see if anything fails. Generally I am down with merging this, properly READMEed, CHANGELOGed, squashed, etc., please. We need to replace rack-mount since it's a dead project. |
Haven't debugged, but the first failure in a real world project is with grape-swagger.
It's missing |
2acfd29
to
64bd67e
Compare
@dblock Sorry for the failing with grape-swagger. |
methods_per_path[route.route_path] ||= [] | ||
methods_per_path[route.route_path] << route.route_method | ||
# using the :any shorthand produces [nil] for route methods, substitute all manually | ||
route_settings = (routes_map[route.pattern.to_regexp] ||= {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not in love with this ||=
assignment inside the right hand side. It's just a little difficult to read.
9e06f27
to
1ab3595
Compare
Done, thanks for pointing out! |
First regression. We need specs on master for this since it wasn't caught in the Grape specs and I got it from my real world project, would appreciate a separate PR just for this story on master. require 'spec_helper'
describe Grape::API do
subject do
Class.new(Grape::API) do
format :json
get ':id' do
params
end
end
end
def app
subject
end
it 'does not include extension in id' do
get '/foo.bar'
expect(last_response.status).to eq 404
end
end This succeeds on master, fails on your branch with a 200, and returns a value of Note that it works consistently without a require 'spec_helper'
describe Grape::API do
subject do
Class.new(Grape::API) do
get ':id' do
params.to_json
end
end
end
def app
subject
end
it 'does not include extension in id' do
get '/foo.bar'
expect(last_response.status).to eq 200
expect(last_response.body).to eq({ id: 'foo', format: 'bar' }.to_json)
end
end |
Do you think we can preserve |
|
||
def transaction(env) | ||
input, method, routing_args = *extract_required_args(env) | ||
response = yield(input, method, ROUTING_ARGS_SETTER.curry[env, routing_args]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain what this magic is? :) This is beyond my Ruby-fu.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I removed the tricky code ;)
Failing spec for ruby-grape#1276.
ca065e2
to
92f4787
Compare
@dblock Thanks for your spec, I've just fixed the behavior and squashed. |
Running my battery of tests ... you can write UPGRADING in the meantime maybe. Thanks. |
``` | ||
|
||
Note that `Route#route_xyz` methods have been deprecated since 0.15.0. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be in UPGRADING.
From my tests ... this should be in UPGRADINGn and possibly in code: |
Another tricky one, namusyaka#2. |
path, line = *location.scan(SOURCE_LOCATION_REGEXP).first | ||
path = File.realpath(path) if Pathname.new(path).relative? | ||
warn <<-EOS | ||
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{name}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will say the wrong thing for route_method
;)
@dblock Thank you for polite review! Fixed. |
If the changes have no problem, I'm going to squashed them. |
Lets squash it and get it merged! |
41b5e89
to
9f4ba67
Compare
Wow, great! I'm so happy! Thank you! |
Replace rack-mount with new router
Merged. |
🎉 |
Is this Pendragon or... ?.. |
This is a first step for replcaing rack-mount with new router.
I'm going to improve performance to realize faster new router.
The new router has a few imcompatibility with old one.
The following cases:
route_*
attribute should be deprecated because the feature is implemented by usingmethod_missing
. So it's very slow. (However, reviving the attribute is possible. )rack.routing_args
toapi.routing_args
. The new router is for the Grape. I think the namespace is incorrect.Thoughts?