Course3 Staging Vs Production

Build Robust & Production Quality Applications - Lesson 6: Riding Unicorns

Using Unicorn as a Production Server

Visit http://devcenter.heroku.com/articles/rails-unicorn

The issue with the rails server, is that it only allows for one web process at a time. Unicorn allows you to add additional servers, which can be specified in your config/unicorn.rb file

1. Add the Unicorn gem to your gemfile

gem 'unicorn'

then, run bundle install

2. Config

This is where you define the number worker processes. Worker processes in this context is all processes not just background processes. Below indicates that we want three worker processes running at once.

#config/unicorn.rb
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true

Update your Procfile

Set Unicorn as the server for the web process in your Procfile:

web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb

Adding Unicorn to your Heroku App

1. Add Redis to go

heroku addons:create redistogo

2. Run the following for Sidekiq version 3.0 or later:

heroku config:set REDISPROVIDER=REDISTOGOURL

3 Free background job on Heroku

Running more than 1 dyno (server) on Heroku costs, and ain't nobody got time for that. Coderwall has created a hack:

Course3 Procfile Foreman


layout: post title: "Build Robust & Production Quality Applications - Lesson 6: Procfile & Foreman" tags: tealeaf


On your server, you can run both web and worker processes locally - you can also do the same on Heroku, identifiying the processes you would like to run in your Procfile. A procfile is placed in the root of your diretory and declare what types of processes you want run in your app.

In your procfile, you will define:

<process type>: <command>
web: bundle exec rails server -p $PORT
worker: bundle exec rake jobs:work (example from delayed jobs)
worker: sidekiq

Develop locally with Foreman

Instead of having to start both Sidekiq and our Rails server locally, we can use format to initiate both processes by runniong "foreman start" in your app directory.

Heroku

will parse out the web processes from your Procfile.

heroku ps

will list the processes running

heroku logs

will list all process types

Heroku will allow you to run one web & worker processes but if you'd like to run more, it may costs.

Build Robust & Production Quality Applications - Lesson 6: Background Jobs

Background Jobs

Email sending typically is not very fast since we have to rely on some third party application. Waiting can slow up your app and potentially frustrate users so it's best to put non-essential and non-time-sensitive tasks in background jobs.

Since you can web processes and background job processes running locally, you need tp run both the rails server and sidekiq servers to process jobs respectively.

Resque

came out of Github - Resque is our Redis-backed library for creating background jobs, placing those jobs on multiple queues, and processing them later.

Benefits: has admin interface, is powered by Github Delayed Job does not require Redis, both uses a pulling queue Beanstalk and Stalker may be better if you care about speed, as they have a pulling mechanism

Sidekiq is a higher performance background solution than Resque and can even be ported, added on top of resque solution.

Check out the Railscast on Resque for intro.

Sidekiq

In order to use Sidekiq, you must install and run Redis. Once downloaded, run the "$ src/redis-server" in the redis-3.0.2 folder.

You must also gem install sidekiq, and run "bundle exec sidekiq"

When watching the Railscast on Sidekiq, it talks about the creation of workers:

#app/controllers/invitations_controller.rb
class InvitationsController < ApplicationController
  before_filter :require_user
  def new
    @invitation = Invitation.new
  end

  def create
    @invitation = Invitation.create(invitation_params.merge!(inviter_id: current_user.id))

    if @invitation.save
      InivtationWorker.perform_async
      flash[:success] = "You've successfully invited #{@invitation.recipient_name}."
      redirect_to new_invitation_path
    else
      flash[:error] = "Please fill in all inputs."
      render :new
    end
  end
#app/workers
class InvitationWorker
  include Sidekiq::Worker

  def perform(invitation_id)
    invitation = Invitation.find(invitation_id)
    AppMailer.send_invitation_email(invitation).deliver
  end
end

But instead of creating workers, we can just use delayed extentions

#app/controlers/invitations_controller.rb
  def create
    @invitation = Invitation.create(invitation_params.merge!(inviter_id: current_user.id))

    if @invitation.save
      AppMailer.delay.send_invitation_email(@invitation.id)
      flash[:success] = "You've successfully invited #{@invitation.recipient_name}."
      redirect_to new_invitation_path
    else
      flash[:error] = "Please fill in all inputs."
      render :new
    end
  end

Since we aren't running .perform_async on a worker, we need to pass @invitiation.id to the AppMailer:

#app/app_mailer.rb
def send_invitation_email(invitation_id)
    @invitation = Invitation.find(invitation_id)
    mail to: @invitation.recipient_email, from: "info@myflix.com", subject: "Invitation to join Myflix"
  end
end

To run tests, you have to add the following to run them inline:

#spec/spec_helper.rb

require 'sidekiq/testing'
Sidekiq::Testing.inline!

Build Robust & Production Quality Applications - Lesson 6: Mailgun & Heroku

To send emails from your Heroku app using Mailgun is simple:

1. Mailgun Addon

Add the Mailgun add-on to your Heroku app through the command line (within your app project directory)

heroku addons:create mailgun

or you can add via their web interface

Mailgun Heroku Image

2. Change your smtp settings

Then you copy and paste their smtp settings into your config/production.rb file. Make sure to change your domain name and add your default url options (which should point to your domain name) - if not, you will get an error: Errno::ECONNREFUSED: Connection refused - connect(2) for action mailer ...port(25)

config.action_mailer.delivery_method = :smtp
  # SMTP settings for mailgun
  config.action_mailer.smtp_settings = {
    :port           => ENV['MAILGUN_SMTP_PORT'],
    :address        => ENV['MAILGUN_SMTP_SERVER'],
    :user_name      => ENV['MAILGUN_SMTP_LOGIN'],
    :password       => ENV['MAILGUN_SMTP_PASSWORD'],
    :domain         => 'aqueous-coast-5067.herokuapp.com',
    :authentication => :plain,
  }
  config.action_mailer.default_url_options = { :host => 'aqueous-coast-5067.herokuapp.com' }

3. Heroku config

You do not have to store the ENV variable in application.yml, you only have to run "heroku config" from your command line.

4. If you find you do need to store ENV variable in your app, check out Figaro.