简体   繁体   中英

Deadlock while using ORMLite

I have a multithreaded server Java application which receives requests and does queries/updates to a Postgres DB through OrmLite. Under load, several requests come in which are interested in the same DB row. Thread1 might select , change values and then update . At the same time Thread2 tries something similar. This is currently not synchronized and not done inside a transaction. Without surprise, the update of Thread1 , might not be seen by Thread2 . That's OK ( Thread2 can overwrite results from Thread1 ) and is not my problem.

However, when running this application for some time, I get to a deadlock situation, which results in all available DB connections being used up (and then crash). It seems it is not a standard deadlock (with a circular lock dependency), instead most threads are waiting on a lock, and the thread holding this lock seems to be waiting for a socket read (which probably does not happen, see below).

Using

  • OrmLite 5.1,
  • JVM is Java 1.8.0_251 Hotspot Client VM,
  • Postgres JDBC 42.2.9

How should I go forward to fix this?

Below are relevant parts of the thread dump (analyzed by https://spotify.github.io/threaddump-analyzer )

The thread holding the main lock ( 0x00000000c0179e18 ), seems to be waiting on a socket:

"RaspService-2089": running, holding [0x00000000c0179e18, 0x00000000c2c1f6c0]
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:171)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at org.postgresql.core.VisibleBufferedInputStream.readMore(VisibleBufferedInputStream.java:140)
    at org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:109)
    at org.postgresql.core.VisibleBufferedInputStream.read(VisibleBufferedInputStream.java:67)
    at org.postgresql.core.PGStream.receiveChar(PGStream.java:335)
    at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:505)
    at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:141)
    at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:192)
    at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
    at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:211)
    at org.postgresql.Driver.makeConnection(Driver.java:458)
    at org.postgresql.Driver.connect(Driver.java:260)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at java.sql.DriverManager.getConnection(DriverManager.java:208)
    at com.j256.ormlite.jdbc.JdbcConnectionSource.makeConnection(JdbcConnectionSource.java:266)
    at com.j256.ormlite.jdbc.JdbcPooledConnectionSource.getReadWriteConnection(JdbcPooledConnectionSource.java:140)
    at com.j256.ormlite.dao.BaseDaoImpl.update(BaseDaoImpl.java:408)
    at vgs.vigi.servlet.OrmLite.update(OrmLite.java:361)
    at vgs.vigi.servlet.CachedDao.update(CachedDao.java:287)
    at vgs.vigi.ble.RaspClient.run(RaspClient.java:177)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

26 threads waiting to free connections wait on that lock with stacks like:

"pool-4-thread-96": waiting to acquire [0x00000000c0179e18], holding [0x00000000c0b250a8]
    at com.j256.ormlite.jdbc.JdbcPooledConnectionSource.releaseConnection(JdbcPooledConnectionSource.java:168)
    at com.j256.ormlite.dao.BaseDaoImpl.create(BaseDaoImpl.java:331)
    at vgs.vigi.servlet.OrmLite.create(OrmLite.java:181)
    at vgs.vigi.servlet.CachedDao.create(CachedDao.java:126)
    at vgs.vigi.logic.Notification.sendNotification(Notification.java:491)
    at vgs.vigi.logic.Notification$1.run(Notification.java:640)
    at vgs.lib.MyTimer$2.run(MyTimer.java:103)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

More threads waiting to release connections

"RaspService-828": waiting to acquire [0x00000000c0179e18], holding [0x00000000c1187f88]
    at com.j256.ormlite.jdbc.JdbcPooledConnectionSource.releaseConnection(JdbcPooledConnectionSource.java:168)
    at com.j256.ormlite.dao.BaseDaoImpl.update(BaseDaoImpl.java:412)
    at vgs.vigi.servlet.OrmLite.update(OrmLite.java:361)
    at vgs.vigi.servlet.CachedDao.update(CachedDao.java:287)
    at vgs.vigi.ble.CmdRaspExcutor$8.exec(CmdRaspExcutor.java:318)
    at vgs.vigi.ble.RaspClient.run(RaspClient.java:182)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

While many also try to acquire connections:

"RaspService-991": waiting to acquire [0x00000000c0179e18], holding [0x00000000c1624058]
    at com.j256.ormlite.jdbc.JdbcPooledConnectionSource.getReadWriteConnection(JdbcPooledConnectionSource.java:125)
    at com.j256.ormlite.dao.BaseDaoImpl.update(BaseDaoImpl.java:408)
    at vgs.vigi.servlet.OrmLite.update(OrmLite.java:361)
    at vgs.vigi.servlet.CachedDao.update(CachedDao.java:287)
    at vgs.vigi.ble.RaspClient.run(RaspClient.java:177)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Some GC is happening ( inconsistent means Thread is "BLOCKED (on object monitor)" without waiting for anything)

"qtp1719311117-931": inconsistent?, holding [0x00000000eabfc510]
    at java.lang.Runtime.gc(Native Method)
    at java.lang.System.gc(System.java:993)
    at vgs.vigi.servlet.OrmLite.clearCache(OrmLite.java:33)
    at vgs.vigi.servlet.OrmLite.dao(OrmLite.java:215)
    at vgs.vigi.servlet.OrmLite.getAll(OrmLite.java:300)
    at vgs.vigi.servlet.CachedDao.getAll(CachedDao.java:227)
    at vgs.lib.Ajax.sGetAll(Ajax.java:101)
    ...

And also GC in another thread (which is explicitely coded in our code - not sure why though)

"RaspService-1882": running, holding [0x00000000c05c84c8, 0x00000000c2bed7b0]
    at java.lang.Runtime.gc(Native Method)
    at java.lang.System.gc(System.java:993)
    at vgs.vigi.servlet.OrmLite.clearCache(OrmLite.java:33)
    at vgs.vigi.servlet.OrmLite.dao(OrmLite.java:215)
    at vgs.vigi.servlet.OrmLite.update(OrmLite.java:360)
    at vgs.vigi.servlet.CachedDao.update(CachedDao.java:285)
    at vgs.vigi.ble.RaspClient.run(RaspClient.java:177)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Can I expect OrmLite to be safe with a multithreaded approach as described?

Are there best practices to avoid this issue (while still keeping the multithreaded nature of the server)?


Update

I have a thread dump of a second run, which looks a bit different.

Here the thread holding the lock that everyone is waiting for is inconsistent

"RaspService-1405": inconsistent?, holding [0x00000000c01da9b8, 0x00000000c1cfed28]

With a raw stack of:

"RaspService-1405" #1469 prio=5 os_prio=0 tid=0x0000000021579800 nid=0xa2f4 waiting for monitor entry [0x000000002b36e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.j256.ormlite.jdbc.JdbcPooledConnectionSource.getReadWriteConnection(JdbcPooledConnectionSource.java:125)
    - locked <0x00000000c01da9b8> (a java.lang.Object)
    at com.j256.ormlite.dao.BaseDaoImpl.update(BaseDaoImpl.java:408)
    at vgs.vigi.servlet.OrmLite.update(OrmLite.java:361)
    at vgs.vigi.servlet.CachedDao.update(CachedDao.java:287)
    at vgs.vigi.ble.RaspClient.run(RaspClient.java:177)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
    - <0x00000000c1cfed28> (a java.util.concurrent.ThreadPoolExecutor$Worker)

There is also a RUNNING thread which reads from a connection. Not sure whether that is blocked:

"RaspService-1410": running, holding [0x00000000ed1877c8, 0x00000000c1cfe208]
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:171)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at org.postgresql.core.VisibleBufferedInputStream.readMore(VisibleBufferedInputStream.java:140)
    at org.postgresql.core.VisibleBufferedInputStream.ensureBytes(VisibleBufferedInputStream.java:109)
    at org.postgresql.core.VisibleBufferedInputStream.read(VisibleBufferedInputStream.java:67)
    at org.postgresql.core.PGStream.receiveChar(PGStream.java:335)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2008)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:310)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:447)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:368)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:158)
    at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:124)
    at com.j256.ormlite.jdbc.JdbcDatabaseConnection.update(JdbcDatabaseConnection.java:294)
    at com.j256.ormlite.jdbc.JdbcDatabaseConnection.update(JdbcDatabaseConnection.java:217)
    at com.j256.ormlite.stmt.mapped.MappedUpdate.update(MappedUpdate.java:101)
    at com.j256.ormlite.stmt.StatementExecutor.update(StatementExecutor.java:472)
    at com.j256.ormlite.dao.BaseDaoImpl.update(BaseDaoImpl.java:410)
    at vgs.vigi.servlet.OrmLite.update(OrmLite.java:361)
    at vgs.vigi.servlet.CachedDao.update(CachedDao.java:287)
    at vgs.vigi.ble.RaspClient.run(RaspClient.java:177)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

Update 2:

Here's a graph of sessions as seen from pgAdmin在此处输入图像描述

seems to be waiting on a socket:

It waits until new connection is created, also holding lock inside pool.

Do you have session limit in Postgres? If you do I suggest you set it slightly bigger than pool size in Java.

Otherwise, it is easy to have deadlock if pool size is equal to sessions limit size

  1. All connections are taken (Java pool limit is reached, session count limit is reached)
  2. Application tries to get new connection, takes pool lock and blocked by PG
  3. Application tries to release connection, it cannot take pool lock so cannot release connection to PG, so session limit is still reached

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM