Siempre que solicite SERIALIZABLE
aislamiento, la base de datos intentará hacer que los conjuntos de consultas concurrentes parezcan haberse ejecutado en serie en cuanto a los resultados que producen. Esto no siempre es posible, p. cuando dos transacciones tienen dependencias mutuas. En este caso, PostgreSQL abortará una de las transacciones con un error de falla de serialización, indicándole que debe volver a intentarlo.
Código que usa SERIALIZABLE
siempre debe estar preparado para volver a intentar transacciones. Debe verificar el SQLSTATE
y, para fallas de serialización, repita la transacción.
Consulte la documentación de aislamiento de transacciones .
En este caso, creo que su principal malentendido puede ser que:
como no es nada por el estilo, es un INSERT ... SELECT
que toca vo_business.repositoryoperation
tanto para leer como para escribir. Eso es suficiente para crear una dependencia potencial con otra transacción que haga lo mismo, o una que lea y escriba en la tabla de otra forma.
Además, el código de aislamiento serializable puede, en algunas circunstancias, degenerar y contener información de dependencia a nivel de bloque por razones de eficiencia. Por lo tanto, puede que no sea necesariamente una transacción que toca las mismas filas, solo el mismo bloque de almacenamiento, especialmente bajo carga.
PostgreSQL preferirá abortar una transacción serializable si no está seguro de que sea segura. El sistema de prueba tiene limitaciones. Así que también es posible que hayas encontrado un caso que lo engañe.
Para estar seguro, necesitaría ver ambas transacciones una al lado de la otra, pero aquí hay una prueba que muestra un insert ... select
puede entrar en conflicto consigo mismo. Abra tres psql
sesiones y ejecutar:
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 se comprometerá bien. session2 fallará con:
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.
No es el mismo error de serialización que su caso y no prueba que su las declaraciones pueden entrar en conflicto entre sí, pero muestra que un insert ... select
no es tan atómico como pensabas.