Instantly share code, notes, and snippets.

Embed
What would you like to do?
Simple, hot-restartable Ruby service
require 'socket'
class SuperSimpleService
attr_reader :bind_address, :bind_port
def initialize
@bind_address = 'localhost'
@bind_port = 12345
end
def run
if ENV['SOCKET_FD']
@socket_server = TCPServer.for_fd(ENV['SOCKET_FD'].to_i)
kill_parent
else
@socket_server = TCPServer.new(bind_address, bind_port)
end
@socket_server.autoclose = false
@socket_server.close_on_exec = false
setup_signal_traps
loop do
begin
client_socket = @socket_server.accept # blocks until a new connection is made
@connection_active = true
client_socket.puts "Hello World!"
client_socket.close
ensure
@connection_active = false
end
end
end
def setup_signal_traps
trap('USR1') { hot_restart }
trap('TERM') { graceful_shutdown }
end
def hot_restart
fork do
# :close_others ensures that open file descriptors are inherited by the new process
exec("SOCKET_FD=#{@socket_server.fileno} ruby super_simple_service.rb", close_others: false)
end
end
def graceful_shutdown
@socket_server.close # Stop listening for new connections
sleep 0.1 while @connection_active # Wait for active connection to complete
Process.exit(0)
end
def kill_parent
parent_process_id = Process.ppid
Process.kill('TERM', parent_process_id)
end
end
SuperSimpleService.new.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment