<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>nick evans - devblog &#187; database</title>
	<atom:link href="http://ekenosen.net/nick/devblog/tag/database/feed/" rel="self" type="application/rss+xml" />
	<link>http://ekenosen.net/nick/devblog</link>
	<description>thoughts on software craftsmanship and other technobabble</description>
	<lastBuildDate>Wed, 27 May 2009 18:00:02 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>features of rails migrations you should probably use</title>
		<link>http://ekenosen.net/nick/devblog/2008/11/rails-migrations-features/</link>
		<comments>http://ekenosen.net/nick/devblog/2008/11/rails-migrations-features/#comments</comments>
		<pubDate>Sat, 29 Nov 2008 19:00:02 +0000</pubDate>
		<dc:creator>nick</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[migrations]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://ekenosen.net/nick/devblog/?p=25</guid>
		<description><![CDATA[I recently paired with another developer to fix a bug in a rails DB migration.  As we cleaned up the code in order to analyze the bug, we noticed two simple features that were not being used, and the other developer recommended that I write up an email to point these features out to everyone [...]]]></description>
			<content:encoded><![CDATA[<p>I recently paired with another developer to fix a bug in a rails DB migration.  As we cleaned up the code in order to analyze the bug, we noticed two simple features that were not being used, and the other developer recommended that I write up an email to point these features out to everyone else.  And now I&#8217;m cleaning up that email to post here.  Hopefully this helps someone else out.  Both of these (and more) are documented at<a href="http://api.rubyonrails.org/classes/ActiveRecord/Migration.html"> http://api.rubyonrails.org/classes/ActiveRecord/Migration.html</a></p>
<h3>Cool feature: say_with_time</h3>
<p>If you find yourself putting comments around your code to explain to developers what&#8217;s going on, please consider instead using &#8220;<code>say_with_time</code>&#8220;.  Then you can document what is happening both in the code <em>and</em> on the console when the migration is actually running&#8230; and you&#8217;ll get other nice info printed out (like the elapsed time) as well.</p>
<h3>Important feature: ActiveRecord::IrreversableMigration</h3>
<p>If the migration cannot safely or easily be migrated downwards, then we need to communicate that clearly to other developers.  But &#8220;<code>puts</code>&#8221; isn&#8217;t good enough.  Instead, &#8220;<code>raise ActiveRecord::IrreversableMigration</code>&#8220;.</p>
<p>For complicated migrations, even if it is <em>possible</em> to safely reverse the migration, I strongly prefer simply raising the exception.  It&#8217;s too easy to make a mistake, and then you&#8217;ll have a DB that claims to be at one version, but has corrupted data for that version, which will most likely lead to more pain and suffering down the line.</p>
<p>If the migration is just cleaning up bad data, then there&#8217;s probably no real need to reverse it.  But in that case maybe you should at least print out a message to the screen letting the developer know that nothing is happening, and why that is okay.</p>
<p>Since I rarely ever use down migrations, my threshold is probably lower than most; if <code>:Rinvert</code> from <a href="http://rails.vim.tpope.net/">rails.vim</a> can&#8217;t automatically generate the down migration, then I will probably simply raise the exception.  I&#8217;ve personally witnessed too many needless bugs due to corrupted data and too many broken down migrations to invest any significant time into them.  At any rate, developers should use discretion with down migrations.</p>
<p>Oh, and <em>please</em> don&#8217;t <em>ever</em> run down migrations in production.  That&#8217;s what database backups are for, and you are backing up before you upgrade your production database, <em>aren&#8217;t you?</em></p>
<h3>Use the progressbar gem for long running data migrations</h3>
<p>And one other thing that is not included with rails that you should probably be using anyway: <a href="http://0xcc.net/ruby-progressbar/index.html.en">the progressbar gem</a>.  If you any long running data migrations, this is a must.  And just because it isn&#8217;t long running for you with your developer DB doesn&#8217;t mean it won&#8217;t be long running during deployment to production.  It&#8217;s trivially easy to use, and your deployer&#8217;s won&#8217;t be stuck wondering if their connection has been dropped or the migration has locked up.  And the ETA will let them know if they have time to get a cup of coffee.  The other developers and deployers will thank you.</p>
<h3>Simple Example</h3>
<p><em>(albeit, also a poorly contrived example)</em></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
</pre></td><td class="code"><pre class="ruby" style="font-family:monospace;"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'progressbar'</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> CreateWidgetAuxiliaryFrobs <span style="color:#006600; font-weight:bold;">&lt;</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::Migration</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">up</span>
    create_table <span style="color:#ff3333; font-weight:bold;">:widget_auxiliary_frobs</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>t<span style="color:#006600; font-weight:bold;">|</span>
      t.<span style="color:#CC0066; font-weight:bold;">integer</span> <span style="color:#996600;">&quot;widget_id&quot;</span>
      t.<span style="color:#CC0066; font-weight:bold;">string</span>  <span style="color:#996600;">&quot;frob_type&quot;</span>
      t.<span style="color:#CC0066; font-weight:bold;">integer</span> <span style="color:#996600;">&quot;frobitude&quot;</span>
      <span style="color:#008000; font-style:italic;"># etc...</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    say_with_time<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;migrating froms from widgets&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
      widgets = Widget.<span style="color:#9900CC;">find</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:all</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      pbar = ProgressBar.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;Generating Widget Frobs&quot;</span>, widgets.<span style="color:#9900CC;">size</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      widgets.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> <span style="color:#006600; font-weight:bold;">|</span>w<span style="color:#006600; font-weight:bold;">|</span>
       <span style="color:#008000; font-style:italic;"># this code changes the data irreversibly</span>
       <span style="color:#008000; font-style:italic;"># this code can't be (easily) rewritten with a SQL UPDATE or INSERT</span>
       <span style="color:#008000; font-style:italic;"># etc  etc  etc</span>
       pbar.<span style="color:#9900CC;">inc</span>
      <span style="color:#9966CC; font-weight:bold;">end</span>
      pbar.<span style="color:#9900CC;">finish</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    say_with_time<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;delete obsolete widget/wadgit data&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
      Wadget.<span style="color:#9900CC;">delete_all</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;value = 'kerfluffle'&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      remove_column <span style="color:#ff3333; font-weight:bold;">:widget</span>, <span style="color:#ff3333; font-weight:bold;">:foo</span>
      remove_column <span style="color:#ff3333; font-weight:bold;">:widget</span>, <span style="color:#ff3333; font-weight:bold;">:bar_id</span>
      <span style="color:#008000; font-style:italic;"># etc...</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">down</span>
    <span style="color:#CC0066; font-weight:bold;">raise</span> <span style="color:#6666ff; font-weight:bold;">ActiveRecord::IrreversibleMigration</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></td></tr></table></div>

<h3>If the dataset is <em>very </em>large</h3>
<p>If the dataset is especially large, you&#8217;ll want to iterate through it in a less naive manner than I did above: &#8220;<code>Widget.find(:all).each</code>&#8220;.  At the very least, you&#8217;ll want to iterate in such a way that already handled objects can be garbage collected prior to the end of the loop.  This might be necessary to avoiding the dreaded <code>NoMemoryError</code> (or decreased speed due to massive swapping).  This can be handled simply by iterating through the dataset using pagination, but you could also employ a more sophisticated strategy.</p>
]]></content:encoded>
			<wfw:commentRss>http://ekenosen.net/nick/devblog/2008/11/rails-migrations-features/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
