Search code examples
ruby-on-railsrvmpumaforeman

Foreman shuts down puma after `foreman start`


My puma server does not stay active, it keeps shutting down. I think it has to do with the release, but not sure how to make it dynamic so it works on heroku but not foreman.

I started my puma server and sidekiq worker with a procfile when I run foreman start

Please see the below setup code. I am not sure why it keeps shutting down.

I installed foreman

$ gem install foreman

Setup a Procfile

Procfile.rb

release: bin/heroku_release.sh
web: bundle exec puma -C ./config/puma.rb
worker: bundle exec sidekiq

Setup a bin/heroku_release.sh file

heroku__release.sh file

#!/usr/bin/env bash
#
# Usage: bin/heroku_deploy

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NO_COLOR='\033[0m'
CLEAR_LINE='\r\033[K'

set -euo pipefail

schema_version=$(bin/rails db:version | { grep "^Current version: [0-9]\\+$" || true; } | tr -s ' ' | cut -d ' ' -f3)

if [ -z "$schema_version" ]; then
  printf "💀${RED}   [Release Phase]: Database schema version could not be determined. Does the database exist?${NO_COLOR}\n"
  exit 1
fi

if [ "$schema_version" -eq "0" ]; then
  printf "\n⏳${YELLOW}   [Release Phase]: Loading the database schema.${NO_COLOR}\n"
  bin/rails db:schema:load
else
  printf "\n⏳${YELLOW}   [Release Phase]: Running database migrations.${NO_COLOR}\n"
  bin/rails db:migrate
fi

printf "\n🎉${GREEN}   [Release Phase]: Database is up to date.${NO_COLOR}\n"

Console after foreman start:

14:53:39 release.1 | started with pid 10418
14:53:39 web.1     | started with pid 10419
14:53:39 worker.1  | started with pid 10420
14:53:40 web.1     | Puma starting in single mode...
14:53:40 web.1     | * Version 4.3.5 (ruby 2.7.1-p83), codename: Mysterious Traveller
14:53:40 web.1     | * Min threads: 5, max threads: 5
14:53:40 web.1     | * Environment: development
14:53:44 web.1     | * Listening on tcp://0.0.0.0:5100
14:53:44 web.1     | Use Ctrl-C to stop
14:53:44 release.1 | 
14:53:44 release.1 | ⏳   [Release Phase]: Running database migrations.
14:53:44 worker.1  | 2020-09-25T19:53:44.905Z pid=10420 tid=arg INFO: Booted Rails 6.0.3.3 application in development environment
14:53:44 worker.1  | 2020-09-25T19:53:44.906Z pid=10420 tid=arg INFO: Running in ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin17]
14:53:44 worker.1  | 2020-09-25T19:53:44.907Z pid=10420 tid=arg INFO: See LICENSE and the LGPL-3.0 for licensing details.
14:53:44 worker.1  | 2020-09-25T19:53:44.907Z pid=10420 tid=arg INFO: Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org
14:53:44 worker.1  | 2020-09-25T19:53:44.907Z pid=10420 tid=arg INFO: Booting Sidekiq 6.1.2 with redis options {}
14:53:44 worker.1  | 2020-09-25T19:53:44.910Z pid=10420 tid=arg DEBUG: Client Middleware: 
14:53:44 worker.1  | 2020-09-25T19:53:44.910Z pid=10420 tid=arg DEBUG: Server Middleware: 
14:53:44 worker.1  | 2020-09-25T19:53:44.911Z pid=10420 tid=arg DEBUG: {:queues=>["critical", "default", "mailers"], :labels=>[], :concurrency=>10, :require=>".", :strict=>true, :environment=>nil, :timeout=>25, :poll_interval_average=>nil, :average_scheduled_poll_interval=>5, :error_handlers=>[#<Sidekiq::ExceptionHandler::Logger:0x00007ff9e2a150e8>], :death_handlers=>[], :lifecycle_events=>{:startup=>[], :quiet=>[], :shutdown=>[], :heartbeat=>[]}, :dead_max_jobs=>10000, :dead_timeout_in_seconds=>15552000, :reloader=>#<Sidekiq::Rails::Reloader @app=Teacher::Application>, :verbose=>true, :production=>{:concurrency=>25}, :staging=>{:concurrency=>15}, :config_file=>"./config/sidekiq.yml", :tag=>"teacher", :identity=>"me-MacBook-Air.local", :fetch=>#<Sidekiq::BasicFetch:0x00007ff9e906d430 @options={...}, @strictly_ordered_queues=true, @queues=["queue:critical", "queue:default", "queue:mailers", 2]>}
14:53:48 release.1 |    (0.3ms)  SELECT pg_try_advisory_lock(5720425725902639640)
14:53:48 release.1 |    (1.4ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
14:53:48 release.1 |   ActiveRecord::InternalMetadata Load (0.6ms)  SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2  [["key", "environment"], ["LIMIT", 1]]
14:53:48 release.1 |    (0.4ms)  SELECT pg_advisory_unlock(5720425725902639640)
14:53:48 release.1 |    (1.5ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
14:53:48 release.1 | 
14:53:48 release.1 | 🎉   [Release Phase]: Database is up to date.
14:53:48 release.1 | exited with code 0
14:53:48 system    | sending SIGTERM to all processes
14:53:48 web.1     | - Gracefully stopping, waiting for requests to finish
14:53:48 worker.1  | 2020-09-25T19:53:48.873Z pid=10420 tid=arg DEBUG: Got TERM signal
14:53:48 worker.1  | 2020-09-25T19:53:48.873Z pid=10420 tid=arg INFO: Shutting down
14:53:48 worker.1  | 2020-09-25T19:53:48.873Z pid=10420 tid=arg INFO: Terminating quiet workers
14:53:48 worker.1  | 2020-09-25T19:53:48.874Z pid=10420 tid=1qno INFO: Scheduler exiting...
14:53:48 web.1     | === puma shutdown: 2020-09-25 14:53:48 -0500 ===
14:53:48 web.1     | - Goodbye!
14:53:48 web.1     | terminated by SIGTERM
14:53:49 worker.1  | 2020-09-25T19:53:49.380Z pid=10420 tid=arg INFO: Bye!
14:53:49 worker.1  | exited with code 0

Solution

  • I believe the issue is that the Heroku release script you made is not meant to be used in a development environment. Heroku knows to handle that script as part of the build pipeline it's managing behind the scenes, but your foreman instance in development doesn't have any special handling in place to know what to do. Foreman is meant for long-running processes by default and what Heroku does to handle short one-off processes through a Procfile is custom for their platform.

    I'd suggest creating two different Procfiles, one for development and one for Production, as they're bound to differ over time. One setup I commonly use is:

    1. Procfile - default Production procfile
    2. Procfile.dev - The procfile I want to run in development.

    Your development procfile really only needs your long running processes to be in it, since you can manually run migrations(or your release script), without worrying about impacting anyone.

    To run a different Procfile locally(ie. Procfile.dev), you just need to run:
    foreman start -f Procfile.dev