PG connection is in transaction but PostgreSQLAdapter in_transaction? method returns false

Instead of opening transaction through rails way , if I begin transaction using pg connection directly PostgreSQLAdapter#in_transaction? method returns false.

For example

adapter = ActiveRecord::Base.connection
con = adapter.raw_connection 
con.query("BEGIN")
adapter.in_transaction? #returns false

Reason because in_transaction? not actually checks out connection transaction status . Could we modify logic to check the connection transaction status ?

Something like this

def in_transaction?
   open_transactions > 0 || @connection.transaction_status == PG::PQTRANS_INTRANS
end
1 Like

Is there a real-world issue caused by the current implementation of in_transaction? The reason I ask is because in_transaction? is private to Rails which means it shouldn’t be used by applications directly and current implementation is sufficient for Rails needs since Rails controls in what context the method is being used.

Also the example is not executable as-is as it fails with

NoMethodError: private method `in_transaction?' called for #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter

We could use .send to have a working reproduction script but it’s just another sign that the method is private and not supposed to be used by an application.

Though speaking about hypothetical fix in case if in_transaction? was a public method I’d prefer open_transactions counter behavior to be changed rather than in_transaction? itself. Otherwise we may end up in situation where open_transactions is 0 while in_transaction returns true which doesn’t make sense

1 Like

Thanks for your quick response.

One scenario I could think of is Rails 7 load_async loads query in the foreground thread if connection is in transaction(instead of scheduling it in the background).

If in case I am executing begin command directly using pg connection, load_async method unable to identify connection in transaction state and as a result it schedules query in the background thread.

con = adapter.raw_connection
con.query("BEGIN")
p = Post.where("id is not null").load_async # Executes in background thread

Although this scenario is not linked to in_transaction? method, root cause is same as it doesn’t checks for connection’s transaction status. Thought like having this check would give us more confident on saying the transaction status.

1 Like