<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Mad Marmot: Tag rails</title>
    <link>http://www.lukeludwig.com/blog/articles/tag/rails</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>A blog about programming, ruby, rails, and my crazy outdoor pursuits</description>
    <item>
      <title>Use pagination to save memory when iterating over a lot of ActiveRecord objects.</title>
      <description>It is very easy to consume a ton of memory when using ActiveRecord&amp;#39;s find(:all) method. When transitioning the &lt;a href="http://www.teamsporttech.com"&gt;Team Sport Tech&lt;/a&gt; rails app from using file column to attachment_fu I wrote a migration to convert all of the photos we had to the new database format and the new location on disk. Without thinking I wrote code like this:&lt;br /&gt;&lt;br /&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Photo.find(:all).each do |photo|&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # conversion commands&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&amp;nbsp;&amp;nbsp;&lt;/font&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;br /&gt;&lt;br /&gt;I didn&amp;#39;t notice any issues executing this on my laptop which has 2 GB of RAM, but when I went to run this migration on our staging server over at &lt;a href="http://engineyard.com/"&gt;Engine Yard&lt;/a&gt; I had problems. Our staging server only has 640 MB of RAM. Our database has over 100,000 photos. That is an array with 100,000 ActiveRecord objects in memory all at once. As the migration executed on the staging server I could see that the rake task was using all available RAM and it was paging like crazy, utilizing 600 MB of virtual memory from disk. The cpu fluctuated between 0 and 1 percent of utilization due to the paging. If adequate memory was available this migration would take around 10 minutes and the cpu would be working like crazy. I did a few other things for awhile and came back to this migration 2 hours later. Still working and it probably had a long ways yet to go. 

&lt;p&gt;While the memory limitation on the staging server isn&amp;#39;t that important, the real question was how the production server with 1024 MB of RAM would handle the migration since the live app has to be down for maintenance when we roll this out. Instead of wondering I decided to fix the real problem, which was the stupid code I wrote to load all of the photos in memory for iteration. I quickly realized that the problem was already solved for me in the form of pagination. Pagination is usually thought of as a way to display N number of items to the user on a web page and providing links to move to the &amp;quot;Next&amp;quot; or &amp;quot;Previous&amp;quot; pages. When doing this the pagination code smartly loads only the ActiveRecord objects that are needed for the current page in memory, which is exactly the behavior I needed. &lt;br /&gt; &lt;br /&gt; We recently switched from using the classic rails pagination to the newer &lt;a href="http://errtheblog.com/posts/47-i-will-paginate"&gt;will_paginate&lt;/a&gt; plugin. Since I knew I would need to use will_paginate in this manner in other migrations, and possibly in actual application code, I wrote this iterator for reuse: &lt;br /&gt; &lt;br /&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def apply_to_all(klass, per_page)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; objects = klass.paginate :page =&amp;gt; 1, :per_page =&amp;gt; per_page&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; num_pages = objects.page_count&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; total_entries = objects.total_entries&lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for page in 1 .. num_pages&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; objects = klass.paginate(:page =&amp;gt; page, :per_page =&amp;gt; per_page, :total_entries =&amp;gt; total_entries)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; objects.each do |object|&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; yield object&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end &amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/font&gt;&lt;br /&gt; &lt;br /&gt; So I changed my migration code to look like this:&lt;br /&gt; &lt;br /&gt;&lt;font color="#ff0000"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; apply_to_all(Photo, 500) do |photo|&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # conversion commands&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; end &lt;/font&gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/p&gt;&lt;p&gt;A key thing to note is the use of total_entries. Each time the paginate method is called, 2 sql queries are performed.&amp;nbsp; One to get the records, and one to count how many records there are in total. This counting query can be very expensive when there are a lot of records, and if you provide the total_entries option to the paginate method the counting query will be skipped. While I chose to reuse the will_paginate plugin here, it wouldn&amp;#39;t be very difficult to rewrite the above code without it by using LIMIT with an offset, which is how the will_paginate plugin works. Additionally it may be useful to add a third parameter to the apply_to_all method called options, which is simply passed onto the paginate method allowing such things as conditions or includes to be execute in the paginate sql query. &lt;/p&gt;&lt;p&gt; So now the migration code loads 500 Photo objects in memory at once. I ran this migration on the staging server and it barely used any RAM and executed in the expected 10 minutes. This short piece of code is a prime example of why I enjoy programming in ruby.&lt;/p&gt;</description>
      <pubDate>Sat, 16 Feb 2008 22:57:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:294b91e6-351e-4cef-933a-163ac71010c7</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2008/02/16/use-pagination-to-save-memory-when-iterating-over-a-lot-of-activerecord-objects</link>
      <category>Programming</category>
      <category>will_paginate</category>
      <category>attachment_fu</category>
      <category>ActiveRecord</category>
      <category>ruby</category>
      <category>rails</category>
      <category>apply_to_all</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/8</trackback:ping>
    </item>
    <item>
      <title>RMagick has memory problems. MiniMagick with Attachment_Fu is slowwww. Go ImageScience.</title>
      <description>&lt;p&gt;Most Rails applications have to deal with resizing uploaded images for the creation of thumbnails.&amp;nbsp; The main choices include &lt;a href="http://rmagick.rubyforge.org/"&gt;RMagick&lt;/a&gt;, &lt;a href="https://rubyforge.org/projects/mini-magick/"&gt;MiniMagick&lt;/a&gt;, or &lt;a href="http://seattlerb.rubyforge.org/ImageScience.html"&gt;ImageScience&lt;/a&gt;, all of which come packaged as gems. Alternatively you can write your own which really isn&amp;#39;t that difficult.&amp;nbsp; So which one should you use? First I would recommend not writing your own, because it is really nice to take advantage of one of the very fine attachment plugins that are available. &lt;a href="http://wiki.rubyonrails.org/rails/pages/File+Column+Plugin"&gt;File column&lt;/a&gt; is the old standby rails attachment plugin, but it uses RMagick. &lt;a href="http://svn.techno-weenie.net/projects/plugins/attachment_fu/"&gt;Attachment_Fu&lt;/a&gt; is more flexible since you have the choice of using RMagick, MiniMagick, or ImageScience and can switch between them easily. At &lt;a href="http://www.teamsporttech.com"&gt;TeamSportTech&lt;/a&gt; where I work I recently transitioned our relatively large rails application from using file column and RMagick to using attachment_fu and ImageScience. Originally I was planning on using MiniMagick instead of ImageScience, but it turns out that MiniMagick is quite slow. The following timing results are on my Mac Book when running in development mode, and include uploading a single 3.4 MB jpeg which is resized down to 4 different sizes. MiniMagick consistently took 18 seconds to accomplish this, RMagick 7 seconds, and ImageScience 6 seconds. This doesn&amp;#39;t accurately represent a production environment, but I do believe it is a fair comparison. Note that the time to upload is not a factor since this was done entirely on my local computer.&lt;br /&gt;&lt;br /&gt;So why is MiniMagick with attachment_fu so slow? And why not use RMagick?&amp;nbsp; RMagick and MiniMagick use the well known &lt;a href="http://imagemagick.org/"&gt;ImageMagick&lt;/a&gt; C libraries. RMagick works by providing API ruby bindings to the ImageMagick libraries, which means that RMagick operates within the ruby process. It is well documented that RMagick consumes a lot of memory and has memory leaks as well. See &lt;a href="http://blog.craigambrose.com/past/2007/11/27/image_management_that_will_scale/"&gt;Craig Ambrose&amp;#39;s article&lt;/a&gt; and a &lt;a href="http://mephistoblog.com/2007/1/9/new-asset-thumbnailing-options-available"&gt;Mephisto article.&lt;/a&gt; It appears like &lt;a href="http://rmagick.rubyforge.org/rmagick2.html"&gt;RMagick2&lt;/a&gt; provides better memory management. &lt;/p&gt;

Even if you have memory to spare, the more memory your Mongrels utilize the more garbage collection that will have to be done, which eventually will bog down the Mongrels. MiniMagick solves this memory problem by utilizing the shell commands provided by ImageMagick, which means all image manipulations are done in separate processes outside the ruby process&amp;#39; memory space. ImageScience is a different beast altogether since it uses the &lt;a href="http://sourceforge.net/projects/freeimage"&gt;FreeImage&lt;/a&gt; library instead of ImageMagick, and it is more similar to RMagick in that it runs inside the ruby process. MiniMagick has extra overhead to start up a separate process for each ImageMagick command it executes. It also has extra disk overhead since it must operate on images on the filesystem instead of images loaded in memory.&lt;br /&gt;&lt;br /&gt;I put a print out in the MiniMagick source to track all the ImageMagick commands that MiniMagick runs to accomplish this single upload with 4 resizing operations. Here is the output with timing in the order that they were executed:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;1.421884 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.594993 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.592467 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp; &lt;br /&gt;&amp;nbsp;0.602928 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.594078 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.584066 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;1.444892 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;240x240&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.024799 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&amp;nbsp;0.019388 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-1&amp;quot; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;0.593593 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.580201 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.572714 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.590181 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.600532 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;1.396459 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -shave &amp;quot;648x0&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.973819 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;75x75!&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.016706 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&amp;nbsp;0.016919 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-2&amp;quot; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;0.594606 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.690731 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.592638 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;1.513243 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;550x550&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.028199 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&amp;nbsp;0.029103 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-3&amp;quot; &lt;br /&gt;&lt;br /&gt;&amp;nbsp;0.610143 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.593422 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.638836 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;1.777569 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mogrify -resize &amp;quot;1024x1024&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.089828 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%w\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot; &lt;br /&gt;&amp;nbsp;0.057866 seconds:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; identify -format &amp;quot;%h\\n&amp;quot; &amp;quot;/var/folders/UE/UE7FB01vFkmqEPkTei2ABk+++TI/-Tmp-/minimagick15344-0&amp;quot;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&lt;br /&gt;So as you can see, MiniMagick executed 30 separate shell commands. You can also see the temporary files on disk that it had to create to do these operations. To be fair, it is more correct to say that Attachment Fu executed 30 MiniMagick commands. I was curious why so many identify commands were needed. Each of the identify -format commands returns either the width (%w) or height (%h) of the image, which Attachment Fu stores in the database. This is very wasteful, because the initial identify command done for each resizing operation does include the width and height in the output, which should be parsed to save time. And for some reason notice that there are 10 identify commands to retrieve the width and 10 for the height, even though there are only 5 images involved (the original and 4 resized images). I don&amp;#39;t know why this would be necessary and haven&amp;#39;t checked the source to find out. Instead of pursuing these weird findings of how Attachment Fu uses MiniMagick, I decided to switch to Image Science. It is fast and is not a hog on memory.&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; </description>
      <pubDate>Sat, 16 Feb 2008 18:35:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:4508a339-f655-467d-aef3-f8f46bb58571</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2008/02/16/rmagick-has-memory-problems-minimagick-with-attachment_fu-is-slowwww-go-imagescience</link>
      <category>Programming</category>
      <category>MiniMagick</category>
      <category>attachment_fu</category>
      <category>file column</category>
      <category>RMagick</category>
      <category>ImageScience</category>
      <category>ImageMagick</category>
      <category>FreeImage</category>
      <category>ruby</category>
      <category>rails</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/7</trackback:ping>
    </item>
    <item>
      <title>Running ar_sendmail with monit</title>
      <description>&lt;p&gt;Sending email from a web application, especially blast emails to a lot of people, can take a lot of time. Generally you don&amp;#39;t want the user to wait until all the emails have been handed off to the smtp server. You also probably don&amp;#39;t want to tie up an entire mongrel with sending mail. The &lt;a href="http://blog.segment7.net/articles/2006/08/15/ar_mailer"&gt;ar_mailer gem&lt;/a&gt; solves this problem in excellent fashion, by saving pending emails to the database and having a separate ruby daemon process periodically check the database and send emails. I recently set up one of our rails apps at &lt;a href="http://teamsporttech.com"&gt;work&lt;/a&gt; to use ar_mailer. Configuring it to use ar_mailer was incredibly easy, but it was tricky to get the ar_sendmail ruby daemon process to run under &lt;a href="http://www.tildeslash.com/monit/"&gt;monit&lt;/a&gt;. On our production servers which we have hosted at &lt;a href="http://engineyard.com/"&gt;Engine Yard&lt;/a&gt;, we want every process that our application depends on to be monitored by monit.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The primary feature that ar_sendmail lacks to play nice with monit is the ability to leave a pid file after it starts up and to remove it when the process exits. This has already been pointed out on &lt;a href="http://rubyforge.org/tracker/index.php?func=detail&amp;amp;aid=14839&amp;amp;group_id=1513&amp;amp;atid=5924"&gt;rubyforge as a feature request&lt;/a&gt;. Here is what I did to get ar_sendmail working under monit: (ar_mailer 1.3.1) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0) Find the file ar_sendmail.rb on your system and open it for editing.&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;For me this was at /usr/local/lib/ruby/gems/1.8/gems/ar_mailer-1.3.1/lib/action_mailer/.&amp;nbsp; Another common location is /usr/lib/ruby/gems/1.8/gems/ar_mailer-1.3.1/lib/action_mailer/ &lt;/p&gt;&lt;p&gt;&lt;strong&gt;1) Create a class variable to store the pid file path in and a method to remove the pid file.&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;I put this just below &amp;quot;attr_accessor :failed_auth_count&amp;quot; &lt;/p&gt;&lt;pre&gt;  @@pid_file = nil&lt;/pre&gt;&lt;pre&gt;  def self.remove_pid_file&lt;br /&gt;    FileUtils.rm(@@pid_file) if @@pid_file&lt;br /&gt;  end  &amp;nbsp;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;2) Modify the self.run method in ar_sendmail.rb to create a pid file, and to not start up if an ar_sendmail process is already running&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if options[:Daemon] then &lt;br /&gt;      require &amp;#39;webrick/server&amp;#39;       &lt;br /&gt;      @@pid_file = &amp;quot;#{options[:Chdir]}/log/ar_mailer.pid&amp;quot;            &lt;br /&gt;      if File.exists? @@pid_file             &lt;br /&gt;        # check to see if process is actually running&lt;br /&gt;        pid = &amp;#39;&amp;#39;&lt;br /&gt;        File.open(@@pid_file, &amp;#39;r&amp;#39;) {|f| pid = f.read.chomp }             &lt;br /&gt;        if &lt;span style="padding: 0pt; background-color: yellow; color: black; display: inline; font-size: inherit"&gt;system&lt;/span&gt;(&amp;quot;ps -p #{pid} | grep #{pid}&amp;quot;) # returns true if process is running, o.w. false&lt;br /&gt;          $stderr.puts &amp;quot;Warning: The pid file #{@@pid_file} exists and ar_sendmail is running. Shutting down.&amp;quot;         &lt;br /&gt;          exit&lt;br /&gt;        else&lt;br /&gt;          # not running, so remove existing pid file and continue&lt;br /&gt;          self.remove_pid_file&lt;br /&gt;          log &amp;quot;ar_sendmail is not running. Removing existing pid file and starting up...&amp;quot;          &lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;      WEBrick::Daemon.start  &lt;br /&gt;      File.open(@@pid_file, &amp;#39;w&amp;#39;) {|f| f.write(&amp;quot;#{Process.pid}\n&amp;quot;)}    &lt;br /&gt;    end                &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;If the pid file already exists, this code will check to see if the process is actually running.&amp;nbsp; If it is then it will exit, otherwise it will remove the file and continue. This is useful for when the process dies ungracefully somehow (server crashes, killed with -9, .etc), in which case it will leave a pid file behind. Note that this differs from the code suggested on the &lt;a href="http://rubyforge.org/tracker/index.php?func=detail&amp;amp;aid=14839&amp;amp;group_id=1513&amp;amp;atid=5924"&gt;rubyforge feature request&lt;/a&gt;, not only in that it checks the existence of the pid file, but it references options[:Chdir] instead of Dir.pwd in order to be compatible with the -c and --chdir ar_sendmail option.&amp;nbsp;  &lt;/p&gt;&lt;p&gt;&lt;strong&gt;3) Modify the do_exit method in ar_sendmail.rb to remove the pid file&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;  def do_exit&lt;br /&gt;    log &amp;quot;caught signal, shutting down and removing pid file&amp;quot;&lt;br /&gt;    self.remove_pid_file&lt;br /&gt;    exit&lt;br /&gt;  end &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;4) Create the monit file /etc/monit.d/ar_sendmail.teamsport.monitrc&lt;/strong&gt;&lt;/p&gt;&lt;pre&gt;check process ar_sendmail_teamsport&lt;br /&gt;  with pidfile /data/teamsport/current/log/ar_mailer.pid&lt;br /&gt;  start program = &amp;quot;/usr/bin/ar_sendmail -d -e production -c /data/teamsport/current/&amp;quot; as uid teamsport and gid teamsport&lt;br /&gt;  stop program = &amp;quot;/usr/local/bin/stop_ar_sendmail&amp;quot; as uid teamsport and gid teamsport&lt;br /&gt;  if totalmem is greater than 65.0 MB for 2 cycles then restart      # eating up memory?&lt;br /&gt;  if loadavg(5min) greater than 10 for 8 cycles then restart          # bad, bad, bad&lt;br /&gt;  if 20 restarts within 20 cycles then timeout                        # something is wrong, call the sys-admin&lt;br /&gt;  group ar_sendmail &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Note that I am using a simple shell script script called stop_ar_sendmail to stop the ar_sendmail process.&amp;nbsp; ar_sendmail has signal handlers for SIGINT and SIGTERM so we should use these signals to kill it, which will cause the do_exit method to be triggered. The stop_ar_sendmail script looks like this:&lt;/p&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;kill -2 `cat /data/teamsport/current/log/ar_mailer.pid` &lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Originally I tried putting the contents of this shell script in the .montrc file like this:&lt;/p&gt;&lt;pre&gt;stop program = &amp;quot;/bin/kill -2 `cat /data/teamsport/current/log/ar_mailer.pid`&amp;quot; as uid teamsport and gid teamsport&amp;nbsp;&lt;/pre&gt;&lt;p&gt;This however does not work, since apparently monit doesn&amp;#39;t know what to do with the backticks. Alternatively you could use some sort of grep and kill script, such as pkill, to stop the ar_sendmail process. Ideally in the future, ar_sendmail will support some sort of stop command in the same manner as mongrel so that you could run &amp;quot;ar_sendmail stop&amp;quot; to stop it.&lt;/p&gt;&lt;p&gt;Once you have this all setup you can control ar_sendmail via monit. When creating the ar_sendmail.teamsport.monitrc file, make sure you change &amp;quot;teamsport&amp;quot; to the user that you want to run the process as.&amp;nbsp; Then do a &amp;quot;&lt;font color="#ff0000"&gt;sudo monit reload&lt;/font&gt;&amp;quot; and monit should see that ar_sendmail is not running and will start it for you. To make sure everything is working correctly try &amp;quot;&lt;font color="#ff0000"&gt;sudo monit stop ar_sendmail_teamsport&lt;/font&gt;&amp;quot; and &amp;quot;&lt;font color="#ff0000"&gt;sudo monit start ar_sendmail_teamsport&lt;/font&gt;&amp;quot; (replacing &amp;quot;teamsport&amp;quot; with the appropriate user name). &lt;/p&gt;&lt;p&gt;Other than not working well with monit out of the box, the only other issue I have with ar_sendmail is the memory footprint, which is dependent to some degree on your rails app. The ar_sendmail process for my app runs at around 50 MB, and just to send mail! I assume this is due to the fact that ar_sendmail loads the rails app&amp;#39;s environment.rb file. The environment.rb file runs the boot.rb file, which bootstraps and initializes the entire rails app. Additionally, our environment.rb has several other plugins required inside of it. I think the environment.rb file is loaded primarily just to get at the ActiveMailer smtp_settings, which is a slick way to allow for easy integration of ar_mailer with minimal changes to your existing rails app. Many people wouldn&amp;#39;t think twice about 50 MB, but rails hosts charge quite a bit for RAM. I can definitely envision a slimmed down ar_sendmail that doesn&amp;#39;t load the rails app&amp;#39;s environment.rb file, but it seems almost impossible to do this without making integration with existing rails apps more difficult.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <pubDate>Wed, 05 Dec 2007 19:35:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:672193d6-be4f-4545-acdd-42e3f4f669d6</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/12/05/running-ar_sendmail-with-monit</link>
      <category>rails</category>
      <category>ar_mailer</category>
      <category>ar_sendmail</category>
      <category>monit</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/6</trackback:ping>
    </item>
    <item>
      <title>How to add a new column to Rails' sessions</title>
      <description>&lt;p&gt;For a rails app at work we store user access privileges in the session.&amp;nbsp; This is done as an optimization to avoid an extra sql query that would need to be done for every page view to determine if the user has edit privileges.&amp;nbsp; Security-minded people may see this as a security hole.&amp;nbsp; For this application I don&amp;#39;t see this as a big deal.&amp;nbsp; Its not like our rails app is controlling the launch of nuclear missiles.&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;p&gt;The problem is that when an admin goes to modify the user access privileges for someone, the changes won&amp;#39;t take affect until the user next logs in since the user access privileges are stored in the session. So if the user is already logged in this is a problem. They will have to log out and log back in for the changes to take affect. The solution to this problem is to modify the session of the user who&amp;#39;s access privileges were modified.&amp;nbsp; &lt;/p&gt;&lt;p&gt;To do this we need to add a user_id column to the sessions table. This can be done like any other migration. The tricky part is accessing the user_id column. We will want to set the user_id of the session when someone logs in. This of course will not work:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; session.user_id = @user.id&lt;/font&gt; &lt;/p&gt;

&lt;p&gt;The session we are so familiar with is not like other ActiveRecord objects. If you are using ActiveRecordStore for your session storage (the rails built-in database session storage mechanism), this is easy. For ActiveRecordStore, the session has a model attribute allowing you to access the ActiveRecord session object. First you need to make a session model class which extends ActiveRecord. Mine looked lke this:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; class Session &amp;lt; ActiveRecord::Base &lt;/font&gt;&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/font&gt; &lt;/p&gt;&lt;p&gt;Then inside your &amp;quot;login&amp;quot; action you can do:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; session.model.user_id = @user.id&lt;/font&gt;&lt;/p&gt;&lt;p&gt;Then just rely on the automatic session saving built into rails to save the user_id to the database.&amp;nbsp;&lt;/p&gt;&lt;p&gt;If you are using SqlSessionStore, you don&amp;#39;t have access to the model attribute. Instead you can do this to set the user_id:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; my_session = Session.find_by_session_id(session.session_id)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; my_session.update_attribute(:user_id, current_user.id)&lt;/font&gt;&lt;/p&gt;&lt;p&gt;Now presumably at some point in your application you will need to access and make use of the user_id column. For me this is when the user access privileges are modified. Since we created a session.rb model class, we can use the built in rails find method to find all sessions for the user. &lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; sessions = Session.find(:all, :conditions =&amp;gt; &amp;quot;user_id = #{user.id}&amp;quot;, :select =&amp;gt; &amp;quot;session_id&amp;quot;)&amp;nbsp;&lt;/font&gt;&lt;/p&gt;&lt;p&gt;To take advantage of the built-in session data marshaling in rails, we need to access the session like this:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session = &lt;span class="caps"&gt;CGI&lt;/span&gt;::Session::ActiveRecordStore::Session.find_by_session_id(sess.session_id)&lt;/font&gt;&lt;/p&gt;&lt;p&gt;Then we can set a session value using the data attribute and save the session off.&amp;nbsp; Here is the full code for updating the sessions:&lt;/p&gt;&lt;p&gt;&lt;font color="#ff0000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; sessions.each do |sess|&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session = &lt;span class="caps"&gt;CGI&lt;/span&gt;::Session::ActiveRecordStore::Session.find_by_session_id(sess.session_id)&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session.data[:access_privileges] = new_access_privileges&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; user_session.save&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; end &lt;br /&gt;&lt;/font&gt;&lt;/p&gt;</description>
      <pubDate>Mon, 26 Nov 2007 17:59:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:bb8cf08e-09a7-4b7b-a2ee-302495c3108e</guid>
      <author>Luke Ludwig</author>
      <link>http://www.lukeludwig.com/blog/articles/2007/11/26/how-to-add-a-new-column-to-rails-sessions</link>
      <category>Programming</category>
      <category>rails</category>
      <category>sessions</category>
      <category>ActiveRecordStore</category>
      <category>SqlSessionStore</category>
      <trackback:ping>http://www.lukeludwig.com/blog/articles/trackback/5</trackback:ping>
    </item>
  </channel>
</rss>
