Skip to content

Commit 31737eb

Browse files
committed
Track unowned relations in doubly-linked list
Relations dropped in a single transaction are tracked in a list of unowned relations. With large number of dropped relations this resulted in poor performance at the end of a transaction, when the relations are removed from the singly linked list one by one. Commit b416691 attempted to address this issue (particularly when it happens during recovery) by removing the relations in a reverse order, resulting in O(1) lookups in the list of unowned relations. This did not work reliably, though, and it was possible to trigger the O(N^2) behavior in various ways. Instead of trying to remove the relations in a specific order with respect to the linked list, which seems rather fragile, switch to a regular doubly linked. That allows us to remove relations cheaply no matter where in the list they are. As b416691 was a bugfix, backpatched to all supported versions, do the same thing here. Reviewed-by: Alvaro Herrera Discussion: https://www.postgresql.org/message-id/flat/80c27103-99e4-1d0c-642c-d9f3b94aaa0a%402ndquadrant.com Backpatch-through: 9.4
1 parent 35eaab7 commit 31737eb

File tree

3 files changed

+20
-65
lines changed

3 files changed

+20
-65
lines changed

src/backend/storage/smgr/md.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,13 +1665,7 @@ DropRelationFiles(RelFileNode *delrels, int ndelrels, bool isRedo)
16651665

16661666
smgrdounlinkall(srels, ndelrels, isRedo);
16671667

1668-
/*
1669-
* Call smgrclose() in reverse order as when smgropen() is called.
1670-
* This trick enables remove_from_unowned_list() in smgrclose()
1671-
* to search the SMgrRelation from the unowned list,
1672-
* with O(1) performance.
1673-
*/
1674-
for (i = ndelrels - 1; i >= 0; i--)
1668+
for (i = 0; i < ndelrels; i++)
16751669
smgrclose(srels[i]);
16761670
pfree(srels);
16771671
}

src/backend/storage/smgr/smgr.c

Lines changed: 17 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "postgres.h"
1919

2020
#include "commands/tablespace.h"
21+
#include "lib/ilist.h"
2122
#include "storage/bufmgr.h"
2223
#include "storage/ipc.h"
2324
#include "storage/smgr.h"
@@ -80,12 +81,10 @@ static const int NSmgr = lengthof(smgrsw);
8081
*/
8182
static HTAB *SMgrRelationHash = NULL;
8283

83-
static SMgrRelation first_unowned_reln = NULL;
84+
static dlist_head unowned_relns;
8485

8586
/* local function prototypes */
8687
static void smgrshutdown(int code, Datum arg);
87-
static void add_to_unowned_list(SMgrRelation reln);
88-
static void remove_from_unowned_list(SMgrRelation reln);
8988

9089

9190
/*
@@ -149,7 +148,7 @@ smgropen(RelFileNode rnode, BackendId backend)
149148
ctl.hash = tag_hash;
150149
SMgrRelationHash = hash_create("smgr relation table", 400,
151150
&ctl, HASH_ELEM | HASH_FUNCTION);
152-
first_unowned_reln = NULL;
151+
dlist_init(&unowned_relns);
153152
}
154153

155154
/* Look up or create an entry */
@@ -176,7 +175,7 @@ smgropen(RelFileNode rnode, BackendId backend)
176175
reln->md_fd[forknum] = NULL;
177176

178177
/* it has no owner yet */
179-
add_to_unowned_list(reln);
178+
dlist_push_tail(&unowned_relns, &reln->node);
180179
}
181180

182181
return reln;
@@ -206,7 +205,7 @@ smgrsetowner(SMgrRelation *owner, SMgrRelation reln)
206205
if (reln->smgr_owner)
207206
*(reln->smgr_owner) = NULL;
208207
else
209-
remove_from_unowned_list(reln);
208+
dlist_delete(&reln->node);
210209

211210
/* Now establish the ownership relationship. */
212211
reln->smgr_owner = owner;
@@ -230,53 +229,8 @@ smgrclearowner(SMgrRelation *owner, SMgrRelation reln)
230229
/* unset our reference to the owner */
231230
reln->smgr_owner = NULL;
232231

233-
add_to_unowned_list(reln);
234-
}
235-
236-
/*
237-
* add_to_unowned_list -- link an SMgrRelation onto the unowned list
238-
*
239-
* Check remove_from_unowned_list()'s comments for performance
240-
* considerations.
241-
*/
242-
static void
243-
add_to_unowned_list(SMgrRelation reln)
244-
{
245-
/* place it at head of the list (to make smgrsetowner cheap) */
246-
reln->next_unowned_reln = first_unowned_reln;
247-
first_unowned_reln = reln;
248-
}
249-
250-
/*
251-
* remove_from_unowned_list -- unlink an SMgrRelation from the unowned list
252-
*
253-
* If the reln is not present in the list, nothing happens. Typically this
254-
* would be caller error, but there seems no reason to throw an error.
255-
*
256-
* In the worst case this could be rather slow; but in all the cases that seem
257-
* likely to be performance-critical, the reln being sought will actually be
258-
* first in the list. Furthermore, the number of unowned relns touched in any
259-
* one transaction shouldn't be all that high typically. So it doesn't seem
260-
* worth expending the additional space and management logic needed for a
261-
* doubly-linked list.
262-
*/
263-
static void
264-
remove_from_unowned_list(SMgrRelation reln)
265-
{
266-
SMgrRelation *link;
267-
SMgrRelation cur;
268-
269-
for (link = &first_unowned_reln, cur = *link;
270-
cur != NULL;
271-
link = &cur->next_unowned_reln, cur = *link)
272-
{
273-
if (cur == reln)
274-
{
275-
*link = cur->next_unowned_reln;
276-
cur->next_unowned_reln = NULL;
277-
break;
278-
}
279-
}
232+
/* add to list of unowned relations */
233+
dlist_push_tail(&unowned_relns, &reln->node);
280234
}
281235

282236
/*
@@ -303,7 +257,7 @@ smgrclose(SMgrRelation reln)
303257
owner = reln->smgr_owner;
304258

305259
if (!owner)
306-
remove_from_unowned_list(reln);
260+
dlist_delete(&reln->node);
307261

308262
if (hash_search(SMgrRelationHash,
309263
(void *) &(reln->smgr_rnode),
@@ -783,13 +737,19 @@ smgrpostckpt(void)
783737
void
784738
AtEOXact_SMgr(void)
785739
{
740+
dlist_mutable_iter iter;
741+
786742
/*
787743
* Zap all unowned SMgrRelations. We rely on smgrclose() to remove each
788744
* one from the list.
789745
*/
790-
while (first_unowned_reln != NULL)
746+
dlist_foreach_modify(iter, &unowned_relns)
791747
{
792-
Assert(first_unowned_reln->smgr_owner == NULL);
793-
smgrclose(first_unowned_reln);
748+
SMgrRelation rel = dlist_container(SMgrRelationData, node,
749+
iter.cur);
750+
751+
Assert(rel->smgr_owner == NULL);
752+
753+
smgrclose(rel);
794754
}
795755
}

src/include/storage/smgr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define SMGR_H
1616

1717
#include "fmgr.h"
18+
#include "lib/ilist.h"
1819
#include "storage/block.h"
1920
#include "storage/relfilenode.h"
2021

@@ -68,7 +69,7 @@ typedef struct SMgrRelationData
6869
struct _MdfdVec *md_fd[MAX_FORKNUM + 1];
6970

7071
/* if unowned, list link in list of all unowned SMgrRelations */
71-
struct SMgrRelationData *next_unowned_reln;
72+
dlist_node node;
7273
} SMgrRelationData;
7374

7475
typedef SMgrRelationData *SMgrRelation;

0 commit comments

Comments
 (0)