I am running a puma server ruby application on fedora 32. In my server I have certain calls which will spawn new long running processes for various reasons. I came across an issue where my spawned processes were running and listening on the same port as my server. This lead to issues with restarting my server on deploys as the server could not start because of processes listening on the desired port. How could this be possible? From my understanding when I spawn a process it should have completely different memory to the parent process, and share no file descriptors. My spawn command is simply
my_pid = Process.spawn(my_cmd, %i[out err] => log_file)
Ruby version 2.7.0
Edit: something I had overlooked in my deploy process and my original problem description, server restart is not an actual tear down and restart of a new process, but via signalling USR2 to the puma server (as described here)
I believe I have found what is causing this. Seems to be an issue with puma restart process, which I was using. By restarting the server with a USR2
signal, it changes the flags on the open fd for the socket.
[me@home puma_testing]$ cat /proc/511620/fdinfo/5
pos: 0
flags: 02000002
mnt_id: 10
[me@home puma_testing]$ kill -s USR2 511620
[me@home puma_testing]$ cat /proc/511620/fdinfo/5
pos: 0
flags: 02
mnt_id: 10
This was tested on fedora 32 using a very simple puma and sinatra setup like so:
puma.rb
# frozen_string_literal: true
rackup File.join(File.dirname(File.realpath(__FILE__)), './server.ru')
# https://www.rubydoc.info/gems/puma/Puma/DSL#prune_bundler-instance_method
# This allows us to install new gems with just a phased-restart. Otherwise you
# need to take the master process down each time.
prune_bundler
port 11111
environment 'production'
pidfile File.join(File.dirname(File.realpath(__FILE__)), '../', 'server.pid')
tag 'test'
And server.ru
like so
require 'sinatra'
class App < Sinatra::Base
get "/" do
"Hello World!"
end
get "/spawn" do
spawn "sleep 500"
end
end
run App
Ran using bundler bundle exec puma -C puma.rb
. Note you can use /spawn
get request to test spawning a new process before and after restart to see if it is listening on the socket with lsof -itcp:11111