On 3/10/19 9:09 PM, Alvaro Herrera wrote: > On 2019-Feb-07, Tomas Vondra wrote: > >> Attached is a WIP patch removing the optimization from DropRelationFiles >> and adding it to smgrDoPendingDeletes. This resolves the issue, at least >> in the cases I've been able to reproduce. But maybe we should deal with >> this issue earlier by ensuring the two lists are ordered the same way >> from the very beginning, somehow. > > I noticed that this patch isn't in the commitfest. Are you planning to > push it soon? >
I wasn't planning to push anything particularly soon, for two reasons: Firstly, the issue is not particularly pressing except with non-trivial number of relations (and I only noticed that during benchmarking). Secondly, I still have a feeling I'm missing something about b4166911 because for me that commit does not actually fix the issue - i.e. I can create a lot of relations in a transaction, abort it, and observe that the replica actually accesses the relations in exactly the wrong order. So that commit does not seem to actually fix anything. Attached is a patch adopting the dlist approach - it seems to be working quite fine, and is a bit cleaner than just slapping another pointer into the SMgrRelationData struct. So I'd say this is the way to go. I see b4166911 was actually backpatched to all supported versions, on the basis that it fixes oversight in 279628a0a7. So I suppose this fix should also be backpatched. regards -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c index 0c0bba4ab3..10e84dc571 100644 --- a/src/backend/storage/smgr/smgr.c +++ b/src/backend/storage/smgr/smgr.c @@ -18,6 +18,7 @@ #include "postgres.h" #include "commands/tablespace.h" +#include "lib/ilist.h" #include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/smgr.h" @@ -97,7 +98,7 @@ static const int NSmgr = lengthof(smgrsw); */ static HTAB *SMgrRelationHash = NULL; -static SMgrRelation first_unowned_reln = NULL; +static dlist_head unowned_relns; /* local function prototypes */ static void smgrshutdown(int code, Datum arg); @@ -165,7 +166,7 @@ smgropen(RelFileNode rnode, BackendId backend) ctl.entrysize = sizeof(SMgrRelationData); SMgrRelationHash = hash_create("smgr relation table", 400, &ctl, HASH_ELEM | HASH_BLOBS); - first_unowned_reln = NULL; + dlist_init(&unowned_relns); } /* Look up or create an entry */ @@ -258,9 +259,7 @@ smgrclearowner(SMgrRelation *owner, SMgrRelation reln) 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; + dlist_push_tail(&unowned_relns, &reln->node); } /* @@ -268,31 +267,19 @@ add_to_unowned_list(SMgrRelation reln) * * 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; + /* if not in the list, do nothing */ + if ((reln->node.prev == NULL) && (reln->node.next == NULL)) + return; - 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; - } - } + dlist_delete(&reln->node); + + /* reset the dlist pointers */ + reln->node.prev = NULL; + reln->node.next = NULL; } /* @@ -812,13 +799,19 @@ smgrpostckpt(void) void AtEOXact_SMgr(void) { + dlist_mutable_iter iter; + /* * Zap all unowned SMgrRelations. We rely on smgrclose() to remove each * one from the list. */ - while (first_unowned_reln != NULL) + dlist_foreach_modify(iter, &unowned_relns) { - Assert(first_unowned_reln->smgr_owner == NULL); - smgrclose(first_unowned_reln); + SMgrRelation rel = dlist_container(SMgrRelationData, node, + iter.cur); + + Assert(rel->smgr_owner == NULL); + + smgrclose(rel); } } diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h index 4d60b28dac..8e98273878 100644 --- a/src/include/storage/smgr.h +++ b/src/include/storage/smgr.h @@ -14,6 +14,7 @@ #ifndef SMGR_H #define SMGR_H +#include "lib/ilist.h" #include "storage/block.h" #include "storage/relfilenode.h" @@ -71,7 +72,7 @@ typedef struct SMgrRelationData struct _MdfdVec *md_seg_fds[MAX_FORKNUM + 1]; /* if unowned, list link in list of all unowned SMgrRelations */ - struct SMgrRelationData *next_unowned_reln; + dlist_node node; } SMgrRelationData; typedef SMgrRelationData *SMgrRelation;