J'essaie de définir un Lock
pour la ligne sur laquelle je travaille jusqu'au prochain commit:
entityManager.createQuery("SELECT value from Table where id=:id")
.setParameter("id", "123")
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.setHint("javax.persistence.lock.timeout", 10000)
.getSingleResult();
Ce que je pensais devoir arriver, c'est que si deux threads essayent d'écrire sur la base de données en même temps, un thread atteindra l'opération de mise à jour avant l'autre, le second devrait attendre 10 secondes, puis lancer PessimisticLockException
.
Mais au lieu de cela, le thread se bloque jusqu'à ce que l'autre thread se termine, quel que soit le délai imparti.
Regardez cet exemple:
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();
});
Je m'attendais à ce que, dans la deuxième demande, la transaction attende jusqu'à l'expiration du délai imparti, puis jette le PessimisticLockException
, mais à la place, elle bloque pour toujours.
Hibernate génère ma demande à la base de données de la manière suivante:
SELECT value from Table where id=123 FOR UPDATE
Dans cette réponse , j'ai vu que Postgres
n'autorise que SELECT FOR UPDATE NO WAIT
qui définit le délai d'attente à 0, mais il n'est pas possible de définir un délai d'attente de cette façon.
Existe-t-il un autre moyen que je peux utiliser avec Hibernate / JPA
? Peut-être que cette façon est en quelque sorte recommandée?
Hibernate supporte un tas d'indices de requête . Celui que vous utilisez définit le délai d'expiration de la requête, pas celui du verrou pessimiste. La requête et le verrou sont indépendants l'un de l'autre et vous devez utiliser l'indice indiqué ci-dessous.
Mais avant de faire cela, sachez qu'Hibernate ne gère pas le délai d'attente lui-même. Il l'envoie seulement à la base de données et cela dépend de la base de données, si et comment il l'applique.
Pour définir un délai d'expiration pour le verrou pessimiste, vous devez plutôt utiliser l'indicateur javax.persistence.lock.timeout
. Voici un exemple:
entityManager.createQuery("SELECT value from Table where id=:id")
.setParameter("id", "123")
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.setHint("javax.persistence.lock.timeout", 10000)
.getSingleResult();
Je pense que tu pourrais essayer
SET LOCAL lock_timeout = '10s';
SELECT ....;
Je doute qu'Hibernate supporte cet out-of-box. Vous pourriez essayer de trouver un moyen de l'étendre, sans savoir si cela en valait la peine. Parce que je suppose que l'utilisation de verrous sur une base de données Postges (qui est mvcc) n'est pas l'option la plus intelligente.
Vous pouvez également faire NO WAIT
et retenter plusieurs fois votre code à plusieurs reprises.
Il y a le paramètre lock_timeout
qui fait exactement ce que vous voulez.
Vous pouvez le définir dans postgresql.conf
ou avec ALTER ROLE
ou ALTER DATABASE
par utilisateur ou par base de données.