• Stars
    star
    109
  • Rank 319,077 (Top 7 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 12 years ago
  • Updated over 9 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Beat Heroku's 60s boot timeout with a proxy.

Heroku::Forward Build Status

Beat Heroku's 60 seconds timeout with a proxy.

What's this?

Heroku will report an application crashing and yield an R10 Boot Timeout error when a web process took longer than 60 seconds to bind to its assigned $PORT. This error is often caused by a process being unable to reach an external resource, such as a database or because Heroku is pretty slow and you have a lot of gems in your Gemfile.

This gem implements a proxy using em-proxy. This proxy is booted almost immediately, binding to the port assigned by Heroku. Heroku now reports the dyno up. The proxy then spawns your application's web server and establishes a connection over a unix domain socket (a file) between the proxy and the application. Once the application is ready, it will be able to serve HTTP requests normally. Until then requests may queue and some may timeout depending on how long it actually takes to start.

Usage

Add heroku-forward to your Gemfile.

gem "heroku-forward"

Create a new application rackup file, eg. my_app.ru that boots your application. Under Rails, this is the file that calls run.

require ::File.expand_path('../config/environment',  __FILE__)
run MyApp::Application

Modify your default rackup file as follows. Under Rails this file is called config.ru.

require 'rubygems'
require 'bundler'

$stdout.sync = true
Bundler.require(:rack)

port = (ARGV.first || ENV['PORT'] || 3000).to_i
env = ENV['RACK_ENV'] || 'development'

require 'em-proxy'
require 'logger'
require 'heroku-forward'
require 'heroku/forward/backends/thin'

application = File.expand_path('../my_app.ru', __FILE__)
backend = Heroku::Forward::Backends::Thin.new(application: application, env: env)
proxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)
proxy.logger = Logger.new(STDOUT)
proxy.forward!

This sets up a proxy on the port requested by Heroku and runs your application with Thin. Other back-ends are also supported, see below.

Add the following line to config/development.rb to see Rails logger output on STDOUT:

config.middleware.use Rails::Rack::LogTailer

Foreman

Heroku Cedar expects a Procfile that defines your application processes.

web: bundle exec ruby config.ru
worker: bundle exec rake jobs:work

You can use foreman to test the proxy locally with foreman start web.

Heroku Log

Here's the log output from an application that uses this gem. Notice that Heroku reports the state of web.1 up after just 4 seconds, while the application takes 67 seconds to boot.

2012-12-11T23:33:42+00:00 heroku[web.1]: Starting process with command `bundle exec ruby config.ru`
2012-12-11T23:33:46+00:00 app[web.1]:  INFO -- : Launching Backend ...
2012-12-11T23:33:46+00:00 app[web.1]:  INFO -- : Launching Proxy Server at 0.0.0.0:42017 ...
2012-12-11T23:33:46+00:00 app[web.1]: DEBUG -- : Attempting to connect to /tmp/thin20121211-2-1bfazzx.
2012-12-11T23:33:46+00:00 app[web.1]:  WARN -- : no connection, 10 retries left.
2012-12-11T23:33:46+00:00 heroku[web.1]: State changed from starting to up
2012-12-11T23:34:32+00:00 app[web.1]: >> Thin web server (v1.5.0 codename Knife)
2012-12-11T23:34:32+00:00 app[web.1]: >> Maximum connections set to 1024
2012-12-11T23:34:32+00:00 app[web.1]: >> Listening on /tmp/thin20121211-2-1bfazzx, CTRL+C to stop
2012-12-11T23:34:53+00:00 app[web.1]: DEBUG -- : Attempting to connect to /tmp/thin20121211-2-1bfazzx.
2012-12-11T23:34:53+00:00 app[web.1]: DEBUG -- : Proxy Server ready at 0.0.0.0:42017 (67s).

Proxy Forwarding Options

Heroku::Forward::Proxy::Server.forward! accepts the following options:

  • delay: number of seconds to sleep before launching the proxy, eg. proxy.forward!(delay: 15). This prevents queuing of requests or reporting invalid up status to Heroku. It's recommended to set this value to as close as possible to the boot time of your application and less than the Heroku's 60s boot limit. Also note that the proxy itself needs time to boot.

Available Backends

Thin

For more information about Thin see http://code.macournoyer.com/thin.

require 'heroku/forward/backends/thin'

application = File.expand_path('../my_app.ru', __FILE__)
backend = Heroku::Forward::Backends::Thin.new(application: application, env: env)
proxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)
proxy.forward!

The Thin back-end supports the following options.

  • application: application to load
  • env: environment, eg. :development

SSL is also supported.

  • ssl: enable SSL
  • ssl-key-file: path to private key
  • ssl-cert-file: path to certificate
  • ssl-verify: enable SSL certificate verification
options = { application: File.expand_path('../my_app.ru', __FILE__) }

# branch to disable SSL depending on your environment
if ENV['THIN_SSL_ENABLED']
  options[:ssl] = true
  options[:ssl_verify] = true
  options[:ssl_cert_file] = ENV['THIN_SSL_CERT_FILE']
  options[:ssl_key_file] = ENV['THIN_SSL_KEY_FILE']
end

backend = Heroku::Forward::Backends::Thin.new(options)

Unicorn

For more information about Unicorn see http://unicorn.bogomips.org.

The Unicorn back-end supports the following options.

  • application: application to load
  • env: environment, eg. :development
  • config_file: Unicorn configuration file
require 'heroku-forward'
require 'heroku/forward/backends/unicorn'

application = File.expand_path('../my_app.ru', __FILE__)
config_file = File.expand_path('../config/unicorn.rb', __FILE__)
backend = Heroku::Forward::Backends::Unicorn.new(application: application, env: env, config_file: config_file)
proxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)
proxy.forward!

Puma

For more information about Puma see http://puma.io.

The Puma back-end supports the following options.

  • application: application to load
  • env: environment, eg. :development
  • socket: socket, eg. /tmp/puma.sock
  • config_file: Puma configuration file
  • term_signal: Signal to send to Puma cluster when the backend is terminated; defaults to TERM
require 'heroku-forward'
require 'heroku/forward/backends/puma'

application = File.expand_path('../my_app.ru', __FILE__)
config_file = File.expand_path('../config/puma.rb', __FILE__)
backend = Heroku::Forward::Backends::Puma.new(application: application, env: env, config_file: config_file)
proxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)
proxy.forward!

Note that heroku-forward does not currently run on JRuby, see em-proxy#39 for details.

Fail-Safe

If you're worried about always using heroku-forward, consider building a fail-safe. Modify your config.ru to run without a proxy if DISABLE_FORWARD_PROXY is set as demonstrated in this gist. Add DISABLE_FORWARD_PROXY via heroku config:add DISABLE_FORWARD_PROXY=1.

Reading Materials

Contributing

Fork the project. Make your feature addition or bug fix with tests. Send a pull request. Bonus points for topic branches.

Copyright and License

MIT License, see LICENSE for details.

(c) 2012-2013 Daniel Doubrovkine, Artsy

More Repositories

1

fui

Find unused Objective-C imports.
Ruby
2,041
star
2

ARTiledImageView

Display, pan and deep zoom with tiled images on iOS.
Objective-C
229
star
3

ruby-enum

A handy way to define enums in Ruby.
Ruby
176
star
4

slack-gamebot

Ping pong, chess, pool and other games bot for Slack.
Ruby
129
star
5

iex-ruby-client

IEX Finance API Ruby Client
Ruby
119
star
6

rspec-rerun

Re-run (retry) failed RSpec examples.
Ruby
94
star
7

ios-snapshot-test-case-expecta

Expecta matchers for ios-snapshot-test-case.
Objective-C
93
star
8

msiext

AppSecInc. Community MSI Extensions
C++
85
star
9

strava-ruby-client

A complete Ruby client for the Strava API v3.
Ruby
80
star
10

gem-licenses

Collect gem licenses.
Ruby
67
star
11

vmwaretasks

C# VixCOM Wrapper Library & Tools
XSLT
58
star
12

TSMiniWebBrowser

In-app web browser control for iOS apps, maintained version of tonisalae/TSMiniWebBrowser.
Objective-C
56
star
13

dzt

Slice deep-zoom images.
Ruby
54
star
14

ARASCIISwizzle

Swizzle your iOS project into ASCII art.
Objective-C
51
star
15

guard-rack

Restart Rack when files change.
Ruby
42
star
16

obcd

Deal with obsessive compulsive issues of programmers in Objective-C.
Ruby
41
star
17

slack-strava

(Re)Post Strava activities to Slack
Ruby
37
star
18

open-weather-ruby-client

OpenWeather Ruby Client
Ruby
34
star
19

ocmock-expecta

Expecta matchers for OCMock
Objective-C
33
star
20

slack-market

Market quotes in Slack.
Ruby
32
star
21

danger-changelog

A plugin that is OCD about your CHANGELOG format.
Ruby
29
star
22

alexa-parrot

A walkthrough for building a basic Alexa skill.
JavaScript
27
star
23

log4jna

Log4jna is a library of native appenders for log4j.
Java
26
star
24

pixmatch

Idee PixMatch API client
Ruby
24
star
25

canonical-emails

Combine email validation and transformations to produce canonical email addresses.
Ruby
23
star
26

fue

Find an e-mail addresses of Github users.
Ruby
20
star
27

slack-sup

Slack bot that generates fresh triads of team members to meet every week in an informal standup, or S'Up.
Ruby
18
star
28

actionmailer-text

Automatically insert a text/plain part into your HTML multipart e-mails.
Ruby
17
star
29

objc-ngram

Rudimentary ngram search in ObjectiveC.
Objective-C
15
star
30

remoteinstall

Pragmatic VMWare-based tool for automating software testing, starting with software installation.
C#
13
star
31

grape-on-rack-v1-inside-v2

Demonstrate versioning fallback in Grape.
Ruby
13
star
32

dotfiles

My dotfiles.
Shell
10
star
33

google-finance-ruby-client

Google Finance web API ruby client with support for retrieving stock quotes and historical prices.
Ruby
10
star
34

ruby-overload

Overload Ruby methods at runtime.
Ruby
8
star
35

heroku-commander

Master the Heroku CLI from Ruby.
Ruby
7
star
36

grape-with-mongoid

Grape API implementing a basic CRUD model with Mongoid.
Ruby
7
star
37

faf

Find active Github forks.
Ruby
7
star
38

slack-amber-alert

Missing kids notifications from missingkids.org in Slack.
Ruby
7
star
39

run.dblock.org

Personal running website.
JavaScript
6
star
40

svn2svn

Copy, sync, replay or reflect changes across SVN repositories.
C#
6
star
41

grape-with-roar-walkthrough

Live coding of a rudimentary Grape+Roar HAL API.
Ruby
6
star
42

email-example-spec

Integration testing with e-mail examples.
Ruby
6
star
43

baseclasses

a collection of C++ classes from the 90s, used in alkaline search engine
C++
6
star
44

graphql-invoices

GraphQL demo on Ruby on Rails.
Ruby
5
star
45

strava-ruby-cli

Strava command-line client.
Ruby
5
star
46

slack-moji

You don't have enough emoji on Slack.
Ruby
5
star
47

grape_warden

Ruby
4
star
48

3licenses

Collect your 3rd party licenses.
Java
4
star
49

danger-toc

A danger.systems plugin for your markdown TOC.
Ruby
4
star
50

codeproject

source code and experiments for various CodeProject articles
C#
4
star
51

alexa-app-server-hello-world

JavaScript
4
star
52

amerika

Make America great again!
JavaScript
4
star
53

with-version

Syntax sugar for version checks.
Ruby
4
star
54

strava-motor

Automate me some Strava.
Ruby
3
star
55

artsy-download-script

A command-line tool for downloading full-size images from artsy.com
Python
3
star
56

sncore

social networking foundation that runs foodcandy.com
C#
3
star
57

slack-arena

Are.na integration into Slack.
Ruby
3
star
58

ARImageViewInsideScrollView

Demo for Tiling and Zooming ASCII Art @ iOSOHO, NYC, June 9th, 2014.
Objective-C
2
star
59

discord-strava

Connect Strava to Discord.
Ruby
2
star
60

dance-the-art-genome

Dance the Art Genome!
JavaScript
2
star
61

autoconf

an attempt to save the source code of autoconf, my famous multi-boot program for DOS
Fortran
2
star
62

alexa-mom

Shhh... a hackathon project.
JavaScript
2
star
63

ssoexample-waffle

single sign on example using WAFFLE
Java
2
star
64

excalc

vestris inc. expression calculator, circa 96'
Pascal
1
star
65

amzn-stock-price-data

Amazon stock price data and 4 year grant averages.
Ruby
1
star
66

alkaline

the alkaline search engine, circa 2000
C++
1
star
67

scala-parse-json

Parse JSON in Scala with Jackson.
Scala
1
star
68

1aat-ruby

A dummy web server that takes a long time to process a request and closes its listening socket every time.
Ruby
1
star
69

artsy-feathers

A set of examples that tweet artworks and shows retrieved from the Artsy API.
Ruby
1
star
70

delayed_job_rspec

Patches Delayed::Job to run jobs immediately under RSpec.
Ruby
1
star
71

agnes

aGNeS BBS, circa 96
C++
1
star
72

ruby-link-checker

Fast ruby link checker.
Ruby
1
star
73

p5art

A dip into digital art.
HTML
1
star
74

opensearch-dotnet-client-demo

OpenSearch DotNet (.NET) Client Demo
C#
1
star
75

enumerable-detect-value

Unlike Enumerable#detect, #detect_value returns the result of the block being evaluated.
Ruby
1
star