Rambling Labs Blog Ramblings on software development

  • Using RVM within a cron job

    Long time no post! I've been really busy these days with some projects. Yet, here I am again, so here it goes.

    A couple of days ago, I started working on a big story in one of those projects I'm currently working on. It's one of those things that needs to be run a couple of times a day... yeah, a scheduled background job.

    Basically, what it need to do is check some database records, check their expiration date against the current date, and take some actions for each one of the expired ones. Simple enough, right?

    I went ahead and wrote a rake task that would do the heavy work. Something like this:

    task :check_expired_records do
      Record.where('expired = ? AND expires_at < ?', false, Time.now).each do |record|
        # send some emails
        # ...
        # take some other actions
        # ...
        record.expired = true

    Then, I was going to add a line to the crontab to run it when scheduled, which I thought would be the easiest part. I added a line like this:

    0 */12 * * * cd /path/to/the/records_app/ && rake check_expired_records

    To my surprise, it didn't work. Checking the /var/log/syslog file, I saw this perplexing message:

    error (grandchild #4993 failed with exit status 127)

    Doing some extra debugging (redirecting both stdout and stderr outputs to a file), I found out that the problem was rake not being found.

    Hmm, that must mean that rvm isn't being loaded, right? I tried this instead:

    0 */12 * * * rvm use 1.9.2 && rvm gemset use records_app cd /path/to/the/records_app/ && rake check_expired_records

    Same error... But now it doesn't find rvm. Let's load it to the path manually:

    0 */12 * * * PATH=$PATH:$HOME/.rvm/bin && rvm use 1.9.2 && rvm gemset use records_app && cd /path/to/the/records_app/ && rake check_expired_records

    Ugh, yet another error message... Gemsets cannot be set when not using rvm rubies (currently).. Ok, let's add the gems path to our path as well:

    0 */12 * * * PATH=$PATH:$HOME/.rvm/bin:$HOME/.rvm/gems/ruby-1.9.2-p290@records_app/bin && rvm use 1.9.2 && cd /path/to/the/records_app/ && rake check_expired_records

    Nothing!... Now the error is a GemNotFoundException... Say what??.
    Hmm, taking into account all previous errors, it's definitely an environment issue.

    After some googling, I found this post which pretty much explained how to do what I was trying to do. Turns out that the cron job is executed with a very limited environment. That means that in order for rvm to work correctly I have to fire up another bash with the correct environment, load rvm and finally execute what I want to.

    So, at last, I ended up with this line in the crontab file:

    0 */12 * * * /bin/bash -l -c 'source "$HOME/.rvm/scripts/rvm" && rvm use 1.9.2 && rvm gemset use records_app && cd /path/to/the/records_app/ && rake check_expired_records'

    Awesome! It works!
    Now that was intense!

  • blog comments powered by Disqus