]> granicus.if.org Git - postgresql/commitdiff
Retry after buffer locking failure during SPGiST index creation.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 Nov 2013 20:45:42 +0000 (16:45 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 2 Nov 2013 20:45:52 +0000 (16:45 -0400)
The original coding thought this case was impossible, but it can happen
if the bgwriter or checkpointer processes decide to write out an index
page while creation is still proceeding, leading to a bogus "unexpected
spgdoinsert() failure" error.  Problem reported by Jonathan S. Katz.

Teodor Sigaev

src/backend/access/spgist/spgdoinsert.c
src/backend/access/spgist/spginsert.c

index 1fd331cbdfd1559ac5495fba9fe87c74a317a28f..9f425ca3558652572af902c59beae95d31b79636 100644 (file)
@@ -1946,9 +1946,12 @@ spgdoinsert(Relation index, SpGistState *state,
                         * Attempt to acquire lock on child page.  We must beware of
                         * deadlock against another insertion process descending from that
                         * page to our parent page (see README).  If we fail to get lock,
-                        * abandon the insertion and tell our caller to start over.  XXX
-                        * this could be improved; perhaps it'd be worth sleeping a bit
-                        * before giving up?
+                        * abandon the insertion and tell our caller to start over.
+                        *
+                        * XXX this could be improved, because failing to get lock on a
+                        * buffer is not proof of a deadlock situation; the lock might be
+                        * held by a reader, or even just background writer/checkpointer
+                        * process.  Perhaps it'd be worth retrying after sleeping a bit?
                         */
                        if (!ConditionalLockBuffer(current.buffer))
                        {
index f4d0fe5a0c253512ee9c095bf0f06e7546d9271e..2a50d87c74b09f720ee7bacddf5ac9a2528bdfd2 100644 (file)
@@ -45,10 +45,17 @@ spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
        /* Work in temp context, and reset it after each tuple */
        oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
 
-       /* No concurrent insertions can be happening, so failure is unexpected */
-       if (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
-                                        *values, *isnull))
-               elog(ERROR, "unexpected spgdoinsert() failure");
+       /*
+        * Even though no concurrent insertions can be happening, we still might
+        * get a buffer-locking failure due to bgwriter or checkpointer taking a
+        * lock on some buffer.  So we need to be willing to retry.  We can flush
+        * any temp data when retrying.
+        */
+       while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
+                                               *values, *isnull))
+       {
+               MemoryContextReset(buildstate->tmpCtx);
+       }
 
        MemoryContextSwitchTo(oldCtx);
        MemoryContextReset(buildstate->tmpCtx);