diff options
author | Colin Law <colin@clanlaw.org.uk> | 2009-01-11 12:13:50 +0000 |
---|---|---|
committer | Colin Law <colin@clanlaw.org.uk> | 2009-01-11 12:13:50 +0000 |
commit | 55901d7d86f5608c4a07386ca6378121eb8127cb (patch) | |
tree | 8ede6a21529dfac8c818a3fe1584fb46f00c03ff /railties/doc/guides | |
parent | 3e60ea6c231810e2b850492a86874b4800dfbf27 (diff) | |
parent | cf4b24407ada79c133fbae1ec7db692882225956 (diff) | |
download | rails-55901d7d86f5608c4a07386ca6378121eb8127cb.tar.gz rails-55901d7d86f5608c4a07386ca6378121eb8127cb.tar.bz2 rails-55901d7d86f5608c4a07386ca6378121eb8127cb.zip |
Merge branch 'master' of git@github.com:lifo/docrails
Diffstat (limited to 'railties/doc/guides')
-rw-r--r-- | railties/doc/guides/html/activerecord_validations_callbacks.html | 122 | ||||
-rw-r--r-- | railties/doc/guides/html/index.html | 2 | ||||
-rw-r--r-- | railties/doc/guides/html/performance_testing.html | 557 | ||||
-rw-r--r-- | railties/doc/guides/source/activerecord_validations_callbacks.txt | 79 | ||||
-rw-r--r-- | railties/doc/guides/source/index.txt | 2 | ||||
-rw-r--r-- | railties/doc/guides/source/performance_testing.txt | 433 |
6 files changed, 854 insertions, 341 deletions
diff --git a/railties/doc/guides/html/activerecord_validations_callbacks.html b/railties/doc/guides/html/activerecord_validations_callbacks.html index 7936de209d..0862776f53 100644 --- a/railties/doc/guides/html/activerecord_validations_callbacks.html +++ b/railties/doc/guides/html/activerecord_validations_callbacks.html @@ -285,9 +285,19 @@ ul#navMain { <li><a href="#_callbacks_registration">Callbacks registration</a></li> - <li><a href="#_registering_callbacks_by_overriding_the_callback_methods">Registering callbacks by overriding the callback methods</a></li> + </ul> + </li> + <li> + <a href="#_conditional_callbacks">Conditional callbacks</a> + <ul> - <li><a href="#_registering_callbacks_by_using_macro_style_class_methods">Registering callbacks by using macro-style class methods</a></li> + <li><a href="#_using_a_symbol_with_the_tt_if_tt_and_tt_unless_tt_options_2">Using a symbol with the <tt>:if</tt> and <tt>:unless</tt> options</a></li> + + <li><a href="#_using_a_string_with_the_tt_if_tt_and_tt_unless_tt_options_2">Using a string with the <tt>:if</tt> and <tt>:unless</tt> options</a></li> + + <li><a href="#_using_a_proc_object_with_the_tt_if_tt_and_tt_unless_tt_options_2">Using a Proc object with the <tt>:if</tt> and :<tt>unless</tt> options</a></li> + + <li><a href="#_multiple_conditions_for_callbacks">Multiple Conditions for Callbacks</a></li> </ul> </li> @@ -635,7 +645,7 @@ http://www.gnu.org/software/src-highlite --> <td class="icon">
<img src="./images/icons/note.png" alt="Note" />
</td>
-<td class="content">If you want to validate the presence of a boolean field (where the real values are true and false), you will want to use validates_inclusion_of :field_name, :in => [true, false] This is due to the way Object#blank? handles boolean values. false.blank? # => true</td>
+<td class="content">If you want to validate the presence of a boolean field (where the real values are true and false), you will want to use validates_inclusion_of :field_name, :in ⇒ [true, false] This is due to the way Object#blank? handles boolean values. false.blank? # ⇒ true</td>
</tr></table>
</div>
<div class="paragraph"><p>The default error message for <tt>validates_presence_of</tt> is "<em>can’t be empty</em>".</p></div>
@@ -735,7 +745,7 @@ http://www.gnu.org/software/src-highlite --> <div class="sectionbody">
<div class="paragraph"><p>Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the <tt>:if</tt> and <tt>:unless</tt> options, which can take a symbol, a string or a Ruby Proc. You may use the <tt>:if</tt> option when you want to specify when the validation <strong>should</strong> happen. If you want to specify when the validation <strong>should not</strong> happen, then you may use the <tt>:unless</tt> option.</p></div>
<h3 id="_using_a_symbol_with_the_tt_if_tt_and_tt_unless_tt_options">5.1. Using a symbol with the <tt>:if</tt> and <tt>:unless</tt> options</h3>
-<div class="paragraph"><p>You can associated the <tt>:if</tt> and <tt>:unless</tt> options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.</p></div>
+<div class="paragraph"><p>You can associate the <tt>:if</tt> and <tt>:unless</tt> options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -1055,26 +1065,7 @@ An object of the <tt>ActionView::Helpers::InstanceTag</tt> class. <div class="sectionbody">
<div class="paragraph"><p>Callbacks are methods that get called at certain moments of an object’s lifecycle. With callbacks it’s possible to write code that will run whenever an Active Record object is created, saved, updated, deleted or loaded from the database.</p></div>
<h3 id="_callbacks_registration">9.1. Callbacks registration</h3>
-<div class="paragraph"><p>In order to use the available callbacks, you need to registrate them. There are two ways of doing that.</p></div>
-<h3 id="_registering_callbacks_by_overriding_the_callback_methods">9.2. Registering callbacks by overriding the callback methods</h3>
-<div class="paragraph"><p>You can specify the callback method directly, by overriding it. Let’s see how it works using the <tt>before_validation</tt> callback, which will surprisingly run right before any validation is done.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> User <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_presence_of <span style="color: #990000">:</span>login<span style="color: #990000">,</span> <span style="color: #990000">:</span>email
-
- protected
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> before_validation
- <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>login<span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">?</span>
- <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span>login <span style="color: #990000">=</span> email <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> email<span style="color: #990000">.</span>blank?
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
-<h3 id="_registering_callbacks_by_using_macro_style_class_methods">9.3. Registering callbacks by using macro-style class methods</h3>
-<div class="paragraph"><p>The other way you can register a callback method is by implementing it as an ordinary method, and then using a macro-style class method to register it as a callback. The last example could be written like that:</p></div>
+<div class="paragraph"><p>In order to use the available callbacks, you need to registrate them. You can do that by implementing them as an ordinary methods, and then using a macro-style class method to register then as callbacks.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -1103,19 +1094,6 @@ http://www.gnu.org/software/src-highlite --> before_create <span style="color: #FF0000">{</span><span style="color: #990000">|</span>user<span style="color: #990000">|</span> user<span style="color: #990000">.</span>name <span style="color: #990000">=</span> user<span style="color: #990000">.</span>login<span style="color: #990000">.</span>capitalize <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> user<span style="color: #990000">.</span>name<span style="color: #990000">.</span>blank?<span style="color: #FF0000">}</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
-<div class="paragraph"><p>In Rails, the preferred way of registering callbacks is by using macro-style class methods. The main advantages of using macro-style class methods are:</p></div>
-<div class="ulist"><ul>
-<li>
-<p>
-You can add more than one method for each type of callback. Those methods will be queued for execution at the same order they were registered.
-</p>
-</li>
-<li>
-<p>
-Readability, since your callback declarations will live at the beggining of your models' files.
-</p>
-</li>
-</ul></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@@ -1125,10 +1103,56 @@ Readability, since your callback declarations will live at the beggining of your </tr></table>
</div>
</div>
-<h2 id="_available_callbacks">10. Available callbacks</h2>
+<h2 id="_conditional_callbacks">10. Conditional callbacks</h2>
+<div class="sectionbody">
+<div class="paragraph"><p>Like in validations, we can also make our callbacks conditional, calling then only when a given predicate is satisfied. You can do that by using the <tt>:if</tt> and <tt>:unless</tt> options, which can take a symbol, a string or a Ruby Proc. You may use the <tt>:if</tt> option when you want to specify when the callback <strong>should</strong> get called. If you want to specify when the callback <strong>should not</strong> be called, then you may use the <tt>:unless</tt> option.</p></div>
+<h3 id="_using_a_symbol_with_the_tt_if_tt_and_tt_unless_tt_options_2">10.1. Using a symbol with the <tt>:if</tt> and <tt>:unless</tt> options</h3>
+<div class="paragraph"><p>You can associate the <tt>:if</tt> and <tt>:unless</tt> options with a symbol corresponding to the name of a method that will get called right before the callback. If this method returns <tt>false</tt> the callback won’t be executed. This is the most common option. Using this form of registration it’s also possible to register several different methods that should be called to check the if the callback should be executed.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
+ before_save <span style="color: #990000">:</span>normalize_card_number<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">=></span> <span style="color: #990000">:</span>paid_with_card?
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
+<h3 id="_using_a_string_with_the_tt_if_tt_and_tt_unless_tt_options_2">10.2. Using a string with the <tt>:if</tt> and <tt>:unless</tt> options</h3>
+<div class="paragraph"><p>You can also use a string that will be evaluated using <tt>:eval</tt> and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
+ before_save <span style="color: #990000">:</span>normalize_card_number<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">=></span> <span style="color: #FF0000">"paid_with_card?"</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
+<h3 id="_using_a_proc_object_with_the_tt_if_tt_and_tt_unless_tt_options_2">10.3. Using a Proc object with the <tt>:if</tt> and :<tt>unless</tt> options</h3>
+<div class="paragraph"><p>Finally, it’s possible to associate <tt>:if</tt> and <tt>:unless</tt> with a Ruby Proc object. This option is best suited when writing short validation methods, usually one-liners.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Order <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
+ before_save <span style="color: #990000">:</span>normalize_card_number<span style="color: #990000">,</span>
+ <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">=></span> Proc<span style="color: #990000">.</span>new <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>order<span style="color: #990000">|</span> order<span style="color: #990000">.</span>paid_with_card? <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
+<h3 id="_multiple_conditions_for_callbacks">10.4. Multiple Conditions for Callbacks</h3>
+<div class="paragraph"><p>When writing conditional callbacks, it’s possible to mix both <tt>:if</tt> and <tt>:unless</tt> in the same callback declaration.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Comment <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
+ after_create <span style="color: #990000">:</span>send_email_to_author<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">=></span> <span style="color: #990000">:</span>author_wants_emails?<span style="color: #990000">,</span>
+ <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> <span style="color: #990000">=></span> Proc<span style="color: #990000">.</span>new <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>comment<span style="color: #990000">|</span> comment<span style="color: #990000">.</span>post<span style="color: #990000">.</span>ignore_comments? <span style="color: #FF0000">}</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
+</div>
+<h2 id="_available_callbacks">11. Available callbacks</h2>
<div class="sectionbody">
<div class="paragraph"><p>Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations.</p></div>
-<h3 id="_callbacks_called_both_when_creating_or_updating_a_record">10.1. Callbacks called both when creating or updating a record.</h3>
+<h3 id="_callbacks_called_both_when_creating_or_updating_a_record">11.1. Callbacks called both when creating or updating a record.</h3>
<div class="ulist"><ul>
<li>
<p>
@@ -1156,7 +1180,7 @@ Readability, since your callback declarations will live at the beggining of your </p>
</li>
</ul></div>
-<h3 id="_callbacks_called_only_when_creating_a_new_record">10.2. Callbacks called only when creating a new record.</h3>
+<h3 id="_callbacks_called_only_when_creating_a_new_record">11.2. Callbacks called only when creating a new record.</h3>
<div class="ulist"><ul>
<li>
<p>
@@ -1184,7 +1208,7 @@ Readability, since your callback declarations will live at the beggining of your </p>
</li>
</ul></div>
-<h3 id="_callbacks_called_only_when_updating_an_existing_record">10.3. Callbacks called only when updating an existing record.</h3>
+<h3 id="_callbacks_called_only_when_updating_an_existing_record">11.3. Callbacks called only when updating an existing record.</h3>
<div class="ulist"><ul>
<li>
<p>
@@ -1212,7 +1236,7 @@ Readability, since your callback declarations will live at the beggining of your </p>
</li>
</ul></div>
-<h3 id="_callbacks_called_when_removing_a_record_from_the_database">10.4. Callbacks called when removing a record from the database.</h3>
+<h3 id="_callbacks_called_when_removing_a_record_from_the_database">11.4. Callbacks called when removing a record from the database.</h3>
<div class="ulist"><ul>
<li>
<p>
@@ -1231,16 +1255,16 @@ Readability, since your callback declarations will live at the beggining of your </li>
</ul></div>
<div class="paragraph"><p>The <tt>before_destroy</tt> and <tt>after_destroy</tt> callbacks will only be called if you delete the model using either the <tt>destroy</tt> instance method or one of the <tt>destroy</tt> or <tt>destroy_all</tt> class methods of your Active Record class. If you use <tt>delete</tt> or <tt>delete_all</tt> no callback operations will run, since Active Record will not instantiate any objects, accessing the records to be deleted directly in the database.</p></div>
-<h3 id="_the_tt_after_initialize_tt_and_tt_after_find_tt_callbacks">10.5. The <tt>after_initialize</tt> and <tt>after_find</tt> callbacks</h3>
+<h3 id="_the_tt_after_initialize_tt_and_tt_after_find_tt_callbacks">11.5. The <tt>after_initialize</tt> and <tt>after_find</tt> callbacks</h3>
<div class="paragraph"><p>The <tt>after_initialize</tt> callback will be called whenever an Active Record object is instantiated, either by direcly using <tt>new</tt> or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record <tt>initialize</tt> method.</p></div>
<div class="paragraph"><p>The <tt>after_find</tt> callback will be called whenever Active Record loads a record from the database. When used together with <tt>after_initialize</tt> it will run first, since Active Record will first read the record from the database and them create the model object that will hold it.</p></div>
<div class="paragraph"><p>The <tt>after_initialize</tt> and <tt>after_find</tt> callbacks are a bit different from the others, since the only way to register those callbacks is by defining them as methods. If you try to register <tt>after_initialize</tt> or <tt>after_find</tt> using macro-style class methods, they will just be ignored. This behaviour is due to performance reasons, since <tt>after_initialize</tt> and <tt>after_find</tt> will both be called for each record found in the database, significantly slowing down the queries.</p></div>
</div>
-<h2 id="_halting_execution">11. Halting Execution</h2>
+<h2 id="_halting_execution">12. Halting Execution</h2>
<div class="sectionbody">
<div class="paragraph"><p>As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model’s validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the <tt>before_create</tt>, <tt>before_save</tt>, <tt>before_update</tt> or <tt>before_destroy</tt> callback methods returns a boolean <tt>false</tt> (not <tt>nil</tt>) value, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on.</p></div>
</div>
-<h2 id="_callback_classes">12. Callback classes</h2>
+<h2 id="_callback_classes">13. Callback classes</h2>
<div class="sectionbody">
<div class="paragraph"><p>Sometimes the callback methods that you’ll write will be useful enough to be reused at other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them.</p></div>
<div class="paragraph"><p>Here’s an example where we create a class with a after_destroy callback for a PictureFile model.</p></div>
@@ -1285,7 +1309,7 @@ http://www.gnu.org/software/src-highlite --> <span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
<div class="paragraph"><p>You can declare as many callbacks as you want inside your callback classes.</p></div>
</div>
-<h2 id="_observers">13. Observers</h2>
+<h2 id="_observers">14. Observers</h2>
<div class="sectionbody">
<div class="paragraph"><p>Active Record callbacks are a powerful feature, but they can pollute your model implementation with code that’s not directly related to the model’s purpose. In object-oriented software, it’s always a good idea to design your classes with a single responsibility in the whole system. For example, it wouldn’t make much sense to have a <tt>User</tt> model with a method that writes data about a login attempt to a log file. Whenever you’re using callbacks to write code that’s not directly related to your model class purposes, it may be a good moment to create an Observer.</p></div>
<div class="paragraph"><p>An Active Record Observer is an object that links itself to a model and registers its methods for callbacks. Your model’s implementation remains clean, while you can reuse the code in the Observer to add behaviour to more than one model class. OK, you may say that we can also do that using callback classes, but it would still force us to add code to our model’s implementation.</p></div>
@@ -1311,7 +1335,7 @@ http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Auditor <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Observer
observe User<span style="color: #990000">,</span> Registration<span style="color: #990000">,</span> Invoice
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
-<h3 id="_registering_observers">13.1. Registering observers</h3>
+<h3 id="_registering_observers">14.1. Registering observers</h3>
<div class="paragraph"><p>If you paid attention, you may be wondering where Active Record Observers are referenced in our applications, so they get instantiated and begin to interact with our models. For observers to work we need to register them somewhere. The usual place to do that is in our application’s <strong>config/environment.rb</strong> file. In this file there is a commented-out line where we can define the observers that our application should load at start-up.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@@ -1322,10 +1346,10 @@ http://www.gnu.org/software/src-highlite --> config<span style="color: #990000">.</span>active_record<span style="color: #990000">.</span>observers <span style="color: #990000">=</span> <span style="color: #990000">:</span>registration_observer<span style="color: #990000">,</span> <span style="color: #990000">:</span>auditor</tt></pre></div></div>
<div class="paragraph"><p>You can uncomment the line with <tt>config.active_record.observers</tt> and change the symbols for the name of the observers that should be registered.</p></div>
<div class="paragraph"><p>It’s also possible to register callbacks in any of the files living at <strong>config/environments/</strong>, if you want an observer to work only in a specific environment. There is not a <tt>config.active_record.observers</tt> line at any of those files, but you can simply add it.</p></div>
-<h3 id="_where_to_put_the_observers_source_files">13.2. Where to put the observers' source files</h3>
+<h3 id="_where_to_put_the_observers_source_files">14.2. Where to put the observers' source files</h3>
<div class="paragraph"><p>By convention, you should always save your observers' source files inside <strong>app/models</strong>.</p></div>
</div>
-<h2 id="_changelog">14. Changelog</h2>
+<h2 id="_changelog">15. Changelog</h2>
<div class="sectionbody">
<div class="paragraph"><p><a href="http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks">http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks</a></p></div>
</div>
diff --git a/railties/doc/guides/html/index.html b/railties/doc/guides/html/index.html index b2dce5cd67..8dc8f6f95b 100644 --- a/railties/doc/guides/html/index.html +++ b/railties/doc/guides/html/index.html @@ -348,7 +348,7 @@ of your code.</p></div> </div></div>
<div class="sidebarblock">
<div class="sidebar-content">
-<div class="sidebar-title"><a href="performance_testing.html">Performance testing Rails Applications</a></div>
+<div class="sidebar-title"><a href="performance_testing.html">Performance Testing Rails Applications</a></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
diff --git a/railties/doc/guides/html/performance_testing.html b/railties/doc/guides/html/performance_testing.html index a13c547849..2e6ba0a891 100644 --- a/railties/doc/guides/html/performance_testing.html +++ b/railties/doc/guides/html/performance_testing.html @@ -2,7 +2,7 @@ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <title>Performance testing Rails Applications</title> + <title>Performance Testing Rails Applications</title> <!--[if lt IE 8]> <script src="http://ie7-js.googlecode.com/svn/version/2.0(beta3)/IE8.js" type="text/javascript"></script> <![endif]--> @@ -199,32 +199,62 @@ ul#navMain { <h2>Chapters</h2> <ol> <li> - <a href="#_using_and_understanding_the_log_files">Using and understanding the log files</a> - </li> - <li> - <a href="#_helper_methods">Helper methods</a> - </li> - <li> <a href="#_performance_test_cases">Performance Test Cases</a> <ul> + <li><a href="#_generating_performance_tests">Generating performance tests</a></li> + + <li><a href="#_examples">Examples</a></li> + <li><a href="#_modes">Modes</a></li> <li><a href="#_metrics">Metrics</a></li> <li><a href="#_understanding_the_output">Understanding the output</a></li> - <li><a href="#_preparing_ruby_and_ruby_prof">Preparing Ruby and Ruby-prof</a></li> + <li><a href="#_tuning_test_runs">Tuning Test Runs</a></li> - <li><a href="#_generating_performance_test">Generating performance test</a></li> + <li><a href="#gc">Installing GC-Patched Ruby</a></li> </ul> </li> <li> - <a href="#_other_profiling_tools">Other Profiling Tools</a> + <a href="#_command_line_tools">Command Line Tools</a> + <ul> + + <li><a href="#_benchmarker">benchmarker</a></li> + + <li><a href="#_profiler">profiler</a></li> + + </ul> </li> <li> - <a href="#_commercial_products_dedicated_to_rails_perfomance">Commercial products dedicated to Rails Perfomance</a> + <a href="#_helper_methods">Helper methods</a> + <ul> + + <li><a href="#_model">Model</a></li> + + <li><a href="#_controller">Controller</a></li> + + <li><a href="#_view">View</a></li> + + </ul> + </li> + <li> + <a href="#_request_logging">Request Logging</a> + </li> + <li> + <a href="#_useful_profiling_tools">Useful Profiling Tools</a> + <ul> + + <li><a href="#_rails_plugins_and_gems">Rails Plugins and Gems</a></li> + + <li><a href="#_external">External</a></li> + + </ul> + </li> + <li> + <a href="#_commercial_products">Commercial Products</a> </li> <li> <a href="#_changelog">Changelog</a> @@ -233,10 +263,10 @@ ul#navMain { </div> <div id="content"> - <h1>Performance testing Rails Applications</h1> + <h1>Performance Testing Rails Applications</h1> <div id="preamble">
<div class="sectionbody">
-<div class="paragraph"><p>This guide covers the benchmarking and profiling tactics/tools of Rails and Ruby in general. By referring to this guide, you will be able to:</p></div>
+<div class="paragraph"><p>This guide covers the various ways of performance testing a Ruby on Rails application. By referring to this guide, you will be able to:</p></div>
<div class="ulist"><ul>
<li>
<p>
@@ -245,17 +275,17 @@ Understand the various types of benchmarking and profiling metrics </li>
<li>
<p>
-Generate performance/benchmarking tests
+Generate performance and benchmarking tests
</p>
</li>
<li>
<p>
-Use GC patched Ruby binary to measure memory usage and object allocation
+Use a GC-patched Ruby binary to measure memory usage and object allocation
</p>
</li>
<li>
<p>
-Understand the information provided by Rails inside the log files
+Understand the benchmarking information provided by Rails inside the log files
</p>
</li>
<li>
@@ -264,77 +294,121 @@ Learn about various tools facilitating benchmarking and profiling </p>
</li>
</ul></div>
-<div class="paragraph"><p>Performance testing is an integral part of the development cycle. It is very important that you don’t make your end users wait for too long before the page is completely loaded. Ensuring a plesant browsing experience to the end users and cutting cost of unnecessary hardwares is important for any web application.</p></div>
+<div class="paragraph"><p>Performance testing is an integral part of the development cycle. It is very important that you don’t make your end users wait for too long before the page is completely loaded. Ensuring a pleasant browsing experience for end users and cutting the cost of unnecessary hardware is important for any non-trivial web application.</p></div>
</div>
</div>
-<h2 id="_using_and_understanding_the_log_files">1. Using and understanding the log files</h2>
+<h2 id="_performance_test_cases">1. Performance Test Cases</h2>
<div class="sectionbody">
-<div class="paragraph"><p>Rails logs files containt basic but very useful information about the time taken to serve every request. A typical log entry looks something like :</p></div>
+<div class="paragraph"><p>Rails performance tests are a special type of integration tests, designed for benchmarking and profiling the test code. With performance tests, you can determine where your application’s memory or speed problems are coming from, and get a more in-depth picture of those problems.</p></div>
+<div class="paragraph"><p>In a freshly generated Rails application, <tt>test/performance/browsing_test.rb</tt> contains an example of a performance test:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-<pre><tt>Processing ItemsController<span style="font-style: italic"><span style="color: #9A1900">#index (for 127.0.0.1 at 2009-01-08 03:06:39) [GET]</span></span>
-Rendering template within layouts<span style="color: #990000">/</span>items
-Rendering items<span style="color: #990000">/</span>index
-Completed <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> 5ms <span style="color: #990000">(</span>View<span style="color: #990000">:</span> <span style="color: #993399">2</span><span style="color: #990000">,</span> DB<span style="color: #990000">:</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="color: #990000">|</span> <span style="color: #993399">200</span> OK <span style="color: #990000">[</span>http<span style="color: #990000">:</span><span style="color: #FF6600">//0.0.0.0/</span>items<span style="color: #990000">]</span></tt></pre></div></div>
-<div class="paragraph"><p>For this section, we’re only interested in the last line from that log entry:</p></div>
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'performance_test_help'</span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># Profiling results for each test method are written to tmp/performance.</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> BrowsingTest <span style="color: #990000"><</span> ActionController<span style="color: #990000">::</span>PerformanceTest
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
+ get <span style="color: #FF0000">'/'</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
+<div class="paragraph"><p>This example is a simple performance test case for profiling a GET request to the application’s homepage.</p></div>
+<h3 id="_generating_performance_tests">1.1. Generating performance tests</h3>
+<div class="paragraph"><p>Rails provides a generator called <tt>performance_test</tt> for creating new performance tests:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-<pre><tt>Completed <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> 5ms <span style="color: #990000">(</span>View<span style="color: #990000">:</span> <span style="color: #993399">2</span><span style="color: #990000">,</span> DB<span style="color: #990000">:</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="color: #990000">|</span> <span style="color: #993399">200</span> OK <span style="color: #990000">[</span>http<span style="color: #990000">:</span><span style="color: #FF6600">//0.0.0.0/</span>items<span style="color: #990000">]</span></tt></pre></div></div>
-<div class="paragraph"><p>This data is fairly straight forward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It’s safe to assume that the remaining 3 ms were spent inside the controller.</p></div>
-</div>
-<h2 id="_helper_methods">2. Helper methods</h2>
-<div class="sectionbody">
-<div class="paragraph"><p>Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a specific code. The method is called <tt>benchmark()</tt> in all three components.</p></div>
+<pre><tt>script/generate performance_test homepage</tt></pre></div></div>
+<div class="paragraph"><p>This generates <tt>homepage_test.rb</tt> in the <tt>test/performance</tt> directory:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-<pre><tt>Project<span style="color: #990000">.</span>benchmark<span style="color: #990000">(</span><span style="color: #FF0000">"Creating project"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
- project <span style="color: #990000">=</span> Project<span style="color: #990000">.</span>create<span style="color: #990000">(</span><span style="color: #FF0000">"name"</span> <span style="color: #990000">=></span> <span style="color: #FF0000">"stuff"</span><span style="color: #990000">)</span>
- project<span style="color: #990000">.</span>create_manager<span style="color: #990000">(</span><span style="color: #FF0000">"name"</span> <span style="color: #990000">=></span> <span style="color: #FF0000">"David"</span><span style="color: #990000">)</span>
- project<span style="color: #990000">.</span>milestones <span style="color: #990000"><<</span> Milestone<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'performance_test_help'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> HomepageTest <span style="color: #990000"><</span> ActionController<span style="color: #990000">::</span>PerformanceTest
+ <span style="font-style: italic"><span style="color: #9A1900"># Replace this with your real tests.</span></span>
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
+ get <span style="color: #FF0000">'/'</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
-<div class="paragraph"><p>The above code benchmarks the multiple statments enclosed inside <tt>Project.benchmark("Creating project") do..end</tt> block and prints the results inside log files. The statement inside log files will look like:</p></div>
+<h3 id="_examples">1.2. Examples</h3>
+<div class="paragraph"><p>Let’s assume your application has the following controller and model:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-<pre><tt>Creating projectem <span style="color: #990000">(</span><span style="color: #993399">185</span><span style="color: #990000">.</span>3ms<span style="color: #990000">)</span></tt></pre></div></div>
-<div class="paragraph"><p>Please refer to <a href="http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M001336">API docs</a> for optional options to <tt>benchmark()</tt></p></div>
-<div class="paragraph"><p>Similarly, you could use this helper method inside <a href="http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715">controllers</a> ( Note that it’s a class method here ):</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> process_projects
- <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">class</span></span><span style="color: #990000">.</span>benchmark<span style="color: #990000">(</span><span style="color: #FF0000">"Processing projects"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
- Project<span style="color: #990000">.</span>process<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>project_ids<span style="color: #990000">])</span>
- Project<span style="color: #990000">.</span>update_cached_projects
+<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># routes.rb</span></span>
+map<span style="color: #990000">.</span>root <span style="color: #990000">:</span>controller <span style="color: #990000">=></span> <span style="color: #FF0000">'home'</span>
+map<span style="color: #990000">.</span>resources <span style="color: #990000">:</span>posts
+
+<span style="font-style: italic"><span style="color: #9A1900"># home_controller.rb</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> HomeController <span style="color: #990000"><</span> ApplicationController
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> dashboard
+ <span style="color: #009900">@users</span> <span style="color: #990000">=</span> User<span style="color: #990000">.</span>last_ten<span style="color: #990000">(:</span><span style="font-weight: bold"><span style="color: #0000FF">include</span></span> <span style="color: #990000">=></span> <span style="color: #990000">:</span>avatars<span style="color: #990000">)</span>
+ <span style="color: #009900">@posts</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>all_today
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># posts_controller.rb</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostsController <span style="color: #990000"><</span> ApplicationController
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> create
+ <span style="color: #009900">@post</span> <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>create<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>post<span style="color: #990000">])</span>
+ redirect_to<span style="color: #990000">(</span><span style="color: #009900">@post</span><span style="color: #990000">)</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+<span style="font-style: italic"><span style="color: #9A1900"># post.rb</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Post <span style="color: #990000"><</span> ActiveRecord<span style="color: #990000">::</span>Base
+ before_save <span style="color: #990000">:</span>recalculate_costly_stats
+
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> slow_method
+ <span style="font-style: italic"><span style="color: #9A1900"># I fire gallzilion queries sleeping all around</span></span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+ private
+
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> recalculate_costly_stats
+ <span style="font-style: italic"><span style="color: #9A1900"># CPU heavy calculations</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
-<div class="paragraph"><p>and <a href="http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715">views</a>:</p></div>
+<h4 id="_controller_example">1.2.1. Controller Example</h4>
+<div class="paragraph"><p>Because performance tests are a special kind of integration test, you can use the <tt>get</tt> and <tt>post</tt> methods in them.</p></div>
+<div class="paragraph"><p>Here’s the performance test for <tt>HomeController#dashboard</tt> and <tt>PostsController#create</tt>:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="color: #FF0000"><% benchmark("Showing projects partial") do %></span>
- <span style="color: #FF0000"><%= render :partial =></span> <span style="color: #009900">@projects</span> <span style="color: #990000">%></span>
-<span style="color: #FF0000"><% end %></span></tt></pre></div></div>
-</div>
-<h2 id="_performance_test_cases">3. Performance Test Cases</h2>
-<div class="sectionbody">
-<div class="paragraph"><p>Rails provides a very easy way to write performance test cases, which look just like the regular integration tests. Performance tests run a code profiler on your test methods. Profiling output for combinations of each test method, measurement, and output format are written to your <tt>tmp/performance</tt> directory. By default, process_time is measured and both flat and graph_html output formats are written, so you’ll have two output files per test method.</p></div>
-<div class="paragraph"><p>If you have a look at <tt>test/performance/browsing_test.rb</tt> in a newly created Rails application:</p></div>
+<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'performance_test_help'</span>
+
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostPerformanceTest <span style="color: #990000"><</span> ActionController<span style="color: #990000">::</span>PerformanceTest
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> setup
+ <span style="font-style: italic"><span style="color: #9A1900"># Application requires logged-in user</span></span>
+ login_as<span style="color: #990000">(:</span>lifo<span style="color: #990000">)</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
+ get <span style="color: #FF0000">'/dashboard'</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_creating_new_post
+ post <span style="color: #FF0000">'/posts'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>post <span style="color: #990000">=></span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>body <span style="color: #990000">=></span> <span style="color: #FF0000">'lifo is fooling you'</span> <span style="color: #FF0000">}</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
+<div class="paragraph"><p>You can find more details about the <tt>get</tt> and <tt>post</tt> methods in the <a href="../testing_rails_applications.html#mgunderloy">Testing Rails Applications</a> guide.</p></div>
+<h4 id="_model_example">1.2.2. Model Example</h4>
+<div class="paragraph"><p>Even though the performance tests are integration tests and hence closer to the request/response cycle by nature, you can still performance test pure model code.</p></div>
+<div class="paragraph"><p>Performance test for <tt>Post</tt> model:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -343,54 +417,59 @@ http://www.gnu.org/software/src-highlite --> <pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'performance_test_help'</span>
-<span style="font-style: italic"><span style="color: #9A1900"># Profiling results for each test method are written to tmp/performance.</span></span>
-<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> BrowsingTest <span style="color: #990000"><</span> ActionController<span style="color: #990000">::</span>PerformanceTest
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
- get <span style="color: #FF0000">'/'</span>
+<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> PostModelTest <span style="color: #990000"><</span> ActionController<span style="color: #990000">::</span>PerformanceTest
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_creation
+ Post<span style="color: #990000">.</span>create <span style="color: #990000">:</span>body <span style="color: #990000">=></span> <span style="color: #FF0000">'still fooling you'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>cost <span style="color: #990000">=></span> <span style="color: #FF0000">'100'</span>
+ <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+
+ <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_slow_method
+ <span style="font-style: italic"><span style="color: #9A1900"># Using posts(:awesome) fixture</span></span>
+ posts<span style="color: #990000">(:</span>awesome<span style="color: #990000">).</span>slow_method
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
-<div class="paragraph"><p>This is an automatically generated example performance test file, for testing performance of homepage(<em>/</em>) of the application.</p></div>
-<h3 id="_modes">3.1. Modes</h3>
-<div class="paragraph"><p>Performance test cases can be run in two modes : Benchmarking and Profling.</p></div>
-<h4 id="_benchmarking">3.1.1. Benchmarking</h4>
-<div class="paragraph"><p>Benchmarking helps you find out how fast are your test cases. Each Test case is run <tt>4 times</tt> in this mode. To run performance tests in benchmarking mode:</p></div>
+<h3 id="_modes">1.3. Modes</h3>
+<div class="paragraph"><p>Performance tests can be run in two modes : Benchmarking and Profiling.</p></div>
+<h4 id="_benchmarking">1.3.1. Benchmarking</h4>
+<div class="paragraph"><p>Benchmarking helps find out how fast each performance test runs. Each test case is run <tt>4 times</tt> in benchmarking mode.</p></div>
+<div class="paragraph"><p>To run performance tests in benchmarking mode:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ rake <span style="font-weight: bold"><span style="color: #0000FF">test</span></span><span style="color: #990000">:</span>benchmark</tt></pre></div></div>
-<h4 id="_profiling">3.1.2. Profiling</h4>
-<div class="paragraph"><p>Profiling helps introspect into your test cases and figure out which are the slow parts. Each Test case is run <tt>1 time</tt> in this mode. To run performance tests in profiling mode:</p></div>
+<h4 id="_profiling">1.3.2. Profiling</h4>
+<div class="paragraph"><p>Profiling helps you see the details of a performance test and provide an in-depth picture of the slow and memory hungry parts. Each test case is run <tt>1 time</tt> in profiling mode.</p></div>
+<div class="paragraph"><p>To run performance tests in profiling mode:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt>$ rake <span style="font-weight: bold"><span style="color: #0000FF">test</span></span><span style="color: #990000">:</span>profile</tt></pre></div></div>
-<h3 id="_metrics">3.2. Metrics</h3>
-<div class="paragraph"><p>Benchmarking and profiling run performance test cases in various modes to help precisely figure out the where the problem lies.</p></div>
-<h4 id="_wall_time">3.2.1. Wall Time</h4>
-<div class="paragraph"><p>Measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system.</p></div>
+<h3 id="_metrics">1.4. Metrics</h3>
+<div class="paragraph"><p>Benchmarking and profiling run performance tests in various modes described below.</p></div>
+<h4 id="_wall_time">1.4.1. Wall Time</h4>
+<div class="paragraph"><p>Wall time measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system.</p></div>
<div class="paragraph"><p>Mode : Benchmarking</p></div>
-<h4 id="_process_time">3.2.2. Process Time</h4>
-<div class="paragraph"><p>Measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load.</p></div>
+<h4 id="_process_time">1.4.2. Process Time</h4>
+<div class="paragraph"><p>Process time measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load.</p></div>
<div class="paragraph"><p>Mode : Profiling</p></div>
-<h4 id="_memory">3.2.3. Memory</h4>
-<div class="paragraph"><p>Measures the amount of memory used for the performance test case.</p></div>
-<div class="paragraph"><p>Mode : Benchmarking, Profiling [Requires specially compiled Ruby]</p></div>
-<h4 id="_objects">3.2.4. Objects</h4>
-<div class="paragraph"><p>Measures the number of objects allocated for the performance test case.</p></div>
-<div class="paragraph"><p>Mode : Benchmarking, Profiling [Requires specially compiled Ruby]</p></div>
-<h4 id="_gc_runs">3.2.5. GC Runs</h4>
-<div class="paragraph"><p>Measures the number of times GC was invoked for the performance test case.</p></div>
-<div class="paragraph"><p>Mode : Benchmarking [Requires specially compiled Ruby]</p></div>
-<h4 id="_gc_time">3.2.6. GC Time</h4>
-<div class="paragraph"><p>Measures the amount of time spent in GC for the performance test case.</p></div>
-<div class="paragraph"><p>Mode : Benchmarking [Requires specially compiled Ruby]</p></div>
-<h3 id="_understanding_the_output">3.3. Understanding the output</h3>
-<div class="paragraph"><p>Performance tests generate different outputs inside <tt>tmp/performance</tt> directory based on the mode it is run in and the metric.</p></div>
-<h4 id="_benchmarking_2">3.3.1. Benchmarking</h4>
+<h4 id="_memory">1.4.3. Memory</h4>
+<div class="paragraph"><p>Memory measures the amount of memory used for the performance test case.</p></div>
+<div class="paragraph"><p>Mode : Benchmarking, Profiling [<a href="#gc">Requires GC-Patched Ruby</a>]</p></div>
+<h4 id="_objects">1.4.4. Objects</h4>
+<div class="paragraph"><p>Objects measures the number of objects allocated for the performance test case.</p></div>
+<div class="paragraph"><p>Mode : Benchmarking, Profiling [<a href="#gc">Requires GC-Patched Ruby</a>]</p></div>
+<h4 id="_gc_runs">1.4.5. GC Runs</h4>
+<div class="paragraph"><p>GC Runs measures the number of times GC was invoked for the performance test case.</p></div>
+<div class="paragraph"><p>Mode : Benchmarking [<a href="#gc">Requires GC-Patched Ruby</a>]</p></div>
+<h4 id="_gc_time">1.4.6. GC Time</h4>
+<div class="paragraph"><p>GC Time measures the amount of time spent in GC for the performance test case.</p></div>
+<div class="paragraph"><p>Mode : Benchmarking [<a href="#gc">Requires GC-Patched Ruby</a>]</p></div>
+<h3 id="_understanding_the_output">1.5. Understanding the output</h3>
+<div class="paragraph"><p>Performance tests generate different outputs inside <tt>tmp/performance</tt> directory depending on their mode and metric.</p></div>
+<h4 id="_benchmarking_2">1.5.1. Benchmarking</h4>
<div class="paragraph"><p>In benchmarking mode, performance tests generate two types of outputs :</p></div>
<h5 id="_command_line">Command line</h5>
<div class="paragraph"><p>This is the primary form of output in benchmarking mode. Example :</p></div>
@@ -406,7 +485,7 @@ http://www.gnu.org/software/src-highlite --> gc_runs<span style="color: #990000">:</span> <span style="color: #993399">0</span>
gc_time<span style="color: #990000">:</span> <span style="color: #993399">19</span> ms</tt></pre></div></div>
<h5 id="_csv_files">CSV files</h5>
-<div class="paragraph"><p>Performance tests results are also appended to <tt>.csv</tt> files inside <tt>tmp/performance/<Class>#<test>_<metric>.csv</tt> file. For example, running the default <tt>BrowsingTest#test_homepage</tt> will generate following five files :</p></div>
+<div class="paragraph"><p>Performance test results are also appended to <tt>.csv</tt> files inside <tt>tmp/performance</tt>. For example, running the default <tt>BrowsingTest#test_homepage</tt> will generate following five files :</p></div>
<div class="ulist"><ul>
<li>
<p>
@@ -434,8 +513,8 @@ BrowsingTest#test_homepage_wall_time.csv </p>
</li>
</ul></div>
-<div class="paragraph"><p>As the results are appended to these files each time the performance tests are run in benchmarking mode, it enables you gather data over a sustainable period of time which can be very helpful with various performance analysis.</p></div>
-<div class="paragraph"><p>Sample output of +BrowsingTest#test_homepage_wall_time.csv + :</p></div>
+<div class="paragraph"><p>As the results are appended to these files each time the performance tests are run in benchmarking mode, you can collect data over a period of time. This can be very helpful in analyzing the effects of code changes.</p></div>
+<div class="paragraph"><p>Sample output of <tt>BrowsingTest#test_homepage_wall_time.csv</tt>:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -452,9 +531,10 @@ http://www.gnu.org/software/src-highlite --> <span style="color: #993399">0.00740450000000004</span><span style="color: #990000">,</span><span style="color: #993399">2009</span>-<span style="color: #993399">01</span>-09T03<span style="color: #990000">:</span><span style="color: #993399">54</span><span style="color: #990000">:</span>47Z<span style="color: #990000">,,</span><span style="color: #993399">2.3</span><span style="color: #990000">.</span><span style="color: #993399">0</span><span style="color: #990000">.</span>master<span style="color: #990000">.</span><span style="color: #993399">859e150</span><span style="color: #990000">,</span>ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6.110</span><span style="color: #990000">,</span>i686-darwin<span style="color: #993399">9.0</span><span style="color: #990000">.</span><span style="color: #993399">0</span>
<span style="color: #993399">0.00603150000000008</span><span style="color: #990000">,</span><span style="color: #993399">2009</span>-<span style="color: #993399">01</span>-09T03<span style="color: #990000">:</span><span style="color: #993399">54</span><span style="color: #990000">:</span>57Z<span style="color: #990000">,,</span><span style="color: #993399">2.3</span><span style="color: #990000">.</span><span style="color: #993399">0</span><span style="color: #990000">.</span>master<span style="color: #990000">.</span><span style="color: #993399">859e150</span><span style="color: #990000">,</span>ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6.111</span><span style="color: #990000">,</span>i686-darwin<span style="color: #993399">9.1</span><span style="color: #990000">.</span><span style="color: #993399">0</span>
<span style="color: #993399">0.00771250000000012</span><span style="color: #990000">,</span><span style="color: #993399">2009</span>-<span style="color: #993399">01</span>-09T15<span style="color: #990000">:</span><span style="color: #993399">46</span><span style="color: #990000">:</span>03Z<span style="color: #990000">,,</span><span style="color: #993399">2.3</span><span style="color: #990000">.</span><span style="color: #993399">0</span><span style="color: #990000">.</span>master<span style="color: #990000">.</span><span style="color: #993399">859e150</span><span style="color: #990000">,</span>ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6.110</span><span style="color: #990000">,</span>i686-darwin<span style="color: #993399">9.0</span><span style="color: #990000">.</span><span style="color: #993399">0</span></tt></pre></div></div>
-<h4 id="_profiling_2">3.3.2. Profiling</h4>
+<h4 id="_profiling_2">1.5.2. Profiling</h4>
+<div class="paragraph"><p>In profiling mode, you can choose from four types of output.</p></div>
<h5 id="_command_line_2">Command line</h5>
-<div class="paragraph"><p>This is the very basic form of output in profiling mode. Example :</p></div>
+<div class="paragraph"><p>This is a very basic form of output in profiling mode:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -465,104 +545,283 @@ http://www.gnu.org/software/src-highlite --> memory<span style="color: #990000">:</span> <span style="color: #993399">832.13</span> KB
objects<span style="color: #990000">:</span> <span style="color: #993399">7882</span></tt></pre></div></div>
<h5 id="_flat">Flat</h5>
-<div class="paragraph"><p>Flat output shows the total amount of time spent in each method. <a href="http://ruby-prof.rubyforge.org/files/examples/flat_txt.html">Check ruby prof documentation for a better explaination</a>.</p></div>
+<div class="paragraph"><p>Flat output shows the total amount of time spent in each method. <a href="http://ruby-prof.rubyforge.org/files/examples/flat_txt.html">Check ruby prof documentation for a better explanation</a>.</p></div>
<h5 id="_graph">Graph</h5>
-<div class="paragraph"><p>Graph output shows how long each method takes to run, which methods call it and which methods it calls. <a href="http://ruby-prof.rubyforge.org/files/examples/graph_txt.html">Check ruby prof documentation for a better explaination</a>.</p></div>
+<div class="paragraph"><p>Graph output shows how long each method takes to run, which methods call it and which methods it calls. <a href="http://ruby-prof.rubyforge.org/files/examples/graph_txt.html">Check ruby prof documentation for a better explanation</a>.</p></div>
<h5 id="_tree">Tree</h5>
-<div class="paragraph"><p>Tree output is profiling information in calltree format for use by kcachegrind and similar tools.</p></div>
-<h3 id="_preparing_ruby_and_ruby_prof">3.4. Preparing Ruby and Ruby-prof</h3>
-<div class="paragraph"><p>Before we go ahead, Rails performance testing requires you to build a special Ruby binary with some super powers - GC patch for measuring GC Runs/Time. This process is very straight forward. If you’ve never compiled a Ruby binary before, you can follow the following steps to build a ruby binary inside your home directory:</p></div>
-<h4 id="_compile">3.4.1. Compile</h4>
+<div class="paragraph"><p>Tree output is profiling information in calltree format for use by <a href="http://kcachegrind.sourceforge.net/html/Home.html">kcachegrind</a> and similar tools.</p></div>
+<h3 id="_tuning_test_runs">1.6. Tuning Test Runs</h3>
+<div class="paragraph"><p>By default, each performance test is run <tt>4 times</tt> in benchmarking mode and <tt>1 time</tt> in profiling. However, test runs can easily be configured.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/caution.png" alt="Caution" />
+</td>
+<td class="content">Performance test configurability is not yet enabled in Rails. But it will be soon.</td>
+</tr></table>
+</div>
+<h3 id="gc">1.7. Installing GC-Patched Ruby</h3>
+<div class="paragraph"><p>To get the best from Rails performance tests, you need to build a special Ruby binary with some super powers - <a href="http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch">GC patch</a> for measuring GC Runs/Time and memory/object allocation.</p></div>
+<div class="paragraph"><p>The process is fairly straight forward. If you’ve never compiled a Ruby binary before, follow these steps to build a ruby binary inside your home directory:</p></div>
+<h4 id="_installation">1.7.1. Installation</h4>
+<div class="paragraph"><p>Compile Ruby and apply this <a href="http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch">GC Patch</a>:</p></div>
+<h4 id="_download_and_extract">1.7.2. Download and Extract</h4>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="color: #990000">[</span>lifo@null <span style="color: #990000">~]</span>$ mkdir rubygc
-<span style="color: #990000">[</span>lifo@null <span style="color: #990000">~]</span>$ wget ftp<span style="color: #990000">:</span>//ftp<span style="color: #990000">.</span>ruby-lang<span style="color: #990000">.</span>org/pub/ruby<span style="color: #990000">/</span><span style="color: #993399">1.8</span>/ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">.</span>tar<span style="color: #990000">.</span>gz
-<span style="color: #990000">[</span>lifo@null <span style="color: #990000">~]</span>$ tar -xzvf ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">.</span>tar<span style="color: #990000">.</span>gz
-<span style="color: #990000">[</span>lifo@null <span style="color: #990000">~]</span>$ cd ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span>
-<span style="color: #990000">[</span>lifo@null ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">]</span>$ curl http<span style="color: #990000">:</span>//rubyforge<span style="color: #990000">.</span>org/tracker/download<span style="color: #990000">.</span>php<span style="color: #990000">/</span><span style="color: #993399">1814</span><span style="color: #990000">/</span><span style="color: #993399">7062</span><span style="color: #990000">/</span><span style="color: #993399">17676</span><span style="color: #990000">/</span><span style="color: #993399">3291</span>/ruby186gc<span style="color: #990000">.</span>patch <span style="color: #990000">|</span> patch -p<span style="color: #993399">0</span>
-<span style="color: #990000">[</span>lifo@null ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">]</span>$ <span style="color: #990000">.</span>/configure --prefix<span style="color: #990000">=</span>/Users/lifo/rubygc
-<span style="color: #990000">[</span>lifo@null ruby-<span style="color: #993399">1.8</span><span style="color: #990000">.</span><span style="color: #993399">6</span>-p<span style="color: #993399">111</span><span style="color: #990000">]</span>$ make <span style="color: #990000">&&</span> make install</tt></pre></div></div>
-<h4 id="_prepare_aliases">3.4.2. Prepare aliases</h4>
-<div class="paragraph"><p>Add the following lines in your ~/.profile for convenience:</p></div>
+<span style="color: #990000">[</span>lifo@null <span style="color: #990000">~]</span>$ wget <span style="color: #990000"><</span>download the latest stable ruby from ftp<span style="color: #990000">:</span>//ftp<span style="color: #990000">.</span>ruby-lang<span style="color: #990000">.</span>org/pub/ruby<span style="color: #990000">></span>
+<span style="color: #990000">[</span>lifo@null <span style="color: #990000">~]</span>$ tar -xzvf <span style="color: #990000"><</span>ruby-version<span style="color: #990000">.</span>tar<span style="color: #990000">.</span>gz<span style="color: #990000">></span>
+<span style="color: #990000">[</span>lifo@null <span style="color: #990000">~]</span>$ cd <span style="color: #990000"><</span>ruby-version<span style="color: #990000">></span></tt></pre></div></div>
+<h4 id="_apply_the_patch">1.7.3. Apply the patch</h4>
<div class="listingblock">
-<div class="content">
-<pre><tt>alias gcruby='/Users/lifo/rubygc/bin/ruby'
-alias gcrake='/Users/lifo/rubygc/bin/rake'
-alias gcgem='/Users/lifo/rubygc/bin/gem'
-alias gcirb='/Users/lifo/rubygc/bin/irb'
-alias gcrails='/Users/lifo/rubygc/bin/rails'</tt></pre>
-</div></div>
-<h4 id="_install_rubygems_and_some_basic_gems">3.4.3. Install rubygems and some basic gems</h4>
-<div class="paragraph"><p>Download <a href="http://rubyforge.org/projects/rubygems">Rubygems</a> and install it from source. Afterwards, install rake. rails, ruby-prof and rack gems:</p></div>
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">[</span>lifo@null ruby-version<span style="color: #990000">]</span>$ curl http<span style="color: #990000">:</span>//rubyforge<span style="color: #990000">.</span>org/tracker/download<span style="color: #990000">.</span>php<span style="color: #990000">/</span><span style="color: #993399">1814</span><span style="color: #990000">/</span><span style="color: #993399">7062</span><span style="color: #990000">/</span><span style="color: #993399">17676</span><span style="color: #990000">/</span><span style="color: #993399">3291</span>/ruby186gc<span style="color: #990000">.</span>patch <span style="color: #990000">|</span> patch -p<span style="color: #993399">0</span></tt></pre></div></div>
+<h4 id="_configure_and_install">1.7.4. Configure and Install</h4>
+<div class="paragraph"><p>The following will install ruby in your home directory’s <tt>/rubygc</tt> directory. Make sure to replace <tt><homedir></tt> with a full patch to your actual home directory.</p></div>
<div class="listingblock">
-<div class="content">
-<pre><tt>[lifo@null ~]$ gcgem install rake
-[lifo@null ~]$ gcgem install rails
-[lifo@null ~]$ gcgem install ruby-prof
-[lifo@null ~]$ gcgem install rack</tt></pre>
-</div></div>
-<h4 id="_install_mysql_gem">3.4.4. Install MySQL gem</h4>
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">[</span>lifo@null ruby-version<span style="color: #990000">]</span>$ <span style="color: #990000">.</span>/configure --prefix<span style="color: #990000">=/<</span>homedir<span style="color: #990000">></span>/rubygc
+<span style="color: #990000">[</span>lifo@null ruby-version<span style="color: #990000">]</span>$ make <span style="color: #990000">&&</span> make install</tt></pre></div></div>
+<h4 id="_prepare_aliases">1.7.5. Prepare aliases</h4>
+<div class="paragraph"><p>For convenience, add the following lines in your <tt>~/.profile</tt>:</p></div>
<div class="listingblock">
<div class="content">
-<pre><tt>[lifo@null ~]$ gcgem install mysql</tt></pre>
+<pre><tt>alias gcruby='~/rubygc/bin/ruby'
+alias gcrake='~/rubygc/bin/rake'
+alias gcgem='~/rubygc/bin/gem'
+alias gcirb='~/rubygc/bin/irb'
+alias gcrails='~/rubygc/bin/rails'</tt></pre>
</div></div>
-<div class="paragraph"><p>If this fails, you can try to install it manually:</p></div>
+<h4 id="_install_rubygems_and_dependency_gems">1.7.6. Install rubygems and dependency gems</h4>
+<div class="paragraph"><p>Download <a href="http://rubyforge.org/projects/rubygems">Rubygems</a> and install it from source. Rubygem’s README file should have necessary installation instructions.</p></div>
+<div class="paragraph"><p>Additionally, install the following gems :</p></div>
+<div class="ulist"><ul>
+<li>
+<p>
+<tt>rake</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>rails</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>ruby-prof</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>rack</tt>
+</p>
+</li>
+<li>
+<p>
+<tt>mysql</tt>
+</p>
+</li>
+</ul></div>
+<div class="paragraph"><p>If installing <tt>mysql</tt> fails, you can try to install it manually:</p></div>
<div class="listingblock">
-<div class="content">
-<pre><tt>[lifo@null ~]$ cd /Users/lifo/rubygc/lib/ruby/gems/1.8/gems/mysql-2.7/
-[lifo@null mysql-2.7]$ gcruby extconf.rb --with-mysql-config
-[lifo@null mysql-2.7]$ make && make install</tt></pre>
-</div></div>
-<h3 id="_generating_performance_test">3.5. Generating performance test</h3>
-<div class="paragraph"><p>Rails provides a generator for creating new performance tests:</p></div>
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #990000">[</span>lifo@null mysql<span style="color: #990000">]</span>$ gcruby extconf<span style="color: #990000">.</span>rb --with-mysql-config
+<span style="color: #990000">[</span>lifo@null mysql<span style="color: #990000">]</span>$ make <span style="color: #990000">&&</span> make install</tt></pre></div></div>
+<div class="paragraph"><p>And you’re ready to go. Don’t forget to use <tt>gcruby</tt> and <tt>gcrake</tt> aliases when running the performance tests.</p></div>
+</div>
+<h2 id="_command_line_tools">2. Command Line Tools</h2>
+<div class="sectionbody">
+<div class="paragraph"><p>Writing performance test cases could be an overkill when you are looking for one time tests. Rails ships with two command line tools that enable quick and dirty performance testing:</p></div>
+<h3 id="_benchmarker">2.1. benchmarker</h3>
+<div class="paragraph"><p><tt>benchmarker</tt> is a wrapper around Ruby’s <a href="http://ruby-doc.org/core/classes/Benchmark.html">Benchmark</a> module.</p></div>
+<div class="paragraph"><p>Usage:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="color: #990000">[</span>lifo@null application <span style="color: #990000">(</span>master<span style="color: #990000">)]</span>$ script/generate performance_test homepage</tt></pre></div></div>
-<div class="paragraph"><p>This will generate <tt>test/performance/homepage_test.rb</tt>:</p></div>
+<pre><tt>$ script/performance/benchmarker <span style="color: #990000">[</span><span style="font-weight: bold"><span style="color: #0000FF">times</span></span><span style="color: #990000">]</span> <span style="color: #FF0000">'Person.expensive_way'</span> <span style="color: #FF0000">'Person.another_expensive_way'</span> <span style="color: #990000">...</span></tt></pre></div></div>
+<div class="paragraph"><p>Examples:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
-<span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'performance_test_help'</span>
-
-<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> HomepageTest <span style="color: #990000"><</span> ActionController<span style="color: #990000">::</span>PerformanceTest
- <span style="font-style: italic"><span style="color: #9A1900"># Replace this with your real tests.</span></span>
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_homepage
- get <span style="color: #FF0000">'/'</span>
+<pre><tt>$ script/performance/benchmarker <span style="color: #993399">10</span> <span style="color: #FF0000">'Item.all'</span> <span style="color: #FF0000">'CouchItem.all'</span></tt></pre></div></div>
+<div class="paragraph"><p>If the <tt>[times]</tt> argument is omitted, supplied methods are run just once:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/performance/benchmarker <span style="color: #FF0000">'Item.first'</span> <span style="color: #FF0000">'Item.last'</span></tt></pre></div></div>
+<h3 id="_profiler">2.2. profiler</h3>
+<div class="paragraph"><p><tt>profiler</tt> is a wrapper around <a href="http://ruby-prof.rubyforge.org/">ruby-prof</a> gem.</p></div>
+<div class="paragraph"><p>Usage:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/performance/profiler <span style="color: #FF0000">'Person.expensive_method(10)'</span> <span style="color: #990000">[</span><span style="font-weight: bold"><span style="color: #0000FF">times</span></span><span style="color: #990000">]</span> <span style="color: #990000">[</span>flat<span style="color: #990000">|</span>graph<span style="color: #990000">|</span>graph_html<span style="color: #990000">]</span></tt></pre></div></div>
+<div class="paragraph"><p>Examples:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/performance/profiler <span style="color: #FF0000">'Item.all'</span></tt></pre></div></div>
+<div class="paragraph"><p>This will profile <tt>Item.all</tt> in <tt>RubyProf::WALL_TIME</tt> measure mode. By default, it prints flat output to the shell.</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/performance/profiler <span style="color: #FF0000">'Item.all'</span> <span style="color: #993399">10</span> graph</tt></pre></div></div>
+<div class="paragraph"><p>This will profile <tt>10.times { Item.all }</tt> with <tt>RubyProf::WALL_TIME</tt> measure mode and print graph output to the shell.</p></div>
+<div class="paragraph"><p>If you want to store the output in a file:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>$ script/performance/profiler <span style="color: #FF0000">'Item.all'</span> <span style="color: #993399">10</span> graph <span style="color: #993399">2</span><span style="color: #990000">></span> graph<span style="color: #990000">.</span>txt</tt></pre></div></div>
+</div>
+<h2 id="_helper_methods">3. Helper methods</h2>
+<div class="sectionbody">
+<div class="paragraph"><p>Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a given piece of code. The method is called <tt>benchmark()</tt> in all the three components.</p></div>
+<h3 id="_model">3.1. Model</h3>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Project<span style="color: #990000">.</span>benchmark<span style="color: #990000">(</span><span style="color: #FF0000">"Creating project"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+ project <span style="color: #990000">=</span> Project<span style="color: #990000">.</span>create<span style="color: #990000">(</span><span style="color: #FF0000">"name"</span> <span style="color: #990000">=></span> <span style="color: #FF0000">"stuff"</span><span style="color: #990000">)</span>
+ project<span style="color: #990000">.</span>create_manager<span style="color: #990000">(</span><span style="color: #FF0000">"name"</span> <span style="color: #990000">=></span> <span style="color: #FF0000">"David"</span><span style="color: #990000">)</span>
+ project<span style="color: #990000">.</span>milestones <span style="color: #990000"><<</span> Milestone<span style="color: #990000">.</span>find<span style="color: #990000">(:</span>all<span style="color: #990000">)</span>
+<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
+<div class="paragraph"><p>This benchmarks the code enclosed in the <tt>Project.benchmark("Creating project") do..end</tt> block and prints the result to the log file:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Creating project <span style="color: #990000">(</span><span style="color: #993399">185</span><span style="color: #990000">.</span>3ms<span style="color: #990000">)</span></tt></pre></div></div>
+<div class="paragraph"><p>Please refer to the <a href="http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M001336">API docs</a> for additional options to <tt>benchmark()</tt></p></div>
+<h3 id="_controller">3.2. Controller</h3>
+<div class="paragraph"><p>Similarly, you could use this helper method inside <a href="http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715">controllers</a></p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="./images/icons/note.png" alt="Note" />
+</td>
+<td class="content"><tt>benchmark</tt> is a class method inside controllers</td>
+</tr></table>
+</div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> process_projects
+ <span style="font-weight: bold"><span style="color: #0000FF">self</span></span><span style="color: #990000">.</span><span style="font-weight: bold"><span style="color: #0000FF">class</span></span><span style="color: #990000">.</span>benchmark<span style="color: #990000">(</span><span style="color: #FF0000">"Processing projects"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
+ Project<span style="color: #990000">.</span>process<span style="color: #990000">(</span>params<span style="color: #990000">[:</span>project_ids<span style="color: #990000">])</span>
+ Project<span style="color: #990000">.</span>update_cached_projects
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span></tt></pre></div></div>
-<div class="paragraph"><p>Which you can modify to suit your needs.</p></div>
+<h3 id="_view">3.3. View</h3>
+<div class="paragraph"><p>And in <a href="http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715">views</a>:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt><span style="color: #FF0000"><% benchmark("Showing projects partial") do %></span>
+ <span style="color: #FF0000"><%= render :partial =></span> <span style="color: #009900">@projects</span> <span style="color: #990000">%></span>
+<span style="color: #FF0000"><% end %></span></tt></pre></div></div>
</div>
-<h2 id="_other_profiling_tools">4. Other Profiling Tools</h2>
+<h2 id="_request_logging">4. Request Logging</h2>
<div class="sectionbody">
+<div class="paragraph"><p>Rails log files contain very useful information about the time taken to serve each request. Here’s a typical log file entry:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Processing ItemsController<span style="font-style: italic"><span style="color: #9A1900">#index (for 127.0.0.1 at 2009-01-08 03:06:39) [GET]</span></span>
+Rendering template within layouts<span style="color: #990000">/</span>items
+Rendering items<span style="color: #990000">/</span>index
+Completed <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> 5ms <span style="color: #990000">(</span>View<span style="color: #990000">:</span> <span style="color: #993399">2</span><span style="color: #990000">,</span> DB<span style="color: #990000">:</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="color: #990000">|</span> <span style="color: #993399">200</span> OK <span style="color: #990000">[</span>http<span style="color: #990000">:</span><span style="color: #FF6600">//0.0.0.0/</span>items<span style="color: #990000">]</span></tt></pre></div></div>
+<div class="paragraph"><p>For this section, we’re only interested in the last line:</p></div>
+<div class="listingblock">
+<div class="content"><!-- Generator: GNU source-highlight 2.9
+by Lorenzo Bettini
+http://www.lorenzobettini.it
+http://www.gnu.org/software/src-highlite -->
+<pre><tt>Completed <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> 5ms <span style="color: #990000">(</span>View<span style="color: #990000">:</span> <span style="color: #993399">2</span><span style="color: #990000">,</span> DB<span style="color: #990000">:</span> <span style="color: #993399">0</span><span style="color: #990000">)</span> <span style="color: #990000">|</span> <span style="color: #993399">200</span> OK <span style="color: #990000">[</span>http<span style="color: #990000">:</span><span style="color: #FF6600">//0.0.0.0/</span>items<span style="color: #990000">]</span></tt></pre></div></div>
+<div class="paragraph"><p>This data is fairly straightforward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It’s safe to assume that the remaining 3 ms were spent inside the controller.</p></div>
+<div class="paragraph"><p>Michael Koziarski has an <a href="http://www.therailsway.com/2009/1/6/requests-per-second">interesting blog post</a> explaining the importance of using milliseconds as the metric.</p></div>
+</div>
+<h2 id="_useful_profiling_tools">5. Useful Profiling Tools</h2>
+<div class="sectionbody">
+<h3 id="_rails_plugins_and_gems">5.1. Rails Plugins and Gems</h3>
<div class="ulist"><ul>
<li>
<p>
-<a href="http://www.hpl.hp.com/research/linux/httperf/">httperf</a>
+<a href="http://rails-analyzer.rubyforge.org/">Rails Analyzer</a>
</p>
</li>
<li>
<p>
-<a href="http://rails-analyzer.rubyforge.org/">Rails Analyzer</a>
+<a href="http://www.flyingmachinestudios.com/projects/">Palmist</a>
</p>
</li>
<li>
<p>
-<a href="http://www.flyingmachinestudios.com/projects/">Palmist</a>
+<a href="http://github.com/josevalim/rails-footnotes/tree/master">Rails Footnotes</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://github.com/dsboulder/query_reviewer/tree/master">Query Reviewer</a>
+</p>
+</li>
+</ul></div>
+<h3 id="_external">5.2. External</h3>
+<div class="ulist"><ul>
+<li>
+<p>
+<a href="http://www.hpl.hp.com/research/linux/httperf">httperf</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://httpd.apache.org/docs/2.2/programs/ab.html">ab</a>
+</p>
+</li>
+<li>
+<p>
+<a href="http://jakarta.apache.org/jmeter">JMeter</a>
</p>
</li>
</ul></div>
</div>
-<h2 id="_commercial_products_dedicated_to_rails_perfomance">5. Commercial products dedicated to Rails Perfomance</h2>
+<h2 id="_commercial_products">6. Commercial Products</h2>
<div class="sectionbody">
+<div class="paragraph"><p>Rails has been lucky to have three startups dedicated to Rails specific performance tools:</p></div>
<div class="ulist"><ul>
<li>
<p>
@@ -581,13 +840,13 @@ http://www.gnu.org/software/src-highlite --> </li>
</ul></div>
</div>
-<h2 id="_changelog">6. Changelog</h2>
+<h2 id="_changelog">7. Changelog</h2>
<div class="sectionbody">
<div class="paragraph"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4">Lighthouse ticket</a></p></div>
<div class="ulist"><ul>
<li>
<p>
-January 9, 2009: Rewrite by Pratik
+January 9, 2009: Complete rewrite by Pratik
</p>
</li>
<li>
diff --git a/railties/doc/guides/source/activerecord_validations_callbacks.txt b/railties/doc/guides/source/activerecord_validations_callbacks.txt index 7d37df1ed2..29bff0c7b3 100644 --- a/railties/doc/guides/source/activerecord_validations_callbacks.txt +++ b/railties/doc/guides/source/activerecord_validations_callbacks.txt @@ -359,7 +359,7 @@ Sometimes it will make sense to validate an object just when a given predicate i === Using a symbol with the +:if+ and +:unless+ options -You can associated the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option. +You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option. [source, ruby] ------------------------------------------------------------------ @@ -620,29 +620,7 @@ Callbacks are methods that get called at certain moments of an object's lifecycl === Callbacks registration -In order to use the available callbacks, you need to registrate them. There are two ways of doing that. - -=== Registering callbacks by overriding the callback methods - -You can specify the callback method directly, by overriding it. Let's see how it works using the +before_validation+ callback, which will surprisingly run right before any validation is done. - -[source, ruby] ------------------------------------------------------------------- -class User < ActiveRecord::Base - validates_presence_of :login, :email - - protected - def before_validation - if self.login.nil? - self.login = email unless email.blank? - end - end -end ------------------------------------------------------------------- - -=== Registering callbacks by using macro-style class methods - -The other way you can register a callback method is by implementing it as an ordinary method, and then using a macro-style class method to register it as a callback. The last example could be written like that: +In order to use the available callbacks, you need to registrate them. You can do that by implementing them as an ordinary methods, and then using a macro-style class method to register then as callbacks. [source, ruby] ------------------------------------------------------------------ @@ -671,12 +649,57 @@ class User < ActiveRecord::Base end ------------------------------------------------------------------ -In Rails, the preferred way of registering callbacks is by using macro-style class methods. The main advantages of using macro-style class methods are: +CAUTION: Remember to always declare the callback methods as being protected or private. These methods should never be public, otherwise it will be possible to call them from code outside the model, violating object encapsulation and exposing implementation details. -* You can add more than one method for each type of callback. Those methods will be queued for execution at the same order they were registered. -* Readability, since your callback declarations will live at the beggining of your models' files. +== Conditional callbacks -CAUTION: Remember to always declare the callback methods as being protected or private. These methods should never be public, otherwise it will be possible to call them from code outside the model, violating object encapsulation and exposing implementation details. +Like in validations, we can also make our callbacks conditional, calling then only when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a Ruby Proc. You may use the +:if+ option when you want to specify when the callback *should* get called. If you want to specify when the callback *should not* be called, then you may use the +:unless+ option. + +=== Using a symbol with the +:if+ and +:unless+ options + +You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. If this method returns +false+ the callback won't be executed. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check the if the callback should be executed. + +[source, ruby] +------------------------------------------------------------------ +class Order < ActiveRecord::Base + before_save :normalize_card_number, :if => :paid_with_card? +end +------------------------------------------------------------------ + +=== Using a string with the +:if+ and +:unless+ options + +You can also use a string that will be evaluated using +:eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition. + +[source, ruby] +------------------------------------------------------------------ +class Order < ActiveRecord::Base + before_save :normalize_card_number, :if => "paid_with_card?" +end +------------------------------------------------------------------ + +=== Using a Proc object with the +:if+ and :+unless+ options + +Finally, it's possible to associate +:if+ and +:unless+ with a Ruby Proc object. This option is best suited when writing short validation methods, usually one-liners. + +[source, ruby] +------------------------------------------------------------------ +class Order < ActiveRecord::Base + before_save :normalize_card_number, + :if => Proc.new { |order| order.paid_with_card? } +end +------------------------------------------------------------------ + +=== Multiple Conditions for Callbacks + +When writing conditional callbacks, it's possible to mix both +:if+ and +:unless+ in the same callback declaration. + +[source, ruby] +------------------------------------------------------------------ +class Comment < ActiveRecord::Base + after_create :send_email_to_author, :if => :author_wants_emails?, + :unless => Proc.new { |comment| comment.post.ignore_comments? } +end +------------------------------------------------------------------ == Available callbacks diff --git a/railties/doc/guides/source/index.txt b/railties/doc/guides/source/index.txt index 130beedf08..61be44c63e 100644 --- a/railties/doc/guides/source/index.txt +++ b/railties/doc/guides/source/index.txt @@ -113,7 +113,7 @@ ways of achieving this and how to understand what is happening "behind the scene of your code. *********************************************************** -.link:performance_testing.html[Performance testing Rails Applications] +.link:performance_testing.html[Performance Testing Rails Applications] *********************************************************** CAUTION: link:http://rails.lighthouseapp.com/projects/16213/tickets/4[Lighthouse Ticket] diff --git a/railties/doc/guides/source/performance_testing.txt b/railties/doc/guides/source/performance_testing.txt index b741ddfd00..84a42cecde 100644 --- a/railties/doc/guides/source/performance_testing.txt +++ b/railties/doc/guides/source/performance_testing.txt @@ -1,108 +1,164 @@ -Performance testing Rails Applications +Performance Testing Rails Applications ====================================== -This guide covers the benchmarking and profiling tactics/tools of Rails and Ruby in general. By referring to this guide, you will be able to: +This guide covers the various ways of performance testing a Ruby on Rails application. By referring to this guide, you will be able to: * Understand the various types of benchmarking and profiling metrics -* Generate performance/benchmarking tests -* Use GC patched Ruby binary to measure memory usage and object allocation -* Understand the information provided by Rails inside the log files +* Generate performance and benchmarking tests +* Use a GC-patched Ruby binary to measure memory usage and object allocation +* Understand the benchmarking information provided by Rails inside the log files * Learn about various tools facilitating benchmarking and profiling -Performance testing is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page is completely loaded. Ensuring a plesant browsing experience to the end users and cutting cost of unnecessary hardwares is important for any web application. +Performance testing is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page is completely loaded. Ensuring a pleasant browsing experience for end users and cutting the cost of unnecessary hardware is important for any non-trivial web application. -== Using and understanding the log files == - -Rails logs files containt basic but very useful information about the time taken to serve every request. A typical log entry looks something like : +== Performance Test Cases == -[source, ruby] ----------------------------------------------------------------------------- -Processing ItemsController#index (for 127.0.0.1 at 2009-01-08 03:06:39) [GET] -Rendering template within layouts/items -Rendering items/index -Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items] ----------------------------------------------------------------------------- +Rails performance tests are a special type of integration tests, designed for benchmarking and profiling the test code. With performance tests, you can determine where your application's memory or speed problems are coming from, and get a more in-depth picture of those problems. -For this section, we're only interested in the last line from that log entry: +In a freshly generated Rails application, +test/performance/browsing_test.rb+ contains an example of a performance test: [source, ruby] ---------------------------------------------------------------------------- -Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items] +require 'test_helper' +require 'performance_test_help' + +# Profiling results for each test method are written to tmp/performance. +class BrowsingTest < ActionController::PerformanceTest + def test_homepage + get '/' + end +end ---------------------------------------------------------------------------- -This data is fairly straight forward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller. +This example is a simple performance test case for profiling a GET request to the application's homepage. -== Helper methods == +=== Generating performance tests === -Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a specific code. The method is called +benchmark()+ in all three components. +Rails provides a generator called +performance_test+ for creating new performance tests: -[source, ruby] +[source, shell] ---------------------------------------------------------------------------- -Project.benchmark("Creating project") do - project = Project.create("name" => "stuff") - project.create_manager("name" => "David") - project.milestones << Milestone.find(:all) -end +script/generate performance_test homepage ---------------------------------------------------------------------------- -The above code benchmarks the multiple statments enclosed inside +Project.benchmark("Creating project") do..end+ block and prints the results inside log files. The statement inside log files will look like: +This generates +homepage_test.rb+ in the +test/performance+ directory: [source, ruby] ---------------------------------------------------------------------------- -Creating projectem (185.3ms) +require 'test_helper' +require 'performance_test_help' + +class HomepageTest < ActionController::PerformanceTest + # Replace this with your real tests. + def test_homepage + get '/' + end +end ---------------------------------------------------------------------------- -Please refer to http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M001336[API docs] for optional options to +benchmark()+ +=== Examples === -Similarly, you could use this helper method inside http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[controllers] ( Note that it's a class method here ): +Let's assume your application has the following controller and model: [source, ruby] ---------------------------------------------------------------------------- -def process_projects - self.class.benchmark("Processing projects") do - Project.process(params[:project_ids]) - Project.update_cached_projects +# routes.rb +map.root :controller => 'home' +map.resources :posts + +# home_controller.rb +class HomeController < ApplicationController + def dashboard + @users = User.last_ten(:include => :avatars) + @posts = Post.all_today + end +end + +# posts_controller.rb +class PostsController < ApplicationController + def create + @post = Post.create(params[:post]) + redirect_to(@post) + end +end + +# post.rb +class Post < ActiveRecord::Base + before_save :recalculate_costly_stats + + def slow_method + # I fire gallzilion queries sleeping all around + end + + private + + def recalculate_costly_stats + # CPU heavy calculations end end ---------------------------------------------------------------------------- -and http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[views]: +==== Controller Example ==== + +Because performance tests are a special kind of integration test, you can use the +get+ and +post+ methods in them. + +Here's the performance test for +HomeController#dashboard+ and +PostsController#create+: [source, ruby] ---------------------------------------------------------------------------- -<% benchmark("Showing projects partial") do %> - <%= render :partial => @projects %> -<% end %> +require 'test_helper' +require 'performance_test_help' + +class PostPerformanceTest < ActionController::PerformanceTest + def setup + # Application requires logged-in user + login_as(:lifo) + end + + def test_homepage + get '/dashboard' + end + + def test_creating_new_post + post '/posts', :post => { :body => 'lifo is fooling you' } + end +end ---------------------------------------------------------------------------- -== Performance Test Cases == +You can find more details about the +get+ and +post+ methods in the link:../testing_rails_applications.html#mgunderloy[Testing Rails Applications] guide. + +==== Model Example ==== -Rails provides a very easy way to write performance test cases, which look just like the regular integration tests. Performance tests run a code profiler on your test methods. Profiling output for combinations of each test method, measurement, and output format are written to your +tmp/performance+ directory. By default, process_time is measured and both flat and graph_html output formats are written, so you'll have two output files per test method. +Even though the performance tests are integration tests and hence closer to the request/response cycle by nature, you can still performance test pure model code. -If you have a look at +test/performance/browsing_test.rb+ in a newly created Rails application: +Performance test for +Post+ model: [source, ruby] ---------------------------------------------------------------------------- require 'test_helper' require 'performance_test_help' -# Profiling results for each test method are written to tmp/performance. -class BrowsingTest < ActionController::PerformanceTest - def test_homepage - get '/' +class PostModelTest < ActionController::PerformanceTest + def test_creation + Post.create :body => 'still fooling you', :cost => '100' + end + + def test_slow_method + # Using posts(:awesome) fixture + posts(:awesome).slow_method end end ---------------------------------------------------------------------------- -This is an automatically generated example performance test file, for testing performance of homepage('/') of the application. - === Modes === -Performance test cases can be run in two modes : Benchmarking and Profling. +Performance tests can be run in two modes : Benchmarking and Profiling. ==== Benchmarking ==== -Benchmarking helps you find out how fast are your test cases. Each Test case is run +4 times+ in this mode. To run performance tests in benchmarking mode: +Benchmarking helps find out how fast each performance test runs. Each test case is run +4 times+ in benchmarking mode. + +To run performance tests in benchmarking mode: [source, shell] ---------------------------------------------------------------------------- @@ -111,7 +167,9 @@ $ rake test:benchmark ==== Profiling ==== -Profiling helps introspect into your test cases and figure out which are the slow parts. Each Test case is run +1 time+ in this mode. To run performance tests in profiling mode: +Profiling helps you see the details of a performance test and provide an in-depth picture of the slow and memory hungry parts. Each test case is run +1 time+ in profiling mode. + +To run performance tests in profiling mode: [source, shell] ---------------------------------------------------------------------------- @@ -120,47 +178,47 @@ $ rake test:profile === Metrics === -Benchmarking and profiling run performance test cases in various modes to help precisely figure out the where the problem lies. +Benchmarking and profiling run performance tests in various modes described below. ==== Wall Time ==== -Measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system. +Wall time measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system. Mode : Benchmarking ==== Process Time ==== -Measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load. +Process time measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load. Mode : Profiling ==== Memory ==== -Measures the amount of memory used for the performance test case. +Memory measures the amount of memory used for the performance test case. -Mode : Benchmarking, Profiling [Requires specially compiled Ruby] +Mode : Benchmarking, Profiling [xref:gc[Requires GC-Patched Ruby]] ==== Objects ==== -Measures the number of objects allocated for the performance test case. +Objects measures the number of objects allocated for the performance test case. -Mode : Benchmarking, Profiling [Requires specially compiled Ruby] +Mode : Benchmarking, Profiling [xref:gc[Requires GC-Patched Ruby]] ==== GC Runs ==== -Measures the number of times GC was invoked for the performance test case. +GC Runs measures the number of times GC was invoked for the performance test case. -Mode : Benchmarking [Requires specially compiled Ruby] +Mode : Benchmarking [xref:gc[Requires GC-Patched Ruby]] ==== GC Time ==== -Measures the amount of time spent in GC for the performance test case. +GC Time measures the amount of time spent in GC for the performance test case. -Mode : Benchmarking [Requires specially compiled Ruby] +Mode : Benchmarking [xref:gc[Requires GC-Patched Ruby]] === Understanding the output === -Performance tests generate different outputs inside +tmp/performance+ directory based on the mode it is run in and the metric. +Performance tests generate different outputs inside +tmp/performance+ directory depending on their mode and metric. ==== Benchmarking ==== @@ -182,7 +240,7 @@ BrowsingTest#test_homepage (31 ms warmup) ===== CSV files ===== -Performance tests results are also appended to +.csv+ files inside +tmp/performance/<Class>#<test>_<metric>.csv+ file. For example, running the default +BrowsingTest#test_homepage+ will generate following five files : +Performance test results are also appended to +.csv+ files inside +tmp/performance+. For example, running the default +BrowsingTest#test_homepage+ will generate following five files : - BrowsingTest#test_homepage_gc_runs.csv - BrowsingTest#test_homepage_gc_time.csv @@ -190,9 +248,9 @@ Performance tests results are also appended to +.csv+ files inside +tmp/performa - BrowsingTest#test_homepage_objects.csv - BrowsingTest#test_homepage_wall_time.csv -As the results are appended to these files each time the performance tests are run in benchmarking mode, it enables you gather data over a sustainable period of time which can be very helpful with various performance analysis. +As the results are appended to these files each time the performance tests are run in benchmarking mode, you can collect data over a period of time. This can be very helpful in analyzing the effects of code changes. -Sample output of +BrowsingTest#test_homepage_wall_time.csv + : +Sample output of +BrowsingTest#test_homepage_wall_time.csv+: [source, shell] ---------------------------------------------------------------------------- @@ -211,9 +269,11 @@ measurement,created_at,app,rails,ruby,platform ==== Profiling ==== +In profiling mode, you can choose from four types of output. + ===== Command line ===== -This is the very basic form of output in profiling mode. Example : +This is a very basic form of output in profiling mode: [source, shell] ---------------------------------------------------------------------------- @@ -225,103 +285,250 @@ BrowsingTest#test_homepage (58 ms warmup) ===== Flat ===== -Flat output shows the total amount of time spent in each method. http://ruby-prof.rubyforge.org/files/examples/flat_txt.html[Check ruby prof documentation for a better explaination]. +Flat output shows the total amount of time spent in each method. http://ruby-prof.rubyforge.org/files/examples/flat_txt.html[Check ruby prof documentation for a better explanation]. ===== Graph ===== -Graph output shows how long each method takes to run, which methods call it and which methods it calls. http://ruby-prof.rubyforge.org/files/examples/graph_txt.html[Check ruby prof documentation for a better explaination]. +Graph output shows how long each method takes to run, which methods call it and which methods it calls. http://ruby-prof.rubyforge.org/files/examples/graph_txt.html[Check ruby prof documentation for a better explanation]. ===== Tree ===== -Tree output is profiling information in calltree format for use by kcachegrind and similar tools. +Tree output is profiling information in calltree format for use by http://kcachegrind.sourceforge.net/html/Home.html[kcachegrind] and similar tools. + +=== Tuning Test Runs === + +By default, each performance test is run +4 times+ in benchmarking mode and +1 time+ in profiling. However, test runs can easily be configured. + +CAUTION: Performance test configurability is not yet enabled in Rails. But it will be soon. + +[[gc]] +=== Installing GC-Patched Ruby === + +To get the best from Rails performance tests, you need to build a special Ruby binary with some super powers - http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch[GC patch] for measuring GC Runs/Time and memory/object allocation. + +The process is fairly straight forward. If you've never compiled a Ruby binary before, follow these steps to build a ruby binary inside your home directory: -=== Preparing Ruby and Ruby-prof === +==== Installation ==== -Before we go ahead, Rails performance testing requires you to build a special Ruby binary with some super powers - GC patch for measuring GC Runs/Time. This process is very straight forward. If you've never compiled a Ruby binary before, you can follow the following steps to build a ruby binary inside your home directory: +Compile Ruby and apply this http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch[GC Patch]: -==== Compile ==== +==== Download and Extract ==== [source, shell] ---------------------------------------------------------------------------- [lifo@null ~]$ mkdir rubygc -[lifo@null ~]$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p111.tar.gz -[lifo@null ~]$ tar -xzvf ruby-1.8.6-p111.tar.gz -[lifo@null ~]$ cd ruby-1.8.6-p111 -[lifo@null ruby-1.8.6-p111]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0 -[lifo@null ruby-1.8.6-p111]$ ./configure --prefix=/Users/lifo/rubygc -[lifo@null ruby-1.8.6-p111]$ make && make install +[lifo@null ~]$ wget <download the latest stable ruby from ftp://ftp.ruby-lang.org/pub/ruby> +[lifo@null ~]$ tar -xzvf <ruby-version.tar.gz> +[lifo@null ~]$ cd <ruby-version> +---------------------------------------------------------------------------- + +==== Apply the patch ==== + +[source, shell] +---------------------------------------------------------------------------- +[lifo@null ruby-version]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0 +---------------------------------------------------------------------------- + +==== Configure and Install ==== + +The following will install ruby in your home directory's +/rubygc+ directory. Make sure to replace +<homedir>+ with a full patch to your actual home directory. + +[source, shell] +---------------------------------------------------------------------------- +[lifo@null ruby-version]$ ./configure --prefix=/<homedir>/rubygc +[lifo@null ruby-version]$ make && make install ---------------------------------------------------------------------------- ==== Prepare aliases ==== -Add the following lines in your ~/.profile for convenience: +For convenience, add the following lines in your +~/.profile+: ---------------------------------------------------------------------------- -alias gcruby='/Users/lifo/rubygc/bin/ruby' -alias gcrake='/Users/lifo/rubygc/bin/rake' -alias gcgem='/Users/lifo/rubygc/bin/gem' -alias gcirb='/Users/lifo/rubygc/bin/irb' -alias gcrails='/Users/lifo/rubygc/bin/rails' +alias gcruby='~/rubygc/bin/ruby' +alias gcrake='~/rubygc/bin/rake' +alias gcgem='~/rubygc/bin/gem' +alias gcirb='~/rubygc/bin/irb' +alias gcrails='~/rubygc/bin/rails' ---------------------------------------------------------------------------- -==== Install rubygems and some basic gems ==== +==== Install rubygems and dependency gems ==== -Download http://rubyforge.org/projects/rubygems[Rubygems] and install it from source. Afterwards, install rake. rails, ruby-prof and rack gems: +Download http://rubyforge.org/projects/rubygems[Rubygems] and install it from source. Rubygem's README file should have necessary installation instructions. +Additionally, install the following gems : + + * +rake+ + * +rails+ + * +ruby-prof+ + * +rack+ + * +mysql+ + +If installing +mysql+ fails, you can try to install it manually: + +[source, shell] +---------------------------------------------------------------------------- +[lifo@null mysql]$ gcruby extconf.rb --with-mysql-config +[lifo@null mysql]$ make && make install +---------------------------------------------------------------------------- + +And you're ready to go. Don't forget to use +gcruby+ and +gcrake+ aliases when running the performance tests. + +== Command Line Tools == + +Writing performance test cases could be an overkill when you are looking for one time tests. Rails ships with two command line tools that enable quick and dirty performance testing: + +=== benchmarker === + ++benchmarker+ is a wrapper around Ruby's http://ruby-doc.org/core/classes/Benchmark.html[Benchmark] module. + +Usage: + +[source, shell] ---------------------------------------------------------------------------- -[lifo@null ~]$ gcgem install rake -[lifo@null ~]$ gcgem install rails -[lifo@null ~]$ gcgem install ruby-prof -[lifo@null ~]$ gcgem install rack +$ script/performance/benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ... ---------------------------------------------------------------------------- -==== Install MySQL gem ==== +Examples: +[source, shell] ---------------------------------------------------------------------------- -[lifo@null ~]$ gcgem install mysql +$ script/performance/benchmarker 10 'Item.all' 'CouchItem.all' ---------------------------------------------------------------------------- -If this fails, you can try to install it manually: +If the +[times]+ argument is omitted, supplied methods are run just once: +[source, shell] ---------------------------------------------------------------------------- -[lifo@null ~]$ cd /Users/lifo/rubygc/lib/ruby/gems/1.8/gems/mysql-2.7/ -[lifo@null mysql-2.7]$ gcruby extconf.rb --with-mysql-config -[lifo@null mysql-2.7]$ make && make install +$ script/performance/benchmarker 'Item.first' 'Item.last' ---------------------------------------------------------------------------- -=== Generating performance test === +=== profiler === + ++profiler+ is a wrapper around http://ruby-prof.rubyforge.org/[ruby-prof] gem. + +Usage: -Rails provides a generator for creating new performance tests: +[source, shell] +---------------------------------------------------------------------------- +$ script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html] +---------------------------------------------------------------------------- + +Examples: [source, shell] ---------------------------------------------------------------------------- -[lifo@null application (master)]$ script/generate performance_test homepage +$ script/performance/profiler 'Item.all' ---------------------------------------------------------------------------- -This will generate +test/performance/homepage_test.rb+: +This will profile +Item.all+ in +RubyProf::WALL_TIME+ measure mode. By default, it prints flat output to the shell. + +[source, shell] +---------------------------------------------------------------------------- +$ script/performance/profiler 'Item.all' 10 graph +---------------------------------------------------------------------------- + +This will profile +10.times { Item.all }+ with +RubyProf::WALL_TIME+ measure mode and print graph output to the shell. + +If you want to store the output in a file: + +[source, shell] +---------------------------------------------------------------------------- +$ script/performance/profiler 'Item.all' 10 graph 2> graph.txt +---------------------------------------------------------------------------- + +== Helper methods == + +Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a given piece of code. The method is called +benchmark()+ in all the three components. + +=== Model === [source, ruby] ---------------------------------------------------------------------------- -require 'test_helper' -require 'performance_test_help' +Project.benchmark("Creating project") do + project = Project.create("name" => "stuff") + project.create_manager("name" => "David") + project.milestones << Milestone.find(:all) +end +---------------------------------------------------------------------------- -class HomepageTest < ActionController::PerformanceTest - # Replace this with your real tests. - def test_homepage - get '/' +This benchmarks the code enclosed in the +Project.benchmark("Creating project") do..end+ block and prints the result to the log file: + +[source, ruby] +---------------------------------------------------------------------------- +Creating project (185.3ms) +---------------------------------------------------------------------------- + +Please refer to the http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M001336[API docs] for additional options to +benchmark()+ + +=== Controller === + +Similarly, you could use this helper method inside http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[controllers] + +NOTE: +benchmark+ is a class method inside controllers + +[source, ruby] +---------------------------------------------------------------------------- +def process_projects + self.class.benchmark("Processing projects") do + Project.process(params[:project_ids]) + Project.update_cached_projects end end ---------------------------------------------------------------------------- -Which you can modify to suit your needs. +=== View === + +And in http://api.rubyonrails.com/classes/ActionController/Benchmarking/ClassMethods.html#M000715[views]: + +[source, ruby] +---------------------------------------------------------------------------- +<% benchmark("Showing projects partial") do %> + <%= render :partial => @projects %> +<% end %> +---------------------------------------------------------------------------- -== Other Profiling Tools == +== Request Logging == + +Rails log files contain very useful information about the time taken to serve each request. Here's a typical log file entry: + +[source, ruby] +---------------------------------------------------------------------------- +Processing ItemsController#index (for 127.0.0.1 at 2009-01-08 03:06:39) [GET] +Rendering template within layouts/items +Rendering items/index +Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items] +---------------------------------------------------------------------------- + +For this section, we're only interested in the last line: + +[source, ruby] +---------------------------------------------------------------------------- +Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items] +---------------------------------------------------------------------------- + +This data is fairly straightforward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller. + +Michael Koziarski has an http://www.therailsway.com/2009/1/6/requests-per-second[interesting blog post] explaining the importance of using milliseconds as the metric. + +== Useful Profiling Tools == + +=== Rails Plugins and Gems === -* http://www.hpl.hp.com/research/linux/httperf/[httperf] * http://rails-analyzer.rubyforge.org/[Rails Analyzer] * http://www.flyingmachinestudios.com/projects/[Palmist] +* http://github.com/josevalim/rails-footnotes/tree/master[Rails Footnotes] +* http://github.com/dsboulder/query_reviewer/tree/master[Query Reviewer] + +=== External === + +* http://www.hpl.hp.com/research/linux/httperf[httperf] +* http://httpd.apache.org/docs/2.2/programs/ab.html[ab] +* http://jakarta.apache.org/jmeter[JMeter] + +== Commercial Products == + +Rails has been lucky to have three startups dedicated to Rails specific performance tools: -== Commercial products dedicated to Rails Perfomance == * http://www.newrelic.com[New Relic] * http://www.fiveruns.com[Fiveruns] * http://scoutapp.com[Scout] @@ -330,6 +537,6 @@ Which you can modify to suit your needs. http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4[Lighthouse ticket] -* January 9, 2009: Rewrite by Pratik +* January 9, 2009: Complete rewrite by Pratik * October 17, 2008: First revision by Pratik -* September 6, 2008: Initial version by Matthew Bergman
\ No newline at end of file +* September 6, 2008: Initial version by Matthew Bergman |