No PID file created when starting Puma as daemon - ruby-on-rails

I am working on getting my Rails app deployed using Nginx as a reverse proxy. Everything work correctly when starting the app manually using rails s to launch it. All the proper PIDs are created in the tmp/pids/ directory (puma.pid, puma.state, andserver.pid) and the puma.sock is properly created in the tmp/sockets/ directory.
When I attempt to start the same app using rails s -d, to start it as a daemon, everything start normally except the tmp/pids/puma.pid is nowhere to be found which causes my reverse proxy to break. I'll paste a copy of my puma.conf below.
Using:
puma 3.12.6 and rails 5.2.6
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
workers ENV.fetch("WEB_CONCURRENCY") { 4 }
preload_app!
plugin :tmp_restart
# Prep for Nginx integration
app_dir = File.expand_path("../..", __FILE__)
tmp_dir = "#{app_dir}/tmp"
bind "unix://#{tmp_dir}/sockets/puma.sock"
pidfile "#{tmp_dir}/pids/puma.pid"
state_path "#{tmp_dir}/pids/puma.state"
activate_control_app

It turns out that the problem occurs when launching the Rails server and using the -d switch to daemonize (which I was doing) like this:
rails s -d
However, if I add daemonize true to the puma.conf everything works as expected. So, now I launch the server using rails s with the following puma.conf and the missing puma.pid appears!
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
workers ENV.fetch("WEB_CONCURRENCY") { 4 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
preload_app!
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
# Prep for Nginx integration
app_dir = File.expand_path("../..", __FILE__)
tmp_dir = "#{app_dir}/tmp"
bind "unix://#{tmp_dir}/sockets/puma.sock"
pidfile "#{tmp_dir}/pids/puma.pid"
state_path "#{tmp_dir}/pids/puma.state"
# Run Puma as a daemon
daemonize true
activate_control_app

to config/puma.rb
add pidfile "tmp/pids/server.pid"

Related

Non thread safe Rails 5 App in elasticbeanstalk

I am deploying my Rails 5.2 app in elastic beanstalk with puma as application server and Nginx as default by Elastic Beanstalk.
I am facing an issue of a race condition. After I check more details in container instance I found this:
#example /opt/elasticbeanstalk/support/conf/pumaconf.rb
directory '/var/app/current'
threads 8, 32
workers %x(grep -c processor /proc/cpuinfo)
bind 'unix:///var/run/puma/my_app.sock'
pidfile '/var/run/puma/puma.pid'
stdout_redirect '/var/log/puma/puma.log', '/var/log/puma/puma.log', true
daemonize false
As seen here the number of workers is equal to the number of my CPU core.
However, in Heroku.com we can do this:
# config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count
preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
on_worker_boot do
# Worker specific setup for Rails 4.1+
# See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot
ActiveRecord::Base.establish_connection
end
How can I lower down the number of threads and increase the number of workers in elastic beanstalk? by taking into account that I have a load balancer enabled and the config above is managed by elastic beanstalk.
In the case of Heroku I can manage with puma.rb, however in elastic beanstalk I don't see any other approach besides changing the file
/opt/elasticbeanstalk/support/conf/pumaconf.rb
manually. Manually modification will cause issues when the number of instances scaling down or up.
Not sure, if you've resolved your issue. I had a similar issue and I resolved it using the .ebextensions.
You can create a new pumaconf.rb file on your config directory in your code. Then in the .ebextensions directory, create a file that will copy out the new pumaconf.rb and replace the default pumaconf.rb.
Also, if you are going about it this way. In your .ebextensions code, use this path for your new file
/var/app/ondeck/config/pumaconf.rb
and not
/var/app/current/config/pumaconf.rb
Because using the latter one, wont copy out your latest pumaconf.rb
cp /var/app/ondeck/config/pumaconf.rb /opt/elasticbeanstalk/support/conf/pumaconf.rb

Puma Rails 5 binding.pry only available for 60 seconds before timeout

Puma times out my request when I'm using binding.pry. In my controller
def new
require 'pry'
binding.pry
end
I then make a request that hits the controller and enter the pry session. After 60 seconds Puma? times out my request, restarts a worker and subsequently blows up by debugging session.
[1] pry(#<Agent::ClientsController>)> [3522] ! Terminating timed out worker: 3566
[3522] - Worker 0 (pid: 4171) booted, phase: 0
I generated this app with suspenders if that matters. How do I extend my debugging session in rails 5?
How about this?
# config/puma.rb
...
environment ENV['RACK_ENV'] || 'development'
...
if ENV['RACK_ENV'] == 'development'
worker_timeout 3600
end
Edit (Rails 5.1.5):
Because ENV['RACK_ENV'] was empty, I did the following:
# config/puma.rb
...
if ENV.fetch('RAILS_ENV') == 'development'
puts "LOGGER: development => worker_timeout 3600"
worker_timeout 3600
end
You can create a configuration file and set the timeout value in there (for all requests, not just ones involved in debugging). I'd recommend making a dev-specific one, and referencing that when running the server locally (and not setting some big timeout value for production).
In your rails application, create a file like /config/dev_puma_config.rb and in it put:
#!/usr/bin/env puma
worker_timeout 3600
Then when you start your server, reference that file with a -C like this:
bundle exec puma -t 1:1 -w 1 -p 3000 -e development -C config/dev_puma_config.rb
As a bit of background info on that worker_timeout setting, here's what the puma config says about it:
Verifies that all workers have checked in to the master process within
the given timeout. If not the worker process will be restarted. This
is not a request timeout, it is to protect against a hung or dead
process. Setting this value will not protect against slow requests.
Default value is 60 seconds.

How to pass Java options to Puma daemon?

I have a Rails application running on jruby-9.0.4.0 and using Puma as the web server.
I am trying to increase the memory limit of puma; the current flags are -Xmx500m -Xss2048k. I added _JAVA_OPTIONS=-Xss4096k -Xmx2048m to my env and all the jruby processes use it like rake assets:precompile for example but the puma instance itself does not.
Part of capistrano trace
DEBUG [0aec947c] Command: cd
/dummy/production/releases/20160707071111 && (
export RBENV_ROOT="/usr/local/rbenv" RBENV_VERSION="jruby-9.0.4.0"
RAILS_ENV="production" ; /usr/local/rbenv/bin/rbenv exec bundle exec
rake assets:precompile )
DEBUG [0aec947c] Picked up _JAVA_OPTIONS: -Xss4096k -Xmx2048m
config/puma.rb
# Min and Max threads per worker
threads 8, 512
# Default to production
rails_env = ENV['RAILS_ENV'] || 'production'
environment rails_env
app_dir = "/dummy/#{rails_env}/current"
# Set up socket location
bind "tcp://localhost:3000"
# Logging
stdout_redirect "#{app_dir}/log/puma.stdout.log", "#{app_dir}/log/puma.stderr.log", true
# Set master PID and state locations
pidfile "#{app_dir}/pids/puma.pid"
state_path "#{app_dir}/pids/puma.state"
activate_control_app
ENV variables
JAVA_OPTS=-Xss4096k -Xmx2048m
_JAVA_OPTIONS=-Xss4096k -Xmx2048m
JAVA_HOME=/usr/lib/jvm/java-7-oracle-amd64
Output of cat /proc/<pid>/environ
RBENV_ROOT=/usr/local/rbenvprevious=NUPSTART_JOB=rcPATH=/dummy/production/shared/bundle/jruby/2.2.0/bin:/usr/local/rbenv/versions/jruby-9.0.4.0/bin:/usr/local/rbenv/libexec:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/binPWD=/dummy/production/releases/20160707133222RBENV_DIR=/dummy/production/releases/20160707133222SUPERVISOR_SERVER_URL=unix:///var/run/supervisor.sockUPSTART_EVENTS=runlevelRUNLEVEL=2NLSPATH=/usr/dt/lib/nls/msg/%L/%N.catSUPERVISOR_PROCESS_NAME=dummyprocessSUPERVISOR_ENABLED=1XFILESEARCHPATH=/usr/dt/app-defaults/%L/DtSHLVL=0UPSTART_INSTANCE=PREVLEVEL=NRBENV_VERSION=jruby-9.0.4.0RBENV_HOOK_PATH=:/usr/local/rbenv/rbenv.d:/usr/local/etc/rbenv.d:/etc/rbenv.d:/usr/lib/rbenv/hooksrunlevel=2SUPERVISOR_GROUP_NAME=dummyprocessTERM=linuxRUBYOPT=-rbundler/setupRUBYLIB=/usr/local/rbenv/versions/jruby-9.0.4.0/lib/ruby/gems/shared/gems/bundler-1.11.2/libroot
Machine is an 8-core with 24GB of RAM.
How can I let the puma instance also pick up the Java options?
As mentionned in this blog post, you can put the JVM options in JRUBY_OPTS environment variable.
In your case, you could set the variable before starting Puma daemon :
export JRUBY_OPTS="-J-Xss4096k -J-Xmx2048m"
You may also try configuring the JVM directly by setting JAVA_OPTS environment variable :
export JAVA_OPTS="-Xss4096k -Xmx2048m"
Also check how the puma daemon is started
cat /proc/<pid>/environ
Your environment is not propagated to the Puma daemon. You need to find out how Puma is started. It may be as an init.d service or an upstart service.
Update:
It seems you can set your environment with rbenv-vars.
Create a .rbenv-vars file in your Rails project with the following
JAVA_OPTS='-Xss4096k -Xmx2048m'
Then your environment should be propagated to the puma daemon.
https://devcenter.heroku.com/articles/optimizing-dyno-usage#basic-methodology-for-optimizing-memory
JRuby
JRuby servers like Puma make good use of concurrency without the need
for multiple processes. However, you will need to tune the amount of
memory allocated to the JVM, depending on the dyno type. The Ruby
buildpack defines sensible defaults, which can be overridden by
setting either JAVA_OPTS or JRUBY_OPTS.

Different Log File for different rails service?

I've a rails service which I am starting up with Puma. I am using
bundle exec puma -C config/puma.rb -p 3000. However I've to start a new service on a different port let's say 3001. So, I've a different puma2.rb which I am starting with bundle exec puma -C config/puma2.rb -p 3001.
Both of these services have a common log file which is development.log. I want to seperate these log files say development-3000.log & development-3001.log.
I've tried
stdout_redirect "#{Dir.pwd}/log/puma.stdout.log", "#{Dir.pwd}/log/puma.stderr.log"
for individual puma files but this logs only the requests. I want the log to be of log_level : debug. How can I achieve this?
Here is my puma config file:
app_path=File.expand_path('../', _ _FILE_ _)
tmp_dir= "#{app_path}/../tmp"
pidfile "#{tmp_dir}/pid"
state_path "#{tmp_dir}/state"
threads 8,32
workers 2
activate_control_app
One way to get separate logs would be to setup a separate rails environment. You could make a copy of the config/development.rb file and name it development2.rb, and then set the environment in the puma2.rb file to development2.
# puma2.rb
environment "development2"
That environment would write to development2.log

How do I use puma's configuration file?

I was following this guide it documents the puma.rb file that is stored inside the app's config directory.
The guide is a bit flakey, but here's what I assume the puma.rb file does. Instead of running crazy commands such as this to get puma running on a specified socket:
bundle exec puma -e production -b unix:///var/run/my_app.sock
You can just specify the port, pid, session and other parameters in the puma.rb file like this:
rails_env = ENV['RAILS_ENV'] || 'production'
threads 4,4
bind "/home/starkers/Documents/alpha/tmp/socket"
pidfile "/home/starkers/Documents/alpha/tmp/pid"
state_path "/home/starkers/Documents/alpha/tmp/state"
activate_control_app
And then you could cd into the app's root and run a simple command like
'puma'
and the parameters set in puma.rb would be followed. Unfortunately that doesn't seem to work for me.
At least, I ran puma inside the root of a tiny test app, and no .sock file appeared in
/home/starkers/Documents/alpha/tmp/sockets so does that mean it isn't working?
How do I get this working? I am on a local development machine, so could that cause this error somehow? Is there a parameter I need to pass in when running
puma ?
I was also stuck trying to find documentation on the config file for puma but I did find the all-in-one config.ru file useful. I've formatted it here for future reference:
# The directory to operate out of.
# The default is the current directory.
directory '/u/apps/lolcat'
# Load “path” as a rackup file.
# The default is “config.ru”.
rackup '/u/apps/lolcat/config.ru'
# Set the environment in which the rack's app will run. The value must be a string.
# The default is “development”.
environment 'production'
# Daemonize the server into the background. Highly suggest that
# this be combined with “pidfile” and “stdout_redirect”.
# The default is “false”.
daemonize
daemonize false
# Store the pid of the server in the file at “path”.
pidfile '/u/apps/lolcat/tmp/pids/puma.pid'
# Use “path” as the file to store the server info state. This is
# used by “pumactl” to query and control the server.
state_path '/u/apps/lolcat/tmp/pids/puma.state'
# Redirect STDOUT and STDERR to files specified. The 3rd parameter
# (“append”) specifies whether the output is appended, the default is
# “false”.
stdout_redirect '/u/apps/lolcat/log/stdout', '/u/apps/lolcat/log/stderr'
stdout_redirect '/u/apps/lolcat/log/stdout', '/u/apps/lolcat/log/stderr', true
# Disable request logging.
# The default is “false”.
quiet
# Configure “min” to be the minimum number of threads to use to answer
# requests and “max” the maximum.
# The default is “0, 16”.
threads 0, 16
# Bind the server to “url”. “tcp://”, “unix://” and “ssl://” are the only
# accepted protocols.
# The default is “tcp://0.0.0.0:9292”.
bind 'tcp://0.0.0.0:9292'
bind 'unix:///var/run/puma.sock'
bind 'unix:///var/run/puma.sock?umask=0777'
bind 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'
# Listens on port 7001
# The default is 9292
port 7001
# Instead of “bind 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'” you
# can also use the “ssl_bind” option.
ssl_bind '127.0.0.1', '9292', { key: path_to_key, cert: path_to_cert }
# Code to run before doing a restart. This code should
# close log files, database connections, etc.
# This can be called multiple times to add code each time.
on_restart do
puts 'On restart...'
end
# Command to use to restart puma. This should be just how to
# load puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
# to puma, as those are the same as the original process.
restart_command '/u/app/lolcat/bin/restart_puma'
# === Cluster mode ===
# How many worker processes to run.
# The default is “0”.
workers 2
# Code to run when a worker boots to setup the process before booting
# the app.
# This can be called multiple times to add hooks.
on_worker_boot do
puts 'On worker boot...'
end
# === Puma control rack application ===
# Start the puma control rack application on “url”. This application can
# be communicated with to control the main server. Additionally, you can
# provide an authentication token, so all requests to the control server
# will need to include that token as a query parameter. This allows for
# simple authentication.
# Check out https://github.com/puma/puma/blob/master/lib/puma/app/status.rb
# to see what the app has available.
activate_control_app 'unix:///var/run/pumactl.sock'
activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' }
activate_control_app 'unix:///var/run/pumactl.sock', { no_token: true }
Those settings would then go in a ruby file (e.g. config/puma.rb) and then as Starkers says, you can run it with
puma -C config/puma.rb
Update: The original answer is no longer correct for Puma versions since 2019: Puma added a fallback mechanism, so both locations are checked now. ( https://github.com/puma/puma/pull/1885)
Puma first looks for configuration at config/puma/<environment_name>.rb, and then falls back to config/puma.rb.
Outdated answer:
If there is an environment defined - which is the case in your example - the configuration file is read from config/puma/[environment].rb and not config/puma.rb.
Just move your config/puma.rb to config/puma/production.rb and it should work.
Read the Puma documentation for more details: Configuration file
This will work:
puma -C config/puma.rb
You need to tell puma where to find your rackup file you can do it by putting this in your config:
rackup DefaultRackup
It looks like a fix for this is merged into master: https://github.com/puma/puma/pull/271

Resources