web-dev-qa-db-fra.com

Comment définir le délai de verrouillage dans postgres - Hibernate

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?

7
Daniel Taub

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();
3
Thorben Janssen

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.

2
kan

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.

0
Laurenz Albe