- •Introduction
- •Rails Is Agile
- •Finding Your Way Around
- •Acknowledgments
- •Getting Started
- •Models, Views, and Controllers
- •Installing Rails
- •Installing on Windows
- •Installing on Mac OS X
- •Installing on Unix/Linux
- •Rails and Databases
- •Keeping Up-to-Date
- •Rails and ISPs
- •Creating a New Application
- •Hello, Rails!
- •Linking Pages Together
- •What We Just Did
- •Building an Application
- •The Depot Application
- •Incremental Development
- •What Depot Does
- •Task A: Product Maintenance
- •Iteration A1: Get Something Running
- •Iteration A2: Add a Missing Column
- •Iteration A4: Prettier Listings
- •Task B: Catalog Display
- •Iteration B1: Create the Catalog Listing
- •Iteration B2: Add Page Decorations
- •Task C: Cart Creation
- •Sessions
- •More Tables, More Models
- •Iteration C1: Creating a Cart
- •Iteration C3: Finishing the Cart
- •Task D: Checkout!
- •Iteration D2: Show Cart Contents on Checkout
- •Task E: Shipping
- •Iteration E1: Basic Shipping
- •Task F: Administrivia
- •Iteration F1: Adding Users
- •Iteration F2: Logging In
- •Iteration F3: Limiting Access
- •Finishing Up
- •More Icing on the Cake
- •Task T: Testing
- •Tests Baked Right In
- •Testing Models
- •Testing Controllers
- •Using Mock Objects
- •Test-Driven Development
- •Running Tests with Rake
- •Performance Testing
- •The Rails Framework
- •Rails in Depth
- •Directory Structure
- •Naming Conventions
- •Active Support
- •Logging in Rails
- •Debugging Hints
- •Active Record Basics
- •Tables and Classes
- •Primary Keys and IDs
- •Connecting to the Database
- •Relationships between Tables
- •Transactions
- •More Active Record
- •Acts As
- •Aggregation
- •Single Table Inheritance
- •Validation
- •Callbacks
- •Advanced Attributes
- •Miscellany
- •Action Controller and Rails
- •Context and Dependencies
- •The Basics
- •Routing Requests
- •Action Methods
- •Caching, Part One
- •The Problem with GET Requests
- •Action View
- •Templates
- •Builder templates
- •RHTML Templates
- •Helpers
- •Formatting Helpers
- •Linking to Other Pages and Resources
- •Pagination
- •Form Helpers
- •Layouts and Components
- •Adding New Templating Systems
- •Introducing AJAX
- •The Rails Way
- •Advanced Techniques
- •Action Mailer
- •Sending E-mail
- •Receiving E-mail
- •Testing E-mail
- •Web Services on Rails
- •Dispatching Modes
- •Using Alternate Dispatching
- •Method Invocation Interception
- •Testing Web Services
- •Protocol Clients
- •Securing Your Rails Application
- •SQL Injection
- •Cross-Site Scripting (CSS/XSS)
- •Avoid Session Fixation Attacks
- •Creating Records Directly from Form Parameters
- •Knowing That It Works
- •Deployment and Scaling
- •Picking a Production Platform
- •A Trinity of Environments
- •Iterating in the Wild
- •Maintenance
- •Finding and Dealing with Bottlenecks
- •Case Studies: Rails Running Daily
- •Appendices
- •Introduction to Ruby
- •Ruby Names
- •Regular Expressions
- •Source Code
- •Cross-Reference of Code Samples
- •Resources
- •Index
A TRINITY OF ENVIRONMENTS 448
to that shortly), that it should boot 15 servers initially (a good starting number for a dedicated server), and that we want the timeout to be 60 seconds instead of the default 30.
This timeout is a critical value. If any request takes longer than the limit allows, Apache will assume that FastCGI crashed and return an error 500 (and possibly kill the process). You may need to push the timeout even higher, depending on your application. This is especially important if your application talks to remote servers and even more so if it needs to transfer large amounts of data to them.
With FastCGI both installed and configured, you’ll just need to change your public/.htaccess file6 to referencedispatch.fcgi instead of dispatch.cgi, restart your server, and hit Refresh in your browser. If all went well, you’ll pay the start-up price of initialization, and then all subsequent requests should be riding the FastCGI lightning.
If all didn’t go well, you’ll have three log files to investigate. First is the Apache error log, which is configured either in your vhost or in the master httpd.conf. This is normally where you’ll find errors about mod_fastcgi being misconfigured (pointing to the wrong dispatcher file, for example). Next is fastcgi.crash.log, which is located in your application log/ folder. This might contain a trace of problems that occur after the Dispatcher had been found and triggered. Finally, there’s the regular Rails production log, which may contain errors from within your application. Configuration problems show up in the first two of these logs, and application problems in the third.
22.2 A Trinity of Environments
Rails has three different environments: development, test, and production. Throughout the book, we’ve been using the default development environment, which reloads the application on every request and makes sure none of the caching mechanisms is active. In the testing chapter, we used the test environment that, for example, ensures that the Action Mailer simulates sending e-mail, rather than actually delivering it.
When we deploy our Rails applications, we use the production environment, where ease of development is traded for speed. As can be seen in config/environments/production.rb, the most important change from development to production is the change of Dependencies.mechanism from :load
6If you want to squeeze the last drop of performance out of Apache, you could make these configuration changes in the server’s main configuration file (often httpd.conf) instead.
Prepared exclusively for Rida Al Barazi
Report erratum
A TRINITY OF ENVIRONMENTS 449
to :require. This ensures that once a model, controller, or other class has been loaded, Rails won’t load it again. In the development environment it is convenient to have these files reloaded, as it means that Rails will pick up changes we make. In production we trade that convenience for speed: there’s no overhead of recompiling on each request, but changes in the application’s source files won’t be honored until the server is restarted.
Rails distinguishes requests that come from local—friendly—hosts from those that don’t. If a failure occurs while handling a request from a local host, Rails displays a wealth of debugging information on the browser as an aid to the developer. In the development environment, Rails assumes that all requests are local. In production, this assumption is disabled; any request coming from outside the local host will no longer see the debugging screen on error. Instead, they’ll see the generic public/500.html page. We’ll return to the implications of this in Section 22.3, Iterating in the Wild, on the following page.
Caching is enabled in production environments. This means that things such as caches_page, the sweepers, and the rest of the caching infrastructure will actually start performing their duties. In development, the parameter ActionController::Base.perform_caching is set to false, and they simply have no effect.
Switching to the Production Environment
You need to tell Rails to use the production environment in order to enjoy the speed and caching it supports. The trick is that you would rather not make any changes to your application in order to do so since that would require a different code base for production and development. For quick tests of changing environments, you could hack config/environment.rb and force the constant RAILS_ENV to be something other than "development", but that’s messy.
That’s why the Rails environment is also changeable through an external environment variable, also called RAILS_ENV. If the environment variable is set, Rails uses its value to define the environment. If RAILS_ENV isn’t set, Rails defaults to "development". To run your application in the production environment, you have to make sure that ENV[’RAILS_ENV’] is set to "production" before Ruby compiles environment.rb. This is easier said than done.
The problem is that the three different web servers each have a unique way of setting environment variables.
Prepared exclusively for Rida Al Barazi
Report erratum
ITERATING IN THE WILD 450
WEBrick:
./script/server --environment=production
Apache/CGI:
In the vhost configuration in httpd.conf, or in the local .htaccess file, set
SetEnv RAILS_ENV production
Apache/FastCGI:
In httpd.conf, add the following option to the FastCgiServer definition.
-initial-env RAILS_ENV=production
lighttpd/FastCGI:
In the fastcgi.server definition file, set
"bin-environment" => ("RAILS_ENV" => "production")
See the Rails README for a longer example.
To change the environment when using scripts such as the Rails runner, you can use a shell assignment, such as
myapp> RAILS_ENV=production ./script/runner 'puts Account.size'
22.3 Iterating in the Wild
Now that your application is being served through FastCGI in the production environment, how do you keep moving forward? Deploying the application is just the beginning of life outside the lab. You need to be able to react to errors and update the codebase to fix these errors (or add features). You also need to be able to diagnose problems when things go wrong.
Handling Errors
In development, everyone sees the debugging screen when something goes wrong. Presenting the end user with a stacktrace when they encounter a problem isn’t particularly friendly, though. So in the production environment, you get a debugging screen by default only when operating from localhost. While that protects the user from being exposed to the system internals, it does the same for the developer trying to debug a problem on the production server, which is not really what we want either.
Luckily, that’s easy to remedy. Action Controller provides a protected method called local_request?( ), which it uses to determine if a request is coming from a local host. In production, this by default returns true if the
Prepared exclusively for Rida Al Barazi
Report erratum
ITERATING IN THE WILD 451
request is coming from 127.0.0.1. You can change this to check against a certain session value tied to your authentication scheme or you could just expand the range of IPs to include the public IPs of your developers.
def local_request?
["127.0.0.1", "88.88.888.101", "77.77.777.102"].include?(request.remote_ip) end
Although this method can be overwritten on a per-controller basis, normally you’ll redefine it just once in ApplicationController (the file application.rb in app/controllers) to share the same definition local across all controllers.
How do you know if a user saw an error and that an investigation is required? You could search the logs every night, but you’d probably forget every now and then, leaving potentially critical errors unsolved for hours or days. It would be better to be notified the minute an exception is thrown and then decide whether it’s something that needs immediate attention or not. E-mail is great for this.
Action Controller has yet another hook that makes adding e-mail notifications on exceptions easy. The method rescue_action_in_public( ) in ActionController::Base is called whenever an exception is raised. This method can be defined in individual controllers, or you can make it global by putting it in application.rb. It’s passed the exception as a parameter. We could override it to send an e-mail to the application maintainer.
def rescue_action_in_public(exception) case exception
when ActiveRecord::RecordNotFound, ActionController::UnknownAction render(:file => "#{RAILS_ROOT}/public/404.html",
:status => "404 Not Found")
else
render(:file => "#{RAILS_ROOT}/public/500.html", :status => "500 Error")
SystemNotifier.deliver_exception_notification( self, request, exception)
end end
In this example, we treat missing records and actions as 404 errors that need not be reported through e-mail. If the exception is anything else, the developers should know about it. SystemNotifier is an Action Mailer class; its exception_notification( ) method packages the exception and the environment in which it occured in a pretty e-mail that goes to the developers. A sample implementation of the notifier and the corresponding view is shown starting on page 511.
Prepared exclusively for Rida Al Barazi
Report erratum
ITERATING IN THE WILD 452
Pushing Changes
After running in production for a while, you find a bug in your application. The fix needs to get applied post haste. The problem is that you can’t take the application offline while doing so—you need to hot-deploy the fix. One way of doing this uses the power of symbolic links.
The trick is to make the application directory used by your web server a symbolic link (symlink). Install your application files somewhere else and have the symlink point to that location. When it comes time to make a new release live, check out the application into a new directory and change the symlink to point there. Restart, and you’re running the latest version. If you need to back out of a bad release, all you need to do is change the symlink back to the previous version, and all is well.
With symlinks, you can set up a structure where a revision of your code base that’s ready to be pushed live goes through the following steps.
1.Check out the latest version of the codebase into a directory labelled after the version, such as releases/rel25.
2.Delete the old current → releases/rel24 symlink, and create a symlink to the new release: current → releases/rel25. This is shown in Figure 22.2, on the following page.
3.Restart the web server and stand-alone FastCGI servers.
The situation is slightly more complicated if you also have to include changes to the database schema. In this case you’ll need to stop the application while you update the schema. If you don’t, you might end up with the old application using the new schema.
1.Check out the latest version of the code.
2.Stop the application. If you’ll be down for a while, redirect all requests to a simple Pardon our Dust page.
3.Run any database migration scripts or other post-checkout activities (such as clearing caches) that the new version might require.
4.Move the symlink to the new code.
5.Restart the web server and stand-alone FastCGI servers.
The last step, restarting stand-alone FastCGI servers, deserves a little more detail. We need to ensure that we don’t interrupt any requests when making the switch. If we simply killed and restarted server processes, we could lose a request that was in the middle of being processed. This would
Prepared exclusively for Rida Al Barazi
Report erratum
ITERATING IN THE WILD 453
|
releases/ |
|
|
www/ |
|
rel23/ |
rel24/ |
rel25/ |
public/ |
cgi-bin/ |
logs/ |
|
|
symlink |
|
|
|
|
releases/ |
|
|
www/ |
|
rel23/ |
rel24/ |
rel25/ |
public/ |
cgi-bin/ |
logs/ |
|
|
symlink |
|
|
|
Figure 22.2: Using a Symlink to Switch Versions
inconvenience our users and potentially cost us money (it could have been a payment transaction that we discarded). Apache features the graceful way of restarting softly by allowing all current requests to finish before bouncing the server. The FastCGI dispatcher in Rails has an identical option. On Unix systems, instead of sending the regular KILL or HUP signal to the processes, send them a SIGUSR1 signal. Rails will then allow the current request to finish before doing the bounce.
dave> killall -USR1 dispatch.fcgi
This approach takes a bit of preparation—you have to set up the deployment scripts, directories, and symlinks—but it’s more than worth it. The whole idea of Rails is to deliver working software faster. If you’re able to push changes only every second Sunday between 4:00 and 4:30 a.m., you’re not really taking advantage of that capability.
Using the Console to Look at a Live Application
Sometimes the cause of a problem resides not in the application code but rather in some bad data. The standard approach of solving data problems is to dive straight into the database and start writing queries and updates by hand. That’s hard work. Happily, it’s unnecessary in Rails.
You’ve already created a wonderful set of model classes to represent the domain. These were intended to be used by your application’s controllers.
Prepared exclusively for Rida Al Barazi
Report erratum