Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PG.connect raising PG::ConnectionBad leaves behind open socket/file descriptor #439

Closed
bensheldon opened this issue Feb 24, 2022 · 2 comments · Fixed by #440
Closed

PG.connect raising PG::ConnectionBad leaves behind open socket/file descriptor #439

bensheldon opened this issue Feb 24, 2022 · 2 comments · Fixed by #440

Comments

@bensheldon
Copy link

When unable to connect to a non-running database server PG.connect initializes a PG::Connection but does not clean it up. This leaves behind an open socket and file descriptor that are not closed until the Ruby garbage collector runs.

(I'm the maintainer of GoodJob, and a user reported running out of fds because of a bad connection: bensheldon/good_job#530)

I put together a quick script to reproduce the behavior:

# script.rb

require 'pg'

puts "pg: #{PG::VERSION}"
puts "ruby: #{RUBY_VERSION}"

def show_lsof
  output = `lsof -p #{Process.pid} | grep localhost:postgresql`
  puts output.size > 0 ? "lsof: #{output}" : "lsof: [empty]"
end

begin
  puts "Connecting to non-running database host..."
  PG.connect({ dbname: 'foo', host: 'localhost' })
rescue PG::ConnectionBad => e
  puts "#{e.class}: #{e}"
end

# Running the Garbage Collector frees everything
# GC.start

# find the unreferenced PG::Connection
conn = ObjectSpace.each_object(PG::Connection).first
if conn
  puts "Unreferenced PG::Connection: #{conn}"
else
  puts "Cannot find PG::Connection"
end

socket = conn.socket_io
puts "Socket closed? #{socket.closed?}" #=> false
puts "IO closed? #{IO.for_fd(conn.socket).closed?}" #=> false
show_lsof #=> "ruby    2712 bensheldon   13u   IPv6 0x9b033007e1a875d7         0t0                 TCP localhost:54792->localhost:postgresql (CLOSED)"

puts "-- Closing PG::Connection --"
conn.close

puts "Socket closed? #{socket.closed?}" #=> true
show_lsof #=> "lsof: [empty]"

Resulting in...

❯ ruby script.rb
pg: 1.3.1
ruby: 3.0.3
Connecting to non-running database host...
PG::ConnectionBad: connection to server at "::1", port 5432 failed: Connection refused
	Is the server running on that host and accepting TCP/IP connections?
Unreferenced PG::Connection: #<PG::Connection:0x0000000152248620>
Socket closed? false
IO closed? false
lsof: ruby    4049 bensheldon  11u   IPv6 0x9b033007e08245b7       0t0                 TCP localhost:55013->localhost:postgresql (CLOSED)
-- Closing PG::Connection --
Socket closed? true
lsof: [empty]
larskanis added a commit to larskanis/ruby-pg that referenced this issue Feb 24, 2022
larskanis added a commit to larskanis/ruby-pg that referenced this issue Feb 24, 2022
Previously it was kept open until the PG::Connection was garbage collected.

Fixes ged#439
@larskanis
Copy link
Collaborator

Thank you @bensheldon for filing this issue and providing this very useful repro script! I first assumed that this resource leak is due to changes from pg-1.2 to 1.3, but your script shows the same behavior in pg-1.0.0.

I made a proposal in #440 to fix this issue. Could you @adibsaad please give it a try?

@bensheldon
Copy link
Author

@larskanis thank you 🙌 I tried it locally and it looks good. The IO objects are being closed, and I no longer see an accumulation of open file descriptors 👍 🎉

And I'm a bit surprised that this hasn't been noticed before, so yay to progress!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants