Chaque fois que vous demandez SERIALIZABLE
isolation, la base de données tentera de faire en sorte que des ensembles de requêtes simultanées semblent avoir été exécutées en série en fonction des résultats qu'ils produisent. Ce n'est pas toujours possible, par ex. lorsque deux transactions ont des dépendances mutuelles. Dans ce cas, PostgreSQL va abandonner l'une des transactions avec une erreur d'échec de sérialisation, vous indiquant que vous devez réessayer.
Code qui utilise SERIALIZABLE
doit toujours être prêt à réessayer des transactions. Il doit vérifier le SQLSTATE
et, pour les échecs de sérialisation, répétez la transaction.
Voir la documentation sur l'isolation des transactions .
Dans ce cas, je pense que votre principal malentendu pourrait être que :
comme il n'en est rien, c'est un INSERT ... SELECT
qui touche vo_business.repositoryoperation
aussi bien pour la lecture que pour l'écriture. C'est largement suffisant pour créer une dépendance potentielle avec une autre transaction qui fait la même chose, ou qui lit et écrit dans la table d'une autre manière.
De plus, le code d'isolement sérialisable peut, dans certaines circonstances, dégénérer en contenant des informations de dépendance au niveau du bloc pour des raisons d'efficacité. Il ne s'agit donc pas nécessairement d'une transaction touchant les mêmes lignes, juste le même bloc de stockage, en particulier sous charge.
PostgreSQL préférera abandonner une transaction sérialisable s'il n'est pas sûr qu'elle soit sûre. Le système de preuve a des limites. Il est donc également possible que vous veniez de trouver un cas qui le trompe.
Pour être sûr, j'aurais besoin de voir les deux transactions côte à côte, mais voici une preuve montrant un insert ... select
peut entrer en conflit avec lui-même. Ouvrez trois psql
sessions et exécuter :
session0: CREATE TABLE serialdemo(x integer, y integer);
session0: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session0: LOCK TABLE serialdemo IN ACCESS EXCLUSIVE MODE;
session1: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session2: BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
session1: INSERT INTO serialdemo (x, y)
SELECT 1, 2
WHERE NOT EXISTS (SELECT 1 FROM serialdemo WHERE x = 1);
session2: INSERT INTO serialdemo (x, y)
SELECT 1, 2
WHERE NOT EXISTS (SELECT 1 FROM serialdemo WHERE x = 1);
session0: ROLLBACK;
session1: COMMIT;
session2: COMMIT;
session1 commettra bien. session2 échouera avec :
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
Ce n'est pas le même échec de sérialisation que votre cas, et cela ne prouve pas que votre les instructions peuvent entrer en conflit les unes avec les autres, mais cela montre qu'un insert ... select
n'est pas aussi atomique que vous le pensiez.