v13-0002-PoC-fix-for-race-in-RelationBuildDesc-and-relcac.patch

application/octet-stream

Filename: v13-0002-PoC-fix-for-race-in-RelationBuildDesc-and-relcac.patch
Type: application/octet-stream
Part: 5
Message: Re: CREATE INDEX CONCURRENTLY does not index prepared xact's data

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v13-0002
Subject: PoC fix for race in RelationBuildDesc() and relcache invalidation
File+
src/backend/utils/cache/relcache.c 41 0
From 599cedfb433aa22fa4ebd1f27267de98b378d157 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Fri, 30 Jul 2021 14:40:16 +0500
Subject: [PATCH v13 2/6] PoC fix for race in RelationBuildDesc() and relcache
 invalidation

Index indisready may be changed during RelationBuildDesc(), but invalidation
message for this can be consumed by some other call of RelationBuildDesc().
To prevent this we set up list of invalidated relation descriptions.
---
 src/backend/utils/cache/relcache.c | 41 ++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 13d9994af3..d6165be5a3 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -997,9 +997,21 @@ equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
  *		(suggesting we are trying to access a just-deleted relation).
  *		Any other error is reported via elog.
  */
+
+/*
+ * InProgressRels is a stack of current RelationBuildDesc() calls
+ * that can consume incalidation messages and put borken desc into cache.
+ */
+typedef struct InProgressRels {
+	Oid relid;
+	bool invalidated;
+} InProgressRels;
+static InProgressRels inProgress[100];
+
 static Relation
 RelationBuildDesc(Oid targetRelId, bool insertIt)
 {
+	int in_progress_offset;
 	Relation	relation;
 	Oid			relid;
 	HeapTuple	pg_class_tuple;
@@ -1033,6 +1045,15 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	}
 #endif
 
+	/* Register ourselves as in progress of RelationBuildDesc for invalidation messages */
+	for (in_progress_offset = 0;
+		 OidIsValid(inProgress[in_progress_offset].relid);
+		 in_progress_offset++)
+		;
+	inProgress[in_progress_offset].relid = targetRelId;
+retry:
+	inProgress[in_progress_offset].invalidated = false;
+
 	/*
 	 * find the tuple in pg_class corresponding to the given relation id
 	 */
@@ -1213,6 +1234,13 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	 */
 	heap_freetuple(pg_class_tuple);
 
+	/* We got invalidation message, need to rebuild */
+	if (inProgress[in_progress_offset].invalidated)
+		goto retry;				/* TODO free old one */
+	/* inProgress is in fact the stack, we can safely remove it's top */
+	inProgress[in_progress_offset].relid = InvalidOid;
+	Assert(inProgress[in_progress_offset + 1].relid == InvalidOid);
+
 	/*
 	 * Insert newly created relation into relcache hash table, if requested.
 	 *
@@ -2805,6 +2833,14 @@ RelationCacheInvalidateEntry(Oid relationId)
 		relcacheInvalsReceived++;
 		RelationFlushRelation(relation);
 	}
+	else
+	{
+		/* Any RelationBuildDesc() on the stack must start over. */
+		int i;
+		for (i = 0; OidIsValid(inProgress[i].relid); i++)
+			if (inProgress[i].relid == relationId)
+				inProgress[i].invalidated = true;
+	}
 }
 
 /*
@@ -2845,6 +2881,7 @@ RelationCacheInvalidate(void)
 	List	   *rebuildFirstList = NIL;
 	List	   *rebuildList = NIL;
 	ListCell   *l;
+	int i;
 
 	/*
 	 * Reload relation mapping data before starting to reconstruct cache.
@@ -2931,6 +2968,10 @@ RelationCacheInvalidate(void)
 		RelationClearRelation(relation, true);
 	}
 	list_free(rebuildList);
+
+	/* Any RelationBuildDesc() on the stack must start over. */
+	for (i = 0; OidIsValid(inProgress[i].relid); i++)
+		inProgress[i].invalidated = true;
 }
 
 /*
-- 
2.24.3 (Apple Git-128)