mirror of
https://github.com/postgres/postgres.git
synced 2025-06-10 09:21:54 +03:00
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. Commitb4166911
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. Asb4166911
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
This commit is contained in:
@ -1665,13 +1665,7 @@ DropRelationFiles(RelFileNode *delrels, int ndelrels, bool isRedo)
|
|||||||
|
|
||||||
smgrdounlinkall(srels, ndelrels, isRedo);
|
smgrdounlinkall(srels, ndelrels, isRedo);
|
||||||
|
|
||||||
/*
|
for (i = 0; i < ndelrels; i++)
|
||||||
* Call smgrclose() in reverse order as when smgropen() is called.
|
|
||||||
* This trick enables remove_from_unowned_list() in smgrclose()
|
|
||||||
* to search the SMgrRelation from the unowned list,
|
|
||||||
* with O(1) performance.
|
|
||||||
*/
|
|
||||||
for (i = ndelrels - 1; i >= 0; i--)
|
|
||||||
smgrclose(srels[i]);
|
smgrclose(srels[i]);
|
||||||
pfree(srels);
|
pfree(srels);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "commands/tablespace.h"
|
#include "commands/tablespace.h"
|
||||||
|
#include "lib/ilist.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
#include "storage/ipc.h"
|
#include "storage/ipc.h"
|
||||||
#include "storage/smgr.h"
|
#include "storage/smgr.h"
|
||||||
@ -80,12 +81,10 @@ static const int NSmgr = lengthof(smgrsw);
|
|||||||
*/
|
*/
|
||||||
static HTAB *SMgrRelationHash = NULL;
|
static HTAB *SMgrRelationHash = NULL;
|
||||||
|
|
||||||
static SMgrRelation first_unowned_reln = NULL;
|
static dlist_head unowned_relns;
|
||||||
|
|
||||||
/* local function prototypes */
|
/* local function prototypes */
|
||||||
static void smgrshutdown(int code, Datum arg);
|
static void smgrshutdown(int code, Datum arg);
|
||||||
static void add_to_unowned_list(SMgrRelation reln);
|
|
||||||
static void remove_from_unowned_list(SMgrRelation reln);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -149,7 +148,7 @@ smgropen(RelFileNode rnode, BackendId backend)
|
|||||||
ctl.hash = tag_hash;
|
ctl.hash = tag_hash;
|
||||||
SMgrRelationHash = hash_create("smgr relation table", 400,
|
SMgrRelationHash = hash_create("smgr relation table", 400,
|
||||||
&ctl, HASH_ELEM | HASH_FUNCTION);
|
&ctl, HASH_ELEM | HASH_FUNCTION);
|
||||||
first_unowned_reln = NULL;
|
dlist_init(&unowned_relns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look up or create an entry */
|
/* Look up or create an entry */
|
||||||
@ -176,7 +175,7 @@ smgropen(RelFileNode rnode, BackendId backend)
|
|||||||
reln->md_fd[forknum] = NULL;
|
reln->md_fd[forknum] = NULL;
|
||||||
|
|
||||||
/* it has no owner yet */
|
/* it has no owner yet */
|
||||||
add_to_unowned_list(reln);
|
dlist_push_tail(&unowned_relns, &reln->node);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reln;
|
return reln;
|
||||||
@ -206,7 +205,7 @@ smgrsetowner(SMgrRelation *owner, SMgrRelation reln)
|
|||||||
if (reln->smgr_owner)
|
if (reln->smgr_owner)
|
||||||
*(reln->smgr_owner) = NULL;
|
*(reln->smgr_owner) = NULL;
|
||||||
else
|
else
|
||||||
remove_from_unowned_list(reln);
|
dlist_delete(&reln->node);
|
||||||
|
|
||||||
/* Now establish the ownership relationship. */
|
/* Now establish the ownership relationship. */
|
||||||
reln->smgr_owner = owner;
|
reln->smgr_owner = owner;
|
||||||
@ -230,53 +229,8 @@ smgrclearowner(SMgrRelation *owner, SMgrRelation reln)
|
|||||||
/* unset our reference to the owner */
|
/* unset our reference to the owner */
|
||||||
reln->smgr_owner = NULL;
|
reln->smgr_owner = NULL;
|
||||||
|
|
||||||
add_to_unowned_list(reln);
|
/* add to list of unowned relations */
|
||||||
}
|
dlist_push_tail(&unowned_relns, &reln->node);
|
||||||
|
|
||||||
/*
|
|
||||||
* add_to_unowned_list -- link an SMgrRelation onto the unowned list
|
|
||||||
*
|
|
||||||
* Check remove_from_unowned_list()'s comments for performance
|
|
||||||
* considerations.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
add_to_unowned_list(SMgrRelation reln)
|
|
||||||
{
|
|
||||||
/* place it at head of the list (to make smgrsetowner cheap) */
|
|
||||||
reln->next_unowned_reln = first_unowned_reln;
|
|
||||||
first_unowned_reln = reln;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* remove_from_unowned_list -- unlink an SMgrRelation from the unowned list
|
|
||||||
*
|
|
||||||
* If the reln is not present in the list, nothing happens. Typically this
|
|
||||||
* would be caller error, but there seems no reason to throw an error.
|
|
||||||
*
|
|
||||||
* In the worst case this could be rather slow; but in all the cases that seem
|
|
||||||
* likely to be performance-critical, the reln being sought will actually be
|
|
||||||
* first in the list. Furthermore, the number of unowned relns touched in any
|
|
||||||
* one transaction shouldn't be all that high typically. So it doesn't seem
|
|
||||||
* worth expending the additional space and management logic needed for a
|
|
||||||
* doubly-linked list.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
remove_from_unowned_list(SMgrRelation reln)
|
|
||||||
{
|
|
||||||
SMgrRelation *link;
|
|
||||||
SMgrRelation cur;
|
|
||||||
|
|
||||||
for (link = &first_unowned_reln, cur = *link;
|
|
||||||
cur != NULL;
|
|
||||||
link = &cur->next_unowned_reln, cur = *link)
|
|
||||||
{
|
|
||||||
if (cur == reln)
|
|
||||||
{
|
|
||||||
*link = cur->next_unowned_reln;
|
|
||||||
cur->next_unowned_reln = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -303,7 +257,7 @@ smgrclose(SMgrRelation reln)
|
|||||||
owner = reln->smgr_owner;
|
owner = reln->smgr_owner;
|
||||||
|
|
||||||
if (!owner)
|
if (!owner)
|
||||||
remove_from_unowned_list(reln);
|
dlist_delete(&reln->node);
|
||||||
|
|
||||||
if (hash_search(SMgrRelationHash,
|
if (hash_search(SMgrRelationHash,
|
||||||
(void *) &(reln->smgr_rnode),
|
(void *) &(reln->smgr_rnode),
|
||||||
@ -783,13 +737,19 @@ smgrpostckpt(void)
|
|||||||
void
|
void
|
||||||
AtEOXact_SMgr(void)
|
AtEOXact_SMgr(void)
|
||||||
{
|
{
|
||||||
|
dlist_mutable_iter iter;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Zap all unowned SMgrRelations. We rely on smgrclose() to remove each
|
* Zap all unowned SMgrRelations. We rely on smgrclose() to remove each
|
||||||
* one from the list.
|
* one from the list.
|
||||||
*/
|
*/
|
||||||
while (first_unowned_reln != NULL)
|
dlist_foreach_modify(iter, &unowned_relns)
|
||||||
{
|
{
|
||||||
Assert(first_unowned_reln->smgr_owner == NULL);
|
SMgrRelation rel = dlist_container(SMgrRelationData, node,
|
||||||
smgrclose(first_unowned_reln);
|
iter.cur);
|
||||||
|
|
||||||
|
Assert(rel->smgr_owner == NULL);
|
||||||
|
|
||||||
|
smgrclose(rel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#define SMGR_H
|
#define SMGR_H
|
||||||
|
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
|
#include "lib/ilist.h"
|
||||||
#include "storage/block.h"
|
#include "storage/block.h"
|
||||||
#include "storage/relfilenode.h"
|
#include "storage/relfilenode.h"
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ typedef struct SMgrRelationData
|
|||||||
struct _MdfdVec *md_fd[MAX_FORKNUM + 1];
|
struct _MdfdVec *md_fd[MAX_FORKNUM + 1];
|
||||||
|
|
||||||
/* if unowned, list link in list of all unowned SMgrRelations */
|
/* if unowned, list link in list of all unowned SMgrRelations */
|
||||||
struct SMgrRelationData *next_unowned_reln;
|
dlist_node node;
|
||||||
} SMgrRelationData;
|
} SMgrRelationData;
|
||||||
|
|
||||||
typedef SMgrRelationData *SMgrRelation;
|
typedef SMgrRelationData *SMgrRelation;
|
||||||
|
Reference in New Issue
Block a user