Unrepsonsive Process

Issue #240 wontfix
Tyler Hartland
created an issue

Locks seem to get the process into a state where it can't be interrupted. A test case is attached.

I first noticed the problem with "select ... for update" row-level locks, but switched the example to use an advisory lock to simplify.

The overall story is one thread grabs the lock, another waits on it. When the process gets INT, the thread with the lock seems to shutdown without releasing the lock. The remaining thread keeps waiting indefinitely, and the process doesn't respond to INT, QUIT, or TERM. It does still respond to USR1 which the example uses to dump thread backtraces.

postgres 9.5.1 on CentOS 7.1 in VirtualBox VM.

OS X 10.10.5 Ruby 2.3.1 PG 0.18.4 lib 9.4.4

Comments (5)

  1. Lars Kanis

    Thank you for reporting this issue! And sorry for responding so late!

    You may use the async_exec method instead of exec . exec doesn't process any signals while running, but async_exec does. See: https://deveiate.org/code/pg/PG/Connection.html#method-i-exec

    The reason exec doesn't respond to signals, is that the way how MRIs GVL works is not compatible with the way how libpq handles the network connection.

    However the good question is now: Why doesn't the locking thread execute the "rescue" path, when shutting down. Interestingly enough it executes the "ensure" path. So if I change the transaction method to this, it works:

      def transaction
        conn.exec('begin')
        in_transaction = true
        yield conn
        conn.exec('commit')
        in_transaction = false
      rescue Exception => e
        conn.exec('rollback')
        in_transaction = false
        raise e
      ensure
        conn.exec('rollback') if in_transaction
      end
    
  2. Lars Kanis

    To summarize, you have several options to solve the starvation:

    1. Use Signal.trap to catch the INT signal and send it to the threads, as you already did.
    2. Use async_exec instead of exec, so that the thread, which is waiting for the lock, can be stopped by the signal.
    3. Use the ensure block within the thread, to make sure the transaction is aborted in case the process ends.
    4. Use PG::Connection#transaction, which aborts transactions even in case the process ends.
  3. Log in to comment