rubyにおけるソケットのaccept_nonblockの競合
Rubyで1つのサーバーソケットを2つのプロセスから同時にaccept_nonblockするとどうなるか。サーバーのgraceful restartでよくありそうなシチュエーションを試してみる。
実験コード:
require 'io/wait' require 'socket' s = TCPServer.new(0) p [ :listen, s ] fork{ begin p [ $$, :wait, s.wait_readable(3600) ] p [ $$, :accept, s.accept_nonblock ] rescue p [ $$, $! ] end } fork{ begin p [ $$, :wait, s.wait_readable(3600) ] p [ $$, :accept, s.accept_nonblock ] rescue p [ $$, $! ] end } sleep 1 fork{ TCPSocket.new('localhost', s.local_address.ip_port) } Process.waitall
結果:
$ ruby sock_accept_nonblock_dup.rb [:listen, #<TCPServer:fd 5, AF_INET, 0.0.0.0, 45821>] [13290, :wait, #<TCPServer:fd 5, AF_INET, 0.0.0.0, 45821>] [13291, :wait, #<TCPServer:fd 5, AF_INET, 0.0.0.0, 45821>] [13291, :accept, #<TCPSocket:fd 6, AF_INET, 127.0.0.1, 45821>] [13290, #<IO::EAGAINWaitReadable: Resource temporarily unavailable - accept(2) would block>]
やはり片方が成功しもう片方はリトライしなければならないようだ。