]> granicus.if.org Git - postgresql/commitdiff
Avoid deadlock between concurrent CREATE INDEX CONCURRENTLY commands.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 25 Apr 2013 20:58:05 +0000 (16:58 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 25 Apr 2013 20:58:05 +0000 (16:58 -0400)
There was a high probability of two or more concurrent C.I.C. commands
deadlocking just before completion, because each would wait for the others
to release their reference snapshots.  Fix by releasing the snapshot
before waiting for other snapshots to go away.

Per report from Paul Hinze.  Back-patch to all active branches.

src/backend/commands/indexcmds.c

index f855befd4d4270d6405e8b0c30bc6eaeaa0b9a9b..66eae92a4c15c73f391287e695796431a6b835c1 100644 (file)
@@ -320,6 +320,7 @@ DefineIndex(IndexStmt *stmt,
        int16      *coloptions;
        IndexInfo  *indexInfo;
        int                     numberOfAttributes;
+       TransactionId limitXmin;
        VirtualTransactionId *old_lockholders;
        VirtualTransactionId *old_snapshots;
        int                     n_old_snapshots;
@@ -769,6 +770,18 @@ DefineIndex(IndexStmt *stmt,
         */
        validate_index(relationId, indexRelationId, snapshot);
 
+       /*
+        * Drop the reference snapshot.  We must do this before waiting out other
+        * snapshot holders, else we will deadlock against other processes also
+        * doing CREATE INDEX CONCURRENTLY, which would see our snapshot as one
+        * they must wait for.  But first, save the snapshot's xmin to use as
+        * limitXmin for GetCurrentVirtualXIDs().
+        */
+       limitXmin = snapshot->xmin;
+
+       PopActiveSnapshot();
+       UnregisterSnapshot(snapshot);
+
        /*
         * The index is now valid in the sense that it contains all currently
         * interesting tuples.  But since it might not contain tuples deleted just
@@ -801,7 +814,7 @@ DefineIndex(IndexStmt *stmt,
         * GetCurrentVirtualXIDs.  If, during any iteration, a particular vxid
         * doesn't show up in the output, we know we can forget about it.
         */
-       old_snapshots = GetCurrentVirtualXIDs(snapshot->xmin, true, false,
+       old_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false,
                                                                                  PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
                                                                                  &n_old_snapshots);
 
@@ -818,7 +831,7 @@ DefineIndex(IndexStmt *stmt,
                        int                     j;
                        int                     k;
 
-                       newer_snapshots = GetCurrentVirtualXIDs(snapshot->xmin,
+                       newer_snapshots = GetCurrentVirtualXIDs(limitXmin,
                                                                                                        true, false,
                                                                                 PROC_IS_AUTOVACUUM | PROC_IN_VACUUM,
                                                                                                        &n_newer_snapshots);
@@ -857,12 +870,6 @@ DefineIndex(IndexStmt *stmt,
         */
        CacheInvalidateRelcacheByRelid(heaprelid.relId);
 
-       /* we can now do away with our active snapshot */
-       PopActiveSnapshot();
-
-       /* And we can remove the validating snapshot too */
-       UnregisterSnapshot(snapshot);
-
        /*
         * Last thing to do is release the session-level lock on the parent table.
         */