Advice for Rails Performance Optimization
Upcoming Launch
Recently, our team releasing to a large set of users and needed to ensure that our application could meet the performance needs of the new users. Launch day was a month away. Months of steady Agile feature development needed to meet a healthy amount performance engineering.
We started with a few goals in mind. We wanted:
- data-driven improvements
- to prefer simple performant code to complex caching strategies
- to use available tools to provide visualization for badly performing requests
Ask for Help
"Does my Rails application on top of Passenger get blocked when I upload a file?"
Nope! Passenger uses plain old apache to buffer the file upload so your application instance is free to service other requests while the upload is finishing.
In my last post, I modified a Mongoid::Document during a migration in order to access fields that where no longer defined in the class. This time I am using the mongo ruby driver directly to migrate data.
Since I am using Mongoid in this project, I will be using it to access the dependent mongo driver. This will prevent me from having to provide mongo with a connection string in my migration. I am also using mongoid_rails_migrations to roll out the change.
Initial Design
class User
include Mongoid::Document
field :first_name
field :last_name
end
New Design
class User
include Mongoid::Document
field :name
end
Migration
class MergeUsersFirstAndLastName < Mongoid::Migration
def self.up
#get the mongo database instance from the Mongoid::Document
mongo_db = User.db
#query the collection for the fields needed for the migration
user_hashes = mongo_db.collection("users").find({}, :fields => ["first_name", "last_name"])
user_hashes.each do |user_hash|
new_name = "#{user_hash['first_name']} #{user_hash['last_name']}"
#update the new field
mongo_db.collection("users").update({"_id" => user_hash["_id"]}, {"$set" => {"name" => new_name}})
#remove old fields from collection
mongo_db.collection("users").update({"_id" => user_hash["_id"]}, {"$unset" => { "last_name" => 1, "first_name" => 1}})
end
end
end
Resources
Last week, we announced that we're introducing pricing for Pivotal Tracker. Since then, we've received a great deal of thoughtful and constructive feedback, and we're very grateful that the majority of you have shown support of Tracker’s move to be a paid product. We really appreciate all of your positive comments.
Continue reading below for details about changes to pricing that we're making based on your suggestions, and clarification of some aspects of pricing that may not have been stated clearly, including what remains free.
Ask for Help
Some pivots are trying to import a large number of images into paperclip, and it seemed to hang when shelling out to convert at different images.
Suggestions for troubleshooting included:
- look at the memory usage over the course of the import
- try running one file many time to see if it's just some bad images
Ask for Help
A pivot was looking for help with using mod_proxy on apache. It only seemed to want to proxy the root.
The only real suggestion available was to use HAProxy instead, but that wasn't an option for this project
Some pivots were looking for help using bundler together with capistrano, but without rvm.
No real suggestions on what the problem could be.
Some pivots were wondering how long of a time-box they should set aside to get jasmine running in CI.
Consensus was that it should only take about a half-hour.
Interesting Things
- A pivot noticed that the battery life on his MacBook Air seemed to suddenly drop from about 8 hours down to only 4 hours. After some digging through top, it turned out that running rails server and rake jasmine:ci were causing the machine to never be able to fully enter sleep/hibernate to save battery life. So remember to turn those off when you put your laptop away.
We're pleased to be sponsoring the Open Source Women's Leadership Summit this Friday. We think the work that the RailsBridge community has done is critical to making the Ruby community more diverse and more welcoming to everyone. We know women are significantly under-repesented in the development community as a whole, and in the Ruby community specifically, and we choose not to accept this as inevitable. It was Grace Hopper, after all, who developed this whole idea of programming languages.
Pragmatically, we also see this as an opportunity. Women hold up half the sky, but until recently held down only 3% of the seats at conferences. In a market where everyone is searching for developers, we ignore this large segment of potential developers to our great detriment. Where else are we going to find the potential to double our Ruby community?
And, as our economy slogs its way out of the deepest downturn since the Great Depression, we also see this as an opportunity to even out what has been a very uneven recovery. We have at once deep unemployment and many many unfilled jobs. The jobs are there but they have moved. We are hiring as fast as we find good people and we are certainly not alone. We see RailsBridge as a way to help people develop the skills they need to move to these new jobs, and they're high-paying jobs, too! What better community to enter than one where employers fight to pay you above the median San Francisco income?
The work that RailsBridge started is very important, and is far from done. The work takes a great deal of effort. To that end, we want to celebrate the people who are making this happen. Pivotal Labs is honored to be a sponsor, and to continue to support these fantastic programs with space, time, money and our voice.
Those of you in the San Francisco area, we encourage you to attend and help us celebrate these inspiring leaders, and those of you elsewhere we encourage you to get involved: help get the word out about this exciting movement.
Event Details on EventBrite: OpenSource Women's Leadership Summit
Say you're using Heroku to host a staging version of your app that uses Devise for authentication. You want to keep unwanted visitors out of your staging app, but your app only uses authentication for certain features. You want to block outsiders from accessing the app entirely. Since Heroku doesn't allow you whitelist IP addresses in your webserver config, you might want to try HTTP basic access authentication to provide top-level authentication. Doing this in Rails is pretty straightforward. Let's assume your staging app runs in the "staging environment."
application_controller.rb:
before_filter :authenticate if Rails.env.staging?
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "foo" && password == "bar"
end
end
See Ryan Bates' Railscast #82 HTTP Basic Authentication for more info.
This will work for most cases. However, since we have Devise installed, this won't work. Devise will enter an infinite loop of redirects unless we tell Warden to set the performed flag.
application_controller.rb:
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "foo" && password == "bar"
end
warden.custom_failure! if performed?
end
But wait, we're still not done. Devise has the ability to use your HTTP Basic Authentication credentials and use it to log into its own authentication system - a cool feature if you want to be able to pass your login credentials over HTTP, perhaps as a simple way to authenticate an API consumer. However, in our case, we want to keep the HTTP Basic Authentication that we require for the entire site separate from the Devise login that we use internal to the app, so we need to disable Devise's ability to capture our HTTP Basic Authentication credentials. In some versions of Devise, this feature is on by default, and in others it is off by default. Check your devise.rb file and make sure you have uncommented the line that disables Devise's http_authenticatable configuration setting.
devise.rb:
config.http_authenticatable = false
Be warned that HTTP Basic Authentication transmits the username and password in clear text, so you should not use this method for applications where a higher level of security is required.
Ask for Help
Some pivots were looking for experts with google analytics.
A pivot or two has had experience. Splunk was also suggested as a way to get some potentially different analytics, but warnings were given that it can take a while to get everything set up for Splunk to work correctly.
Interesting Things
Typus allows easily adding the ability for admin users to edit structured content in your rails app.
OpenSource Women's Leadership summit is this Friday. See here for tickets. And check this out for more info
Ask for Help
How do you customize the body of the email message in ActionMailer 3.0? Some pivots want an admin user to be able to edit the default version of the email to be sent, but ActionMailer 3.0 no longer allows the body to be set directly.*
One suggestion was to create a new model that has a default body, and allow the admin user to edit and save, and use that to render the body of the email.
Can you require Bundler inside of the same script that is installing it?
No real solutions other that probably trying to look into the Gem API
Interesting Things
- Amazon has released an email API for sending emails using their servers. Prices start at 10 cents per 1000 messages sent, with breaks for messages coming from within EC2
