TL;DR:la detección de conflictos de serialización mejoró drásticamente en Pg 9.1, así que actualice.
Es complicado averiguar a partir de su descripción cuál es el SQL real y por qué espera obtener una reversión. Parece que has malinterpretado seriamente el aislamiento serializable, tal vez pensando que prueba perfectamente todos los predicados, lo cual no es así, especialmente en Pg 8.4.
SERIALIZABLE
no garantiza perfectamente que las transacciones se ejecuten como si se ejecutaran en serie, ya que hacerlo sería prohibitivamente costoso desde el punto de vista del rendimiento si fuera posible. Solo proporciona una verificación limitada. Exactamente lo que se verifica y cómo varía de una base de datos a otra y de una versión a otra, por lo que debe leer los documentos para su versión de su base de datos.
Las anomalías son posibles, donde dos transacciones se ejecutan en SERIALIZABLE
modo producir un resultado diferente a si esas transacciones realmente se ejecutaran en serie.
Lea la documentación sobre el aislamiento de transacciones en Pg para obtener más información. Tenga en cuenta que SERIALIZABLE
cambió drásticamente el comportamiento en Pg 9.1, así que asegúrese de leer la versión del manual apropiada para su versión de Pg. Esta es la versión 8.4
. En particular, lea 13.2.2.1. Aislamiento serializable versus Serializabilidad real
. Ahora compare eso con el soporte de serialización basado en bloqueo predicado muy mejorado descrito en el Pág. 9.1 documentos
.
Parece que estás tratando de realizar una lógica similar a este pseudocódigo:
count = query("SELECT count(*) FROM the_table");
if (count < threshold):
query("INSERT INTO the_table (...) VALUES (...)");
Si es así, eso no funcionará en Pg 8.4 cuando se ejecute simultáneamente; es más o menos lo mismo que el ejemplo de anomalía utilizado en la documentación vinculada anteriormente. Sorprendentemente, en realidad funciona en la página 9.1; Ni siquiera esperaba que el bloqueo de predicados de 9.1 detectara el uso de agregados.
Escribes eso:
pero 8.4 no detectará que las dos transacciones son interdependientes, algo que puede probar trivialmente usando dos psql
sesiones para probarlo. Solo funcionará con el material de serialización real introducido en 9.1 y, francamente, me sorprendió que funcione en 9.1.
Si desea hacer algo como imponer un número máximo de filas en Pg 8.4, debe LOCK
la mesa
para evitar INSERT
concurrentes s, haciendo el bloqueo manualmente o a través de una función de activación . Hacerlo en un disparador requerirá inherentemente una promoción de bloqueo y, por lo tanto, con frecuencia se bloqueará, pero hará el trabajo con éxito. Es mejor hacerlo en la aplicación donde puedes emitir el LOCK TABLE my_table IN EXCLUSIVE MODE
antes de obtener incluso SELECT
ing de la mesa, por lo que ya tiene el modo de bloqueo más alto que necesitará en la mesa y, por lo tanto, no debería necesitar la promoción de bloqueo propenso a interbloqueos. El EXCLUSIVE
el modo de bloqueo es apropiado porque permite SELECT
s pero nada más.
He aquí cómo probarlo en dos sesiones de psql:
SESSION 1 SESSION 2
create table ser_test( x text );
BEGIN TRANSACTION
ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION
ISOLATION LEVEL SERIALIZABLE;
SELECT count(*) FROM ser_test ;
SELECT count(*) FROM ser_test ;
INSERT INTO ser_test(x) VALUES ('bob');
INSERT INTO ser_test(x) VALUES ('bob');
COMMIT;
COMMIT;
Cuando se ejecuta en Pg 9.1, el st commits succeeds then the second
COMMIT` falla con:
regress=# COMMIT;
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.
pero cuando se ejecuta en 8.4, ambas confirmaciones tienen éxito, porque 8.4 no tenía todo el código de bloqueo predicado para serialización agregado en 9.1.