it-swarm.com.ru

Как установить время ожидания блокировки в postgres - Hibernate

Я пытаюсь установить Lock для строки, над которой я работаю, до следующего коммита:

entityManager.createQuery("SELECT value from Table where id=:id")
            .setParameter("id", "123")
            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
            .setHint("javax.persistence.lock.timeout", 10000)
            .getSingleResult();

Я подумал, что должно произойти, что если два потока попытаются записать в БД одновременно, один поток достигнет операции обновления раньше другого, второй поток должен подождать 10 секунд и затем выбросить PessimisticLockException .

Но вместо этого поток зависает до завершения другого потока, независимо от установленного времени ожидания.

Посмотрите на этот пример:

database.createTransaction(transaction -> {
    // Execute the first request to the db, and lock the table
    requestAndLock(transaction);

    // open another transaction, and execute the second request in
    // a different transaction
    database.createTransaction(secondTransaction -> {
        requestAndLock(secondTransaction);
    });

    transaction.commit();
});

Я ожидал, что во втором запросе транзакция будет ждать, пока не истечет установленное время ожидания, а затем выбросит PessimisticLockException , но вместо этого заблокируется навсегда.

Hibernate генерирует мой запрос к БД следующим образом: 

SELECT value from Table where id=123 FOR UPDATE

В этом ответе я увидел, что Postgres допускает только SELECT FOR UPDATE NO WAIT, который устанавливает время ожидания равным 0, но невозможно установить время ожидания таким образом.

Есть ли другой способ, который я могу использовать с Hibernate / JPA? Может быть таким образом как-то рекомендуется?

7
Daniel Taub

Hibernate поддерживает кучу подсказок запроса . Тот, который вы используете, устанавливает тайм-аут для запроса, а не для пессимистической блокировки. Запрос и блокировка не зависят друг от друга, и вам нужно использовать подсказку, показанную ниже.

Но прежде чем сделать это, имейте в виду, что Hibernate не обрабатывает сам тайм-аут. Он только отправляет его в базу данных и зависит от базы данных, если и как он ее применяет.

Чтобы установить таймаут для пессимистической блокировки, вам нужно использовать подсказку javax.persistence.lock.timeout вместо этого. Вот пример:

entityManager.createQuery("SELECT value from Table where id=:id")
        .setParameter("id", "123")
        .setLockMode(LockModeType.PESSIMISTIC_WRITE)
        .setHint("javax.persistence.lock.timeout", 10000)
        .getSingleResult();
3
Thorben Janssen

Я думаю, вы могли бы попробовать

SET LOCAL lock_timeout = '10s';
SELECT ....;

Я сомневаюсь, что Hibernate поддерживает это из коробки. Вы можете попытаться найти способ продлить его, не уверенный, стоит ли оно того. Потому что я предполагаю, что использование блокировок в базе данных postges (то есть mvcc) - не самый умный вариант.

Вы также можете сделать NO WAIT и отложить несколько раз из своего кода.

2
kan

Существует параметр lock_timeout, который делает именно то, что вы хотите.

Вы можете установить его в postgresql.conf или с ALTER ROLE или ALTER DATABASE для пользователя или для базы данных.

0
Laurenz Albe