I have a problem of performances with rails. When i do an ajax call to a controller like this :
def test
@hotels = Hotel.all
render :json => ['hotels' => @hotels ], :include=> [:country, :city]
end
It takes maybe 2-5 seconds to finish. I only have 40 hotels in my database. I think it very long... for example, the same request on Django will take 400ms
Did i forgot to configure well my environement?
I use Rails entreprise version and passenger.
EDIT : My log file :
Started GET "/hotels/test.json" for 172.16.81.1 at Wed Oct 12 22:11:06 +0200 2011
[paperclip] Duplicate URL for image with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in HotelImage class
[paperclip] Duplicate URL for thumbnail with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in Hotel class
[paperclip] Duplicate URL for map with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in Hotel class
[paperclip] Duplicate URL for thumbnail with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in DestinationAlbumPhoto class
[paperclip] Duplicate URL for map with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in Destination class
[pa开发者_运维知识库perclip] Duplicate URL for image with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in Continent class
[paperclip] Duplicate URL for thumbnail with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in Destination class
[paperclip] Duplicate URL for image with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in Event class
[paperclip] Duplicate URL for thumbnail with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in HotelAlbumPhoto class
[paperclip] Duplicate URL for map with /system/:attachment/:id/:style/:filename. This will clash with attachment defined in Event class
Processing by HotelController#test as JSON
[1m[36mHotel Load (0.2ms)[0m [1mSELECT `hotels`.* FROM `hotels`[0m
[1m[35mCountry Load (0.1ms)[0m SELECT `countries`.* FROM `countries` WHERE (`countries`.`id` = 3)
[1m[36mCity Load (0.1ms)[0m [1mSELECT `cities`.* FROM `cities` WHERE (`cities`.`id` = 2)[0m
Completed 200 OK in 405ms (Views: 366.1ms | ActiveRecord: 0.3ms)
It's writen 405ms but firefox tell me 3,7sec.
My hotel model :
class Hotel < ActiveRecord::Base
cattr_reader :per_page
@@per_page = 16
belongs_to :hotel_type
belongs_to :hotel_theme
belongs_to :country
belongs_to :city
belongs_to :destination
belongs_to :continent
has_many :hotel_comments, :dependent => :destroy
has_many :hotel_album_photos, :dependent => :destroy
has_many :hotel_activity_values
has_many :hotel_service_values
accepts_nested_attributes_for :hotel_album_photos
has_attached_file :thumbnail, :styles => { :medium => "300x300>", :thumb => "191x134>"} , :default_url => '/images/default/missing.png'
has_attached_file :map, :styles => { :medium => "300x300>", :thumb => "191x134>"} , :default_url => '/images/default/missing.png'
scope :country, lambda { |country_id|
self.scoped.where('country_id IN ( ? )', country_id) unless country_id.blank?
}
scope :selection, lambda { |selection|
self.scoped.where('selection = ? ', 1) unless selection.blank?
}
scope :city, lambda { |city_id|
self.scoped.where('city_id IN ( ? )', city_id) unless city_id.blank?
}
scope :hoteltype, lambda { |type|
self.scoped.where('hotel_type_id IN ( ? )', type) unless type.blank?
}
scope :theme, lambda { |theme|
self.scoped.where('hotel_theme_id IN ( ? )', theme) unless theme.blank?
}
scope :prices, lambda { |prices|
condition = []
prices.each do |price|
pricesArray = price.split('-')
condition.push '(price BETWEEN ' + pricesArray[0] + ' AND ' + pricesArray[1] + ')'
end
self.scoped.where(condition.join(' OR '))
}
scope :order_by_price, lambda { |direction|
self.scoped.order('price ' + direction)
}
scope :order_by_rate, lambda { |rate|
self.scoped.order('global_rate ' + rate)
}
scope :services, lambda { |services|
{:joins => [:hotel_service_values ] , :conditions => { :hotel_service_values => {:hotel_service_id => services}}}
}
scope :limiter, lambda { |limiter|
self.scoped.limit(limiter)
}
end
Thank you for help.
Looking at you code, my guess is you have a simple "N+1" problem.
Namely you load @hotels
into an array, but when you come to produce the json
you load the country
and cityfor each
hotel.
So for your 40 hotels, you have to do a total of 81 database queries.
This can simply be improved by doing an include
when you load.
In the old style
Hotel.all(:include => [:country, :city])
In Rails 3 style
Hotel.includes(:country, :city).all
With this change you should only be making 3 database calls in total.
See the Rails Guide on Eager Loading for more info.
Looking at the Log, Rails seems to be thinking it got the response out in 405 ms. This leaves rest of the stack to consider:
- Rails (✓)
- Passenger
- Apache
- Network
- DNS resolving
- Browser rendering
First, see how other browsers fare. I have seen occasional issues on one of our (non-rails) sites with Firefox, while Webkit-based browsers (Safari, Chrome) are OK. Maybe Firefox chokes up for some reason (e.g. DNS resolving issues or something other).
If all browsers are the same (or especially, if Firefox is the culprit), then open Firebug and take look at the Net
tab, especially the XHR (AJAX) tab under that. Hover your mouse over one of the queries' time bar to see breakdown of where the time went: DNS, Connecting, Sending, Waiting, Receiving.
If Firebug indicates that the time is spent on the request itself (Sending, Waiting, Receiving), then take a look at Apache log - turn on CustomLog
time reporting (add %D
- see documentation) and see how long Apache thinks these requests take.
This should narrow down your problem to further analyze causes.
I was using VMWare to make my web server running. My performances problems was about that. I find my solution here :
Webrick is very slow to respond. How to speed it up?
精彩评论