Skip to content

Commit 0bada39

Browse files
committed
Fix inplace update buffer self-deadlock.
A CacheInvalidateHeapTuple* callee might call CatalogCacheInitializeCache(), which needs a relcache entry. Acquiring a valid relcache entry might scan pg_class. Hence, to prevent undetected LWLock self-deadlock, CacheInvalidateHeapTuple* callers must not hold BUFFER_LOCK_EXCLUSIVE on buffers of pg_class. Move the CacheInvalidateHeapTupleInplace() before the BUFFER_LOCK_EXCLUSIVE. No back-patch, since I've reverted commit 243e9b4 from non-master branches. Reported by Alexander Lakhin. Reviewed by Alexander Lakhin. Discussion: https://postgr.es/m/10ec0bc3-5933-1189-6bb8-5dec4114558e@gmail.com
1 parent b412f40 commit 0bada39

File tree

3 files changed

+26
-8
lines changed

3 files changed

+26
-8
lines changed

src/backend/access/heap/heapam.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6214,6 +6214,17 @@ heap_inplace_lock(Relation relation,
62146214

62156215
Assert(BufferIsValid(buffer));
62166216

6217+
/*
6218+
* Construct shared cache inval if necessary. Because we pass a tuple
6219+
* version without our own inplace changes or inplace changes other
6220+
* sessions complete while we wait for locks, inplace update mustn't
6221+
* change catcache lookup keys. But we aren't bothering with index
6222+
* updates either, so that's true a fortiori. After LockBuffer(), it
6223+
* would be too late, because this might reach a
6224+
* CatalogCacheInitializeCache() that locks "buffer".
6225+
*/
6226+
CacheInvalidateHeapTupleInplace(relation, oldtup_ptr, NULL);
6227+
62176228
LockTuple(relation, &oldtup.t_self, InplaceUpdateTupleLock);
62186229
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
62196230

@@ -6309,6 +6320,7 @@ heap_inplace_lock(Relation relation,
63096320
if (!ret)
63106321
{
63116322
UnlockTuple(relation, &oldtup.t_self, InplaceUpdateTupleLock);
6323+
ForgetInplace_Inval();
63126324
InvalidateCatalogSnapshot();
63136325
}
63146326
return ret;
@@ -6345,14 +6357,6 @@ heap_inplace_update_and_unlock(Relation relation,
63456357
dst = (char *) htup + htup->t_hoff;
63466358
src = (char *) tuple->t_data + tuple->t_data->t_hoff;
63476359

6348-
/*
6349-
* Construct shared cache inval if necessary. Note that because we only
6350-
* pass the new version of the tuple, this mustn't be used for any
6351-
* operations that could change catcache lookup keys. But we aren't
6352-
* bothering with index updates either, so that's true a fortiori.
6353-
*/
6354-
CacheInvalidateHeapTupleInplace(relation, tuple, NULL);
6355-
63566360
/* Like RecordTransactionCommit(), log only if needed */
63576361
if (XLogStandbyInfoActive())
63586362
nmsgs = inplaceGetInvalidationMessages(&invalMessages,
@@ -6481,6 +6485,7 @@ heap_inplace_unlock(Relation relation,
64816485
{
64826486
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
64836487
UnlockTuple(relation, &oldtup->t_self, InplaceUpdateTupleLock);
6488+
ForgetInplace_Inval();
64846489
}
64856490

64866491
#define FRM_NOOP 0x0001

src/backend/utils/cache/inval.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,18 @@ AtInplace_Inval(void)
12011201
inplaceInvalInfo = NULL;
12021202
}
12031203

1204+
/*
1205+
* ForgetInplace_Inval
1206+
* Alternative to PreInplace_Inval()+AtInplace_Inval(): discard queued-up
1207+
* invalidations. This lets inplace update enumerate invalidations
1208+
* optimistically, before locking the buffer.
1209+
*/
1210+
void
1211+
ForgetInplace_Inval(void)
1212+
{
1213+
inplaceInvalInfo = NULL;
1214+
}
1215+
12041216
/*
12051217
* AtEOSubXact_Inval
12061218
* Process queued-up invalidation messages at end of subtransaction.

src/include/utils/inval.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ extern void AtEOXact_Inval(bool isCommit);
3030

3131
extern void PreInplace_Inval(void);
3232
extern void AtInplace_Inval(void);
33+
extern void ForgetInplace_Inval(void);
3334

3435
extern void AtEOSubXact_Inval(bool isCommit);
3536

0 commit comments

Comments
 (0)