git.postgresql.org Git - postgresql.git/commitdiff (original) (raw)

/*

* Note: GetLockConflicts() never reports our own xid, hence we need not

- * check for that. Also, prepared xacts are not reported, which is fine

- * since they certainly aren't going to do anything anymore.

+ * check for that. Also, prepared xacts are reported and awaited.

*/

/* Finally wait for each such transaction to complete */

* so use of this function has to be thought about carefully.

*

* Note we never include the current xact's vxid in the result array,

- * since an xact never blocks itself. Also, prepared transactions are

- * ignored, which is a bit more debatable but is appropriate for current

- * uses of the result.

+ * since an xact never blocks itself.

*/

VirtualTransactionId *

GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)

/*

* Allocate memory to store results, and fill with InvalidVXID. We only

- * need enough space for MaxBackends + a terminator, since prepared xacts

- * don't count. InHotStandby allocate once in TopMemoryContext.

+ * need enough space for MaxBackends + max_prepared_xacts + a terminator.

+ * InHotStandby allocate once in TopMemoryContext.

*/

if (InHotStandby)

{

if (vxids == NULL)

vxids = (VirtualTransactionId *)

MemoryContextAlloc(TopMemoryContext,

- sizeof(VirtualTransactionId) * (MaxBackends + 1));

+ sizeof(VirtualTransactionId) *

+ (MaxBackends + max_prepared_xacts + 1));

}

else

vxids = (VirtualTransactionId *)

- palloc0(sizeof(VirtualTransactionId) * (MaxBackends + 1));

+ palloc0(sizeof(VirtualTransactionId) *

+ (MaxBackends + max_prepared_xacts + 1));

/* Compute hash code and partition lock, and look up conflicting modes. */

hashcode = LockTagHashCode(locktag);

/* Conflict! */

GET_VXID_FROM_PGPROC(vxid, *proc);

- /*

- * If we see an invalid VXID, then either the xact has already

- * committed (or aborted), or it's a prepared xact. In either

- * case we may ignore it.

- */

if (VirtualTransactionIdIsValid(vxid))

vxids[count++] = vxid;

+ /* else, xact already committed or aborted */

/* No need to examine remaining slots. */

break;

GET_VXID_FROM_PGPROC(vxid, *proc);

- /*

- * If we see an invalid VXID, then either the xact has already

- * committed (or aborted), or it's a prepared xact. In either

- * case we may ignore it.

- */

if (VirtualTransactionIdIsValid(vxid))

{

int i;

if (i >= fast_count)

vxids[count++] = vxid;

}

+ /* else, xact already committed or aborted */

}

}

LWLockRelease(partitionLock);

- if (count > MaxBackends) /* should never happen */

+ if (count > MaxBackends + max_prepared_xacts) /* should never happen */

elog(PANIC, "too many conflicting locks found");

vxids[count].backendId = InvalidBackendId;

Assert(VirtualTransactionIdIsValid(vxid));

+ if (VirtualTransactionIdIsPreparedXact(vxid))

+ {

+ LockAcquireResult lar;

+ /*

+ * Prepared transactions don't hold vxid locks. The

+ * LocalTransactionId is always a normal, locked XID.

+ */

+ SET_LOCKTAG_TRANSACTION(tag, vxid.localTransactionId);

+ lar = LockAcquire(&tag, ShareLock, false, !wait);

+ if (lar != LOCKACQUIRE_NOT_AVAIL)

+ LockRelease(&tag, ShareLock, false);

+ return lar != LOCKACQUIRE_NOT_AVAIL;

+ }

SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);

/*

/*

* Top-level transactions are identified by VirtualTransactionIDs comprising

- * the BackendId of the backend running the xact, plus a locally-assigned

- * LocalTransactionId. These are guaranteed unique over the short term,

- * but will be reused after a database restart; hence they should never

- * be stored on disk.

+ * PGPROC fields backendId and lxid. For prepared transactions, the

+ * LocalTransactionId is an ordinary XID. These are guaranteed unique over

+ * the short term, but will be reused after a database restart or XID

+ * wraparound; hence they should never be stored on disk.

*

* Note that struct VirtualTransactionId can not be assumed to be atomically

* assignable as a whole. However, type LocalTransactionId is assumed to

*/

typedef struct

{

- BackendId backendId; /* determined at backend startup */

- LocalTransactionId localTransactionId; /* backend-local transaction id */

+ BackendId backendId; /* backendId from PGPROC */

+ LocalTransactionId localTransactionId; /* lxid from PGPROC */

} VirtualTransactionId;

#define InvalidLocalTransactionId 0

#define LocalTransactionIdIsValid(lxid) ((lxid) != InvalidLocalTransactionId)

#define VirtualTransactionIdIsValid(vxid) \

- (((vxid).backendId != InvalidBackendId) && \

- LocalTransactionIdIsValid((vxid).localTransactionId))

+ (LocalTransactionIdIsValid((vxid).localTransactionId))

+#define VirtualTransactionIdIsPreparedXact(vxid) \

+ ((vxid).backendId == InvalidBackendId)

#define VirtualTransactionIdEquals(vxid1, vxid2) \

((vxid1).backendId == (vxid2).backendId && \

(vxid1).localTransactionId == (vxid2).localTransactionId)

check: all

(pgisolationregresscheck)−−schedule=(pg_isolation_regress_check) --schedule=(pgisolationregresscheck)schedule=(srcdir)/isolation_schedule

-# Versions of the check tests that include the prepared_transactions test

-# It only makes sense to run these if set up to use prepared transactions,

-# via TEMP_CONFIG for the check case, or via the postgresql.conf for the

-# installcheck case.

+# Non-default tests. It only makes sense to run these if set up to use

+# prepared transactions, via TEMP_CONFIG for the check case, or via the

+# postgresql.conf for the installcheck case.

installcheck-prepared-txns: all temp-install

- (pgisolationregressinstallcheck)−−schedule=(pg_isolation_regress_installcheck) --schedule=(pgisolationregressinstallcheck)schedule=(srcdir)/isolation_schedule prepared-transactions

+ (pgisolationregressinstallcheck)−−schedule=(pg_isolation_regress_installcheck) --schedule=(pgisolationregressinstallcheck)schedule=(srcdir)/isolation_schedule prepared-transactions prepared-transactions-cic

check-prepared-txns: all temp-install

- (pgisolationregresscheck)−−schedule=(pg_isolation_regress_check) --schedule=(pgisolationregresscheck)schedule=(srcdir)/isolation_schedule prepared-transactions

+ (pgisolationregresscheck)−−schedule=(pg_isolation_regress_check) --schedule=(pgisolationregresscheck)schedule=(srcdir)/isolation_schedule prepared-transactions prepared-transactions-cic

./pg_isolation_regress fk-contention fk-deadlock

(look into the specs/ subdirectory to see the available tests).

-The prepared-transactions test requires the server's

-max_prepared_transactions parameter to be set to at least 3; therefore it

-is not run by default. To include it in the test run, use

+Certain tests require the server's max_prepared_transactions parameter to be

+set to at least 3; therefore they are not run by default. To include them in

+the test run, use

make check-prepared-txns

or

make installcheck-prepared-txns

--- /dev/null

+Parsed test spec with 2 sessions

+starting permutation: w1 p1 cic2 c1 r2

+step w1: BEGIN; INSERT INTO cic_test VALUES (1);

+step p1: PREPARE TRANSACTION 's1';

+step cic2:

+ CREATE INDEX CONCURRENTLY on cic_test(a);

+ERROR: canceling statement due to lock timeout

+step c1: COMMIT PREPARED 's1';

+step r2:

+ SET enable_seqscan to off;

+ SET enable_bitmapscan to off;

+ SELECT * FROM cic_test WHERE a = 1;

+a

+1

--- /dev/null

+# This test verifies that CREATE INDEX CONCURRENTLY interacts with prepared

+# transactions correctly.

+setup

+{

+ CREATE TABLE cic_test (a int);

+}

+teardown

+{

+ DROP TABLE cic_test;

+}

+# Sessions for CREATE INDEX CONCURRENTLY test

+session "s1"

+step "w1" { BEGIN; INSERT INTO cic_test VALUES (1); }

+step "p1" { PREPARE TRANSACTION 's1'; }

+step "c1" { COMMIT PREPARED 's1'; }

+session "s2"

+# The isolation tester never recognizes that a lock of s1 blocks s2, because a

+# prepared transaction's locks have no pid associated. While there's a slight

+# chance of timeout while waiting for an autovacuum-held lock, that wouldn't

+# change the output. Hence, no timeout is too short.

+setup { SET lock_timeout = 10; }

+step "cic2"

+{

+ CREATE INDEX CONCURRENTLY on cic_test(a);

+}

+step "r2"

+{

+ SET enable_seqscan to off;

+ SET enable_bitmapscan to off;

+ SELECT * FROM cic_test WHERE a = 1;

+}

+permutation "w1" "p1" "cic2" "c1" "r2"