Justinas Jakavonis
Justinas Jakavonis

Reputation: 8818

'idle in transaction' when using Hibernate, Postgres and Guice Provider

When I execute:

select * from pg_stat_activity where state ~ 'idle in transact'

I get inappropriate number of rows with state 'idle in transaction'. Some of them idle for a few days. Most of them, are the same simple select query which are executed from one service class (Hibernate 5.1.0.Final, Guice 4.1.0):

public class FirebaseServiceImpl implements FirebaseService {

    @Inject
    private Provider<FirebaseKeyDAO> firebaseKeyDAO;

    @Override
    public void sendNotification(User recipient) {

        List<FirebaseKey> firebaseKeys = firebaseKeyDAO.get().findByUserId(recipient.getId());

        final ExecutorService notificationsPool = Executors.newFixedThreadPool(3);

        for (FirebaseKey firebaseKey : firebaseKeys)
            notificationsPool.execute(new Runnable() {

                 @Override
                 public void run() {
                    sendNotification(new FirebaseNotification(firebaseKey.getFirebaseKey(), "example");
                 }
        });

        notificationsPool.shutdown();
    }
}

DAO method:

@Override
@SuppressWarnings("unchecked")
public List<FirebaseKey> findByUserId(Long userId) {
    Criteria criteria = getSession().createCriteria(type);
    criteria.add(Restrictions.eq("userId", userId));
    return criteria.list();
}

Why does it happen? How to avoid this?

UPDATE

Transactions are not commited when I use Guice Provider exampleDAO.get() in a separate thread:

@Inject
Provider<ExampleDAO> exampleDAO;

Upvotes: 1

Views: 2492

Answers (1)

Vao Tsun
Vao Tsun

Reputation: 51519

It usually happens when you use pgbouncer or other pooler/session manager that uses pool_mode = transaction. Eg when client opens a transaction and holds it, not committing nor rolling back. Check if you see DISCARD ALL in query column - if you do this is the case, because pooler has to discard shared session plans, sequences, deallocate statements etc to avoid mixing those for different sessions in pool.

On the other hand any "normal" transaction gives same idle in transaction, eg:

2>select now(),pg_backend_pid();
               now                | pg_backend_pid
----------------------------------+----------------
 2017-05-05 16:53:01.867444+05:30 |          26500
(1 row)

if we check its state we see orthodox idle:

t=# select query,state from pg_stat_activity where pid = 26500;
             query              | state
--------------------------------+-------
 select now(),pg_backend_pid(); | idle
(1 row)

now we start transaction on session 2 >: 2>begin; BEGIN

2>select now(),pg_backend_pid();
               now                | pg_backend_pid
----------------------------------+----------------
 2017-05-05 16:54:15.856306+05:30 |          26500
(1 row)

and check pg_stat_statements gain:

t=# select query,state from pg_stat_activity where pid = 26500;
             query              |        state
--------------------------------+---------------------
 select now(),pg_backend_pid(); | idle in transaction
(1 row)

It will remain this way until statement timeout or end of transaction:

2>end;
COMMIT
t=# select query,state from pg_stat_activity where pid = 26500;
 query | state
-------+-------
 end;  | idle
(1 row)

So it is quite common and ok to have it. If you want to avoid connected sessions, you have to disconnect client. But connection in postgres is expensive, so usually people try reuse existing connections with pool, and so such states appear in pg_stat_activity

Upvotes: 2

Related Questions