Thursday, May 31, 2012

Capistrano to deploy rails application - how to handle long migrations?


So I am using Capistrano to deploy a rails application to my production server (apache+passenger) and at the moment deployment usually goes along the lines:




$cap deploy
$cap deploy:migrations



It got me wondering, let's say my db:migrations took a long time to execute on the production server (a big refactor of the db schema) - in this case what is best practice with Capistrano? What happens if users are connected to my application at the time of deployment? Should I gracefully send users to a static placeholder page while the database is being updated? Does Capistrano handle this automagically? Do I need to code up a recipe to help with this? Or does the internal mechanisms of rails / passenger mean that I don't have to worry at all about this particular case?



Thanks.


Source: Tips4all

4 comments:

  1. You should put up a maintenance page if the application is not going to be available for a while. I use this Capistrano task:

    namespace :deploy do
    namespace :web do
    desc <<-DESC
    Present a maintenance page to visitors. Disables your application's web \
    interface by writing a "maintenance.html" file to each web server. The \
    servers must be configured to detect the presence of this file, and if \
    it is present, always display it instead of performing the request.

    By default, the maintenance page will just say the site is down for \
    "maintenance", and will be back "shortly", but you can customize the \
    page by specifying the REASON and UNTIL environment variables:

    $ cap deploy:web:disable \\
    REASON="a hardware upgrade" \\
    UNTIL="12pm Central Time"

    Further customization will require that you write your own task.
    DESC
    task :disable, :roles => :web do
    require 'erb'
    on_rollback { run "rm #{shared_path}/system/maintenance.html" }

    reason = ENV['REASON']
    deadline = ENV['UNTIL']
    template = File.read('app/views/admin/maintenance.html.erb')
    page = ERB.new(template).result(binding)

    put page, "#{shared_path}/system/maintenance.html", :mode => 0644
    end
    end
    end


    The app/views/admin/maintenance.html.erb file should contain:

    <p>We’re currently offline for <%= reason ? reason : 'maintenance' %> as of <%= Time.now.utc.strftime('%H:%M %Z') %>.</p>
    <p>Sorry for the inconvenience. We’ll be back <%= deadline ? "by #{deadline}" : 'shortly' %>.</p>


    The final step is to configure the Apache virtual host with some directives to look for the maintenance.html file and redirect all requests to it if it's present:

    <IfModule mod_rewrite.c>
    RewriteEngine On

    # Redirect all requests to the maintenance page if present
    RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
    RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
    RewriteCond %{SCRIPT_FILENAME} !maintenance.html
    RewriteRule ^.*$ /system/maintenance.html [L]
    </IfModule>


    To put the application into maintenance mode, run cap deploy:web:disable and to make it live again do cap deploy:web:enable.

    ReplyDelete
  2. My production deploys generally follow this process:


    cap production deploy:web:disable which directs all requests to a static maintenance page
    cap production deploy
    migrations etc, testing each of the servers individually to make sure things are OK
    cap production deploy:web:enable to make the site work as it should


    John Topley's response gives you some good in depth info here.

    ReplyDelete
  3. I use Capistrano for deployment and I use

    cap deploy
    cap deploy:migrations


    to deploy if I have migrations.

    What I need is just using cap deploy, I need to run my migration also.

    Is there any way to do this.

    ReplyDelete
  4. Hi I was able to fix my issue in the above by adding the following line at the end my deploy.rb file

    after "deploy", "deploy:migrate"

    ReplyDelete