Тупик при использовании ORMLite - PullRequest
0 голосов
/ 27 мая 2020

У меня есть приложение с многопоточным сервером Java, которое получает запросы и выполняет запросы / обновления к Postgres БД через OrmLite. Под нагрузкой приходит несколько запросов, интересующихся одной и той же строкой БД. Thread1 может select, изменить значения, а затем update. В то же время Thread2 пробует нечто подобное. В настоящее время это не синхронизируется и не выполняется внутри транзакции. Неудивительно, что обновление Thread1 может не отображаться Thread2. Это нормально (Thread2 может перезаписывать результаты из Thread1), и это не моя проблема.

Однако при запуске этого приложения в течение некоторого времени я попадаю в тупиковую ситуацию, которая приводит ко всем доступным соединениям с БД израсходовано (а затем cra sh). Кажется, что это не стандартный тупик (с зависимостью круговой блокировки), вместо этого большинство потоков ожидают блокировки, а поток, удерживающий эту блокировку, похоже, ожидает чтения сокета (что, вероятно, не происходит, см. Ниже).

Использование

  • OrmLite 5.1,
  • JVM - это Java 1.8.0_251 Клиентская виртуальная машина точки доступа,
  • Postgres JDB C 42.2.9

Как мне go переслать, чтобы исправить это?

Ниже приведены соответствующие части дампа потока (проанализированы https://spotify.github.io/threaddump-analyzer)

Поток, удерживающий основную блокировку (0x00000000c0179e18), кажется, ожидает сокета:

"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 потоков, ожидающих освобождения соединений, ждут этого блокировка со стеками, например:

"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)

Больше потоков, ожидающих освобождения соединений

"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)

Хотя многие также пытаются получить соединения:

"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)

Некоторые G C происходит (inconsistent означает, что поток «ЗАБЛОКИРОВАН (на мониторе объекта)», ничего не дожидаясь)

"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)
    ...

И Так же G C в другом потоке (который явно закодирован в нашем коде - не знаю почему)

"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)

Могу ли я ожидать, что OrmLite будет безопасным с многопоточным подходом, как описано?

Есть ли передовые методы, позволяющие избежать этой проблемы (при сохранении многопоточной природы сервера)?


Обновление

У меня есть дамп потока второго прогона, который выглядит немного по-другому.

Здесь поток, удерживающий блокировку, которую все ждут, это несогласованный

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

С необработанным стеком:

"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)

Существует также RUNNING thread, который читает из соединения. Не уверен, что это заблокировано:

"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)

Обновление 2:

Вот график сеансов, как видно из pgAdmin enter image description here

1 Ответ

0 голосов
/ 27 мая 2020

, похоже, ожидает сокета:

Он ждет, пока не будет создано новое соединение, а также lock внутри пула.

Есть ли у вас ограничение на количество сеансов в Postgres? Если вы это сделаете, я предлагаю вам установить его немного больше, чем размер пула в Java.

В противном случае легко получить взаимоблокировку, если размер пула равен размеру лимита сеансов

  1. Все соединения принимаются (Java лимит пула достигнут, лимит количества сеансов достигнут )
  2. Приложение пытается установить новое соединение, принимает блокировку пула и блокируется PG
  3. Приложение пытается разорвать соединение, оно не может принять блокировку пула, поэтому не может освободить соединение с PG, поэтому ограничение сеанса все еще достигли
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...