Fix bug in PreCommit_CheckForSerializationFailure. A transaction that has

already been marked as PREPARED cannot be killed. Kill the current
transaction instead.

One of the prepared_xacts regression tests actually hits this bug. I
removed the anomaly from the duplicate-gids test so that it fails in the
intended way, and added a new test to check serialization failures with
a prepared transaction.

Dan Ports
This commit is contained in:
Heikki Linnakangas 2011-06-21 14:32:11 +03:00
parent 7cb2ff9621
commit 1eea8e8a06
3 changed files with 79 additions and 9 deletions

View File

@ -4542,6 +4542,21 @@ PreCommit_CheckForSerializationFailure(void)
&& !SxactIsReadOnly(farConflict->sxactOut)
&& !SxactIsDoomed(farConflict->sxactOut)))
{
/*
* Normally, we kill the pivot transaction to make sure we
* make progress if the failing transaction is retried.
* However, we can't kill it if it's already prepared, so
* in that case we commit suicide instead.
*/
if (SxactIsPrepared(nearConflict->sxactOut))
{
LWLockRelease(SerializableXactHashLock);
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
errdetail("Cancelled on commit attempt with conflict in from prepared pivot."),
errhint("The transaction might succeed if retried.")));
}
nearConflict->sxactOut->flags |= SXACT_FLAG_DOOMED;
break;
}

View File

@ -88,14 +88,6 @@ SELECT gid FROM pg_prepared_xacts;
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO pxtest1 VALUES ('fff');
SELECT * FROM pxtest1;
foobar
--------
aaa
ddd
fff
(3 rows)
-- This should fail, because the gid foo3 is already in use
PREPARE TRANSACTION 'foo3';
ERROR: transaction identifier "foo3" is already in use
@ -114,6 +106,49 @@ SELECT * FROM pxtest1;
ddd
(2 rows)
-- Test serialization failure (SSI)
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd';
SELECT * FROM pxtest1;
foobar
--------
aaa
eee
(2 rows)
PREPARE TRANSACTION 'foo4';
SELECT gid FROM pg_prepared_xacts;
gid
------
foo4
(1 row)
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM pxtest1;
foobar
--------
aaa
ddd
(2 rows)
INSERT INTO pxtest1 VALUES ('fff');
-- This should fail, because the two transactions have a write-skew anomaly
PREPARE TRANSACTION 'foo5';
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Cancelled on commit attempt with conflict in from prepared pivot.
HINT: The transaction might succeed if retried.
SELECT gid FROM pg_prepared_xacts;
gid
------
foo4
(1 row)
ROLLBACK PREPARED 'foo4';
SELECT gid FROM pg_prepared_xacts;
gid
-----
(0 rows)
-- Clean up
DROP TABLE pxtest1;
-- Test subtransactions

View File

@ -54,7 +54,6 @@ SELECT gid FROM pg_prepared_xacts;
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO pxtest1 VALUES ('fff');
SELECT * FROM pxtest1;
-- This should fail, because the gid foo3 is already in use
PREPARE TRANSACTION 'foo3';
@ -65,6 +64,27 @@ ROLLBACK PREPARED 'foo3';
SELECT * FROM pxtest1;
-- Test serialization failure (SSI)
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd';
SELECT * FROM pxtest1;
PREPARE TRANSACTION 'foo4';
SELECT gid FROM pg_prepared_xacts;
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT * FROM pxtest1;
INSERT INTO pxtest1 VALUES ('fff');
-- This should fail, because the two transactions have a write-skew anomaly
PREPARE TRANSACTION 'foo5';
SELECT gid FROM pg_prepared_xacts;
ROLLBACK PREPARED 'foo4';
SELECT gid FROM pg_prepared_xacts;
-- Clean up
DROP TABLE pxtest1;