Mark Grealish

> cats and wizardry


Thorncastle Street Apartments, Ringsend

in ireland

FIXME

The Iveagh Trust, Dublin

in ireland

The Iveagh Trust, Christchurch, Dublin

Ghetto Single-Instance Cron jobs on Elastic Beanstalk

in code

Before you read this article, go and read about Elastic Beanstalk’s worker environments. A worker environment is a copy of your application which you configure to handle certain background tasks. In the case of Ruby on Rails, you can configure ActiveJob to interact with the worker tier through the active-elastic-job gem.

If a worker tier sounds like too much work, then Elastic Beanstalk also supports cron jobs which are executed on all instances. I understand the intent behind Amazon’s cron setup (perform a task against the instance), but the decision leads to pain. A cron job which executes an ActiveJob tasks will execute the task on all instances, a situation which leads to undesireable race conditions.

Our ghetto solution is to employ leader-only container commands which create the cron files for us:

---
files:
  '/usr/local/bin/setup_job':
    mode: '000755'
    owner: root
    group: root
    content: |
      #!/bin/bash

      APP_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_deploy_dir)
      CRON_SCRIPT=/usr/local/bin/cron_fetch_articles
      CRON_RUNNER=/etc/cron.d/fetch_blog_articles

      # Executes every two hours at the 0th minute.
      # https://crontab.guru/#0_*/2_*_*_*

      cat << END > $CRON_RUNNER
        0 */2 * * * root $CRON_SCRIPT

      END

      cat <<\ END > $CRON_SCRIPT
        #!/bin/bash

        # Import ENV vars and set Ruby version.
        source /opt/elasticbeanstalk/support/envvars
        source /opt/elasticbeanstalk/support/scripts/use-app-ruby.sh

        # Go to the app dir and execute BlogFeedJob.
        cd $(/opt/elasticbeanstalk/bin/get-config container -k app_deploy_dir)
        bundle exec bin/rails runner -e production 'RssJob.perform_later'
      END

      chmod +x $CRON_SCRIPT
commands:
  00_remove_old_cron:
    command: 'rm -f /etc/cron.d/*.bak'
  01_remove_old_script:
    command: 'rm -f /usr/local/bin/*.bak'
  02_setup_blog_feed_cron:
    leader_only: true
    command: '/usr/local/bin/setup_job'

So, the file setup_blog_feed_cron is written out on all instances; the command (02_setup_blog_feed_cron) which turn it into a cron only executes on the leader. The script writes out two further files using HEREDOC blocks:

  1. The cron timer (0 */2 * * * root $CRON_SCRIPT) with trailing newline.
  2. The actual cron script, written out to /usr/local/bin.

It works. ¯\_(ツ)_/¯


My Body is Ready

in science fiction

\\//

U.S.S. Discovery

March for Repeal

in me

At least one hundred people attended the March for Repeal today in Dublin.

Myself at the march

Liffey Sunset

in ireland

Hell of a gorgeous evening!

Low-tide Liffey sunset