Hello all, Current implementation of ResourceOwner uses arrays to store resources like TupleDescs, Snapshots, etc. When we want to release one of these resources ResourceOwner finds it with linear scan. Granted, resource array are usually small so its not a problem most of the time. But it appears to be a bottleneck when we are working with tables which have a lot of partitions.
To reproduce this issue: 1. run `./gen.pl 10000 | psql my_database postgres` 2. run `pgbench -j 8 -c 8 -f q.sql -T 100 my_database` 3. in second terminal run `sudo perf top -u postgres` Both gen.pl and q.sql are attached to this message. You will see that postgres spends a lot of time in ResourceOwnerForget* procedures: 32.80% postgres [.] list_nth 20.29% postgres [.] ResourceOwnerForgetRelationRef 12.87% postgres [.] find_all_inheritors 7.90% postgres [.] get_tabstat_entry 6.68% postgres [.] ResourceOwnerForgetTupleDesc 1.17% postgres [.] hash_search_with_hash_value ... < 1% ... I would like to suggest a patch (see attachment) witch fixes this bottleneck. Also I discovered that there is a lot of code duplication in ResourceOwner. Patch fixes this too. The idea is quite simple. I just replaced arrays with something that could be considered hash tables, but with some specific optimizations. After applying this patch we can see that bottleneck is gone: 42.89% postgres [.] list_nth 18.30% postgres [.] find_all_inheritors 10.97% postgres [.] get_tabstat_entry 1.82% postgres [.] hash_search_with_hash_value 1.21% postgres [.] SearchCatCache ... < 1% ... For tables with thousands partitions we gain in average 32.5% more TPS. As far as I can see in the same time patch doesn't make anything worse. `make check` passes with asserts enabled and disabled. There is no performance degradation according to both standard pgbench benchmark and benchmark described above for tables with 10 and 100 partitions. Best regards, Aleksander
gen.pl
Description: Perl program
q.sql
Description: application/sql
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c index 0e7acbf..6abbfa5 100644 --- a/src/backend/utils/resowner/resowner.c +++ b/src/backend/utils/resowner/resowner.c @@ -47,62 +47,296 @@ #define MAX_RESOWNER_LOCKS 15 /* - * ResourceOwner objects look like this + * This number is used as initial size of resource arrays (like buffers, + * catrefs, etc) in ResourceOwnerData structure. If given number of items is + * not enough, we double array size and reallocate memory. + * + * Should be power of two since we use (arrsize -1) as mask for hash value. + * */ -typedef struct ResourceOwnerData +#define RESARRAY_INIT_SIZE 16 + +/* + * How many items could be stored in a resource array of given capacity. If + * this number is reached we need to resize an array to prevent hash collisions. + * + * This computation actually costs only two additions and one binary shift. + */ +#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4) + +/* + * Common structure for storing different type of resources. + */ +typedef struct ResourceArray +{ + uint8* itemsarr; /* buffer for storing values */ + uint32 itemsizelg: 2; /* sizeof one item log 2 */ + uint32 capacity: 30; /* capacity of array */ + uint32 nitems; /* how many items is stored in items array */ + uint32 maxitems; /* precalculated RESARRAY_MAX_ITEMS(capacity) */ +} ResourceArray; + +/* + * Such type of callback function is called when resource stored in + * ResourceArray is released using ResourceArrayFree. Callback should + * _actually_ release a resource so nitems value will be decremented. + */ +typedef void (*ResourceArrayRemoveCallback)(const uint8* ref, bool isCommit); + +/* Used as argument to memcmp to determine if ResourceArray[i] is free. */ +static const uint8 RESOURCE_ARRAY_ZERO_ELEMENT[sizeof(void*)] = {0}; + +/* + * Calculate hash_any of given data. For uint32 values use faster hash_uint32. + */ +static Datum +ResourceArrayHash(const uint8* data, int size) +{ + uint32 tmp; + + Assert(size == sizeof(uint32) || size == sizeof(void*)); + + if(size == sizeof(uint32)) + { + tmp = *((const uint32*)data); + return hash_uint32(tmp); + } + else + { + return hash_any(data, size); + } +} + +/* + * Initialize ResourceArray + */ +static void +ResourceArrayInit(ResourceArray *resarr, uint32 itemsize) { + Assert(itemsize == sizeof(int) || itemsize == sizeof(void*)); + Assert(resarr->itemsarr == NULL); + Assert(resarr->capacity == 0); + Assert(resarr->nitems == 0); + Assert(resarr->maxitems == 0); + + resarr->itemsizelg = 0; + while(itemsize > 1) + { + resarr->itemsizelg++; + itemsize >>= 1; + } + + Assert(resarr->itemsizelg == 2 || resarr->itemsizelg == 3); +} + +/* + * Add a resource to ResourceArray + * + * Caller must have previously done ResourceArrayEnlarge() + */ +static void +ResourceArrayAdd(ResourceArray *resarr, const uint8 *dataref) +{ + uint8 *itemptr; + Datum idx; + Datum mask = resarr->capacity - 1; + uint32 itemsize = 1 << resarr->itemsizelg; + + Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0); + Assert(resarr->maxitems > resarr->nitems); + Assert(resarr->capacity > 0); + Assert(resarr->itemsarr != NULL); + + /* + * Hashing is quite expensive, so we use it only for large arrays. For + * small arrays we just use a linear scan. + */ + if(resarr->capacity == RESARRAY_INIT_SIZE) + idx = resarr->nitems; + else + idx = ResourceArrayHash(dataref, itemsize); + idx &= mask; + + while(true) + { + itemptr = resarr->itemsarr + (idx << resarr->itemsizelg); + if(memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0) + break; + idx = (idx + 1) & mask; + } + + memcpy(itemptr, dataref, itemsize); + resarr->nitems++; +} + +/* + * Remove a resource from ResourceArray + * + * Returns true on success, false if resource was not found + */ +static bool +ResourceArrayRemove(ResourceArray *resarr, const uint8 *dataref) +{ + uint8 *itemptr; + Datum idx; + uint32 i; + Datum mask = resarr->capacity - 1; + uint32 itemsize = 1 << resarr->itemsizelg; + + Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0); + Assert(resarr->capacity > 0); + Assert(resarr->itemsarr != NULL); + + /* + * Hashing is quite expensive, so we use it only for large arrays. For + * small arrays we use a linear scan. + */ + if(resarr->capacity == RESARRAY_INIT_SIZE) + idx = 0; + else + idx = ResourceArrayHash(dataref, itemsize); + idx &= mask; + + for(i = 0; i < resarr->capacity; i++) + { + itemptr = resarr->itemsarr + (idx << resarr->itemsizelg); + if(memcmp(itemptr, dataref, itemsize) == 0) + { + memset(itemptr, 0, itemsize); + resarr->nitems--; + return true; + } + idx = (idx + 1) & mask; + } + + return false; +} + +/* + * Make sure there is a room for at least one more resource in an array. + * + * This is separate from actually inserting a resource because if we run out + * of memory, it's critical to do so *before* acquiring the resource. + */ +static void +ResourceArrayEnlarge(ResourceArray* resarr) +{ + uint32 oldcap; + uint8* olditemsarr; + uint8* olditemptr; + uint32 itemsize = 1 << resarr->itemsizelg; + + Assert(resarr->itemsizelg != 0); + + if(resarr->nitems < resarr->maxitems) + return; /* nothing to do */ + + olditemsarr = resarr->itemsarr; + oldcap = resarr->capacity; + + resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE; + resarr->itemsarr = (uint8*) + MemoryContextAllocZero(TopMemoryContext, + resarr->capacity * itemsize); + resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity); + resarr->nitems = 0; + + if(olditemsarr != NULL) + { + while(oldcap > 0) + { + oldcap--; + /* + * Read next line as: + * + * olditemptr = &(olditemsarr[oldcap]). + * + * We use binary shift since compiler don't know that itemsize + * is always power of two. It would use multiplication instead of + * efficient binary shift in code `oldcap * itemsize`. + */ + olditemptr = olditemsarr + (oldcap << resarr->itemsizelg); + if(memcmp(olditemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0) + ResourceArrayAdd(resarr, olditemptr); + } + pfree(olditemsarr); + } +} + +/* + * Remove all resources in array and call a callback function for each release. + */ +static void +ResourceArrayRemoveAll(ResourceArray *resarr, + ResourceArrayRemoveCallback releasecb, + bool isCommit) +{ + uint32 idx = 0; + uint8* itemptr; + uint32 itemsize = 1 << resarr->itemsizelg; + + while(resarr->nitems > 0) + { + /* + * Read next line as: + * + * itemptr = &(itemsarr[idx]). + * + * We use binary shift since compiler don't know that itemsize + * is always power of two. It would use multiplication instead of + * efficient binary shift in code `oldcap * itemsize`. + */ + itemptr = resarr->itemsarr + (idx << resarr->itemsizelg); + if(memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0) + { + idx++; + continue; + } + releasecb(itemptr, isCommit); + } +} + +/* + * Return ResourceArray to initial state + */ +static void +ResourceArrayFree(ResourceArray *resarr) { - ResourceOwner parent; /* NULL if no parent (toplevel owner) */ - ResourceOwner firstchild; /* head of linked list of children */ - ResourceOwner nextchild; /* next child of same parent */ - const char *name; /* name (just for debugging) */ + Assert(resarr->nitems == 0); + + resarr->capacity = 0; + resarr->maxitems = 0; - /* We have built-in support for remembering owned buffers */ - int nbuffers; /* number of owned buffer pins */ - Buffer *buffers; /* dynamically allocated array */ - int maxbuffers; /* currently allocated array size */ + if(!resarr->itemsarr) + return; + + pfree(resarr->itemsarr); + resarr->itemsarr = NULL; +} + +/* ResourceOwner objects look like this */ +typedef struct ResourceOwnerData +{ + ResourceOwner parent; /* NULL if no parent (toplevel owner) */ + ResourceOwner firstchild; /* head of linked list of children */ + ResourceOwner nextchild; /* next child of same parent */ + const char *name; /* name (just for debugging) */ /* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */ - int nlocks; /* number of owned locks */ + int nlocks; /* number of owned locks */ LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */ - /* We have built-in support for remembering catcache references */ - int ncatrefs; /* number of owned catcache pins */ - HeapTuple *catrefs; /* dynamically allocated array */ - int maxcatrefs; /* currently allocated array size */ - - int ncatlistrefs; /* number of owned catcache-list pins */ - CatCList **catlistrefs; /* dynamically allocated array */ - int maxcatlistrefs; /* currently allocated array size */ - - /* We have built-in support for remembering relcache references */ - int nrelrefs; /* number of owned relcache pins */ - Relation *relrefs; /* dynamically allocated array */ - int maxrelrefs; /* currently allocated array size */ - - /* We have built-in support for remembering plancache references */ - int nplanrefs; /* number of owned plancache pins */ - CachedPlan **planrefs; /* dynamically allocated array */ - int maxplanrefs; /* currently allocated array size */ - - /* We have built-in support for remembering tupdesc references */ - int ntupdescs; /* number of owned tupdesc references */ - TupleDesc *tupdescs; /* dynamically allocated array */ - int maxtupdescs; /* currently allocated array size */ - - /* We have built-in support for remembering snapshot references */ - int nsnapshots; /* number of owned snapshot references */ - Snapshot *snapshots; /* dynamically allocated array */ - int maxsnapshots; /* currently allocated array size */ - - /* We have built-in support for remembering open temporary files */ - int nfiles; /* number of owned temporary files */ - File *files; /* dynamically allocated array */ - int maxfiles; /* currently allocated array size */ - - /* We have built-in support for remembering dynamic shmem segments */ - int ndsms; /* number of owned shmem segments */ - dsm_segment **dsms; /* dynamically allocated array */ - int maxdsms; /* currently allocated array size */ + /* We have built-in support for remembering: */ + + ResourceArray catrefarr; /* `HeapTuple`s */ + ResourceArray catlistrefarr; /* `ResourceOwner`s */ + ResourceArray relrefarr; /* `Relation`s */ + ResourceArray planrefarr; /* `CachedPlan*`s */ + ResourceArray tupdescarr; /* `TupleDesc`s */ + ResourceArray snapshotarr; /* `Snapshot`s */ + ResourceArray dsmarr; /* `dsm_segment*`s */ + ResourceArray bufferarr; /* `Buffer`s */ + ResourceArray filearr; /* `File`s */ + } ResourceOwnerData; @@ -168,6 +402,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name) parent->firstchild = owner; } + ResourceArrayInit(&(owner->catrefarr), sizeof(HeapTuple)); + ResourceArrayInit(&(owner->catlistrefarr), sizeof(ResourceOwner)); + ResourceArrayInit(&(owner->relrefarr), sizeof(Relation)); + ResourceArrayInit(&(owner->planrefarr), sizeof(CachedPlan*)); + ResourceArrayInit(&(owner->tupdescarr), sizeof(TupleDesc)); + ResourceArrayInit(&(owner->snapshotarr), sizeof(Snapshot)); + ResourceArrayInit(&(owner->dsmarr), sizeof(dsm_segment*)); + ResourceArrayInit(&(owner->bufferarr), sizeof(Buffer)); + ResourceArrayInit(&(owner->filearr), sizeof(File)); + return owner; } @@ -221,6 +465,87 @@ ResourceOwnerRelease(ResourceOwner owner, } static void +ReleaseCachedPlanCallback(const uint8* dataref, bool isCommit) +{ + CachedPlan* res = *((CachedPlan**)dataref); + if (isCommit) + PrintPlanCacheLeakWarning(res); + ReleaseCachedPlan(res, true); +} + +static void +ReleaseDSMCallback(const uint8* dataref, bool isCommit) +{ + dsm_segment* res = *((dsm_segment**)dataref); + if (isCommit) + PrintDSMLeakWarning(res); + dsm_detach(res); +} + +static void +ReleaseTupleDescCallback(const uint8* dataref, bool isCommit) +{ + TupleDesc res = *((TupleDesc*)dataref); + if(isCommit) + PrintTupleDescLeakWarning(res); + DecrTupleDescRefCount(res); +} + +static void +ReleaseSnapshotCallback(const uint8* dataref, bool isCommit) +{ + Snapshot res = *((Snapshot*)dataref); + if (isCommit) + PrintSnapshotLeakWarning(res); + UnregisterSnapshot(res); +} + +static void +ReleaseRelationCallback(const uint8* dataref, bool isCommit) +{ + Relation res = *((Relation*)dataref); + if (isCommit) + PrintRelCacheLeakWarning(res); + RelationClose(res); +} + +static void +ReleaseCatCacheListCallback(const uint8* dataref, bool isCommit) +{ + CatCList* res = *((CatCList**)dataref); + if (isCommit) + PrintCatCacheListLeakWarning(res); + ReleaseCatCacheList(res); +} + +static void +ReleaseCatCacheCallback(const uint8* dataref, bool isCommit) +{ + HeapTuple res = *((HeapTuple*)dataref); + if (isCommit) + PrintCatCacheLeakWarning(res); + ReleaseCatCache(res); +} + +static void +ReleaseBufferCallback(const uint8* dataref, bool isCommit) +{ + Buffer res = *((Buffer*)dataref); + if (isCommit) + PrintBufferLeakWarning(res); + ReleaseBuffer(res); +} + +static void +ReleaseFileCallback(const uint8* dataref, bool isCommit) +{ + File res = *((File*)dataref); + if (isCommit) + PrintFileLeakWarning(res); + FileClose(res); +} + +static void ResourceOwnerReleaseInternal(ResourceOwner owner, ResourceReleasePhase phase, bool isCommit, @@ -252,46 +577,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, * During a commit, there shouldn't be any remaining pins --- that * would indicate failure to clean up the executor correctly --- so * issue warnings. In the abort case, just clean up quietly. - * - * We are careful to do the releasing back-to-front, so as to avoid - * O(N^2) behavior in ResourceOwnerForgetBuffer(). */ - while (owner->nbuffers > 0) - { - if (isCommit) - PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]); - ReleaseBuffer(owner->buffers[owner->nbuffers - 1]); - } + ResourceArrayRemoveAll(&(owner->bufferarr), + ReleaseBufferCallback, isCommit); - /* - * Release relcache references. Note that RelationClose will remove - * the relref entry from my list, so I just have to iterate till there - * are none. - * - * As with buffer pins, warn if any are left at commit time, and - * release back-to-front for speed. - */ - while (owner->nrelrefs > 0) - { - if (isCommit) - PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]); - RelationClose(owner->relrefs[owner->nrelrefs - 1]); - } + /* Ditto for relcache references. */ + ResourceArrayRemoveAll(&(owner->relrefarr), + ReleaseRelationCallback, isCommit); - /* - * Release dynamic shared memory segments. Note that dsm_detach() - * will remove the segment from my list, so I just have to iterate - * until there are none. - * - * As in the preceding cases, warn if there are leftover at commit - * time. - */ - while (owner->ndsms > 0) - { - if (isCommit) - PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]); - dsm_detach(owner->dsms[owner->ndsms - 1]); - } + /* Ditto for dynamic shared memory segments */ + ResourceArrayRemoveAll(&(owner->dsmarr), + ReleaseDSMCallback, isCommit); } else if (phase == RESOURCE_RELEASE_LOCKS) { @@ -351,48 +647,28 @@ ResourceOwnerReleaseInternal(ResourceOwner owner, * As with buffer pins, warn if any are left at commit time, and * release back-to-front for speed. */ - while (owner->ncatrefs > 0) - { - if (isCommit) - PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]); - ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]); - } + ResourceArrayRemoveAll(&(owner->catrefarr), + ReleaseCatCacheCallback, isCommit); + /* Ditto for catcache lists */ - while (owner->ncatlistrefs > 0) - { - if (isCommit) - PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]); - ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]); - } + ResourceArrayRemoveAll(&(owner->catlistrefarr), + ReleaseCatCacheListCallback, isCommit); + /* Ditto for plancache references */ - while (owner->nplanrefs > 0) - { - if (isCommit) - PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]); - ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true); - } + ResourceArrayRemoveAll(&(owner->planrefarr), + ReleaseCachedPlanCallback, isCommit); + /* Ditto for tupdesc references */ - while (owner->ntupdescs > 0) - { - if (isCommit) - PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]); - DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]); - } + ResourceArrayRemoveAll(&(owner->tupdescarr), + ReleaseTupleDescCallback, isCommit); + /* Ditto for snapshot references */ - while (owner->nsnapshots > 0) - { - if (isCommit) - PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]); - UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]); - } + ResourceArrayRemoveAll(&(owner->snapshotarr), + ReleaseSnapshotCallback, isCommit); /* Ditto for temporary files */ - while (owner->nfiles > 0) - { - if (isCommit) - PrintFileLeakWarning(owner->files[owner->nfiles - 1]); - FileClose(owner->files[owner->nfiles - 1]); - } + ResourceArrayRemoveAll(&(owner->filearr), + ReleaseFileCallback, isCommit); /* Clean up index scans too */ ReleaseResources_hash(); @@ -418,16 +694,7 @@ ResourceOwnerDelete(ResourceOwner owner) Assert(owner != CurrentResourceOwner); /* And it better not own any resources, either */ - Assert(owner->nbuffers == 0); Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1); - Assert(owner->ncatrefs == 0); - Assert(owner->ncatlistrefs == 0); - Assert(owner->nrelrefs == 0); - Assert(owner->ndsms == 0); - Assert(owner->nplanrefs == 0); - Assert(owner->ntupdescs == 0); - Assert(owner->nsnapshots == 0); - Assert(owner->nfiles == 0); /* * Delete children. The recursive call will delink the child from me, so @@ -444,25 +711,15 @@ ResourceOwnerDelete(ResourceOwner owner) ResourceOwnerNewParent(owner, NULL); /* And free the object. */ - if (owner->buffers) - pfree(owner->buffers); - if (owner->catrefs) - pfree(owner->catrefs); - if (owner->catlistrefs) - pfree(owner->catlistrefs); - if (owner->relrefs) - pfree(owner->relrefs); - if (owner->planrefs) - pfree(owner->planrefs); - if (owner->tupdescs) - pfree(owner->tupdescs); - if (owner->snapshots) - pfree(owner->snapshots); - if (owner->files) - pfree(owner->files); - if (owner->dsms) - pfree(owner->dsms); - + ResourceArrayFree(&(owner->catrefarr)); + ResourceArrayFree(&(owner->catlistrefarr)); + ResourceArrayFree(&(owner->relrefarr)); + ResourceArrayFree(&(owner->planrefarr)); + ResourceArrayFree(&(owner->tupdescarr)); + ResourceArrayFree(&(owner->snapshotarr)); + ResourceArrayFree(&(owner->dsmarr)); + ResourceArrayFree(&(owner->bufferarr)); + ResourceArrayFree(&(owner->filearr)); pfree(owner); } @@ -575,26 +832,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg) void ResourceOwnerEnlargeBuffers(ResourceOwner owner) { - int newmax; - - if (owner == NULL || - owner->nbuffers < owner->maxbuffers) - return; /* nothing to do */ - - if (owner->buffers == NULL) - { - newmax = 16; - owner->buffers = (Buffer *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer)); - owner->maxbuffers = newmax; - } - else - { - newmax = owner->maxbuffers * 2; - owner->buffers = (Buffer *) - repalloc(owner->buffers, newmax * sizeof(Buffer)); - owner->maxbuffers = newmax; - } + if (owner == NULL) + return; + ResourceArrayEnlarge(&(owner->bufferarr)); } /* @@ -608,12 +848,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner) void ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer) { - if (owner != NULL) - { - Assert(owner->nbuffers < owner->maxbuffers); - owner->buffers[owner->nbuffers] = buffer; - owner->nbuffers++; - } + if (owner == NULL) + return; + ResourceArrayAdd(&(owner->bufferarr), (const uint8*)&buffer); } /* @@ -625,33 +862,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer) void ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer) { - if (owner != NULL) - { - Buffer *buffers = owner->buffers; - int nb1 = owner->nbuffers - 1; - int i; + bool res; - /* - * Scan back-to-front because it's more likely we are releasing a - * recently pinned buffer. This isn't always the case of course, but - * it's the way to bet. - */ - for (i = nb1; i >= 0; i--) - { - if (buffers[i] == buffer) - { - while (i < nb1) - { - buffers[i] = buffers[i + 1]; - i++; - } - owner->nbuffers = nb1; - return; - } - } + if (owner == NULL) + return; + + res = ResourceArrayRemove(&(owner->bufferarr), (const uint8*)&buffer); + if(!res) elog(ERROR, "buffer %d is not owned by resource owner %s", buffer, owner->name); - } } /* @@ -667,6 +886,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer) void ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock) { + Assert(locallock != NULL); + if (owner->nlocks > MAX_RESOWNER_LOCKS) return; /* we have already overflowed */ @@ -714,25 +935,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock) void ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner) { - int newmax; - - if (owner->ncatrefs < owner->maxcatrefs) - return; /* nothing to do */ - - if (owner->catrefs == NULL) - { - newmax = 16; - owner->catrefs = (HeapTuple *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple)); - owner->maxcatrefs = newmax; - } - else - { - newmax = owner->maxcatrefs * 2; - owner->catrefs = (HeapTuple *) - repalloc(owner->catrefs, newmax * sizeof(HeapTuple)); - owner->maxcatrefs = newmax; - } + ResourceArrayEnlarge(&(owner->catrefarr)); } /* @@ -743,9 +946,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner) void ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple) { - Assert(owner->ncatrefs < owner->maxcatrefs); - owner->catrefs[owner->ncatrefs] = tuple; - owner->ncatrefs++; + ResourceArrayAdd(&(owner->catrefarr), (const uint8*)&tuple); } /* @@ -754,25 +955,11 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple) void ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple) { - HeapTuple *catrefs = owner->catrefs; - int nc1 = owner->ncatrefs - 1; - int i; - - for (i = nc1; i >= 0; i--) - { - if (catrefs[i] == tuple) - { - while (i < nc1) - { - catrefs[i] = catrefs[i + 1]; - i++; - } - owner->ncatrefs = nc1; - return; - } - } - elog(ERROR, "catcache reference %p is not owned by resource owner %s", - tuple, owner->name); + bool res = ResourceArrayRemove(&(owner->catrefarr), + (const uint8*)&tuple); + if(!res) + elog(ERROR, "catcache reference %p is not owned by resource owner %s", + tuple, owner->name); } /* @@ -785,25 +972,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple) void ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner) { - int newmax; - - if (owner->ncatlistrefs < owner->maxcatlistrefs) - return; /* nothing to do */ - - if (owner->catlistrefs == NULL) - { - newmax = 16; - owner->catlistrefs = (CatCList **) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *)); - owner->maxcatlistrefs = newmax; - } - else - { - newmax = owner->maxcatlistrefs * 2; - owner->catlistrefs = (CatCList **) - repalloc(owner->catlistrefs, newmax * sizeof(CatCList *)); - owner->maxcatlistrefs = newmax; - } + ResourceArrayEnlarge(&(owner->catlistrefarr)); } /* @@ -814,9 +983,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner) void ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list) { - Assert(owner->ncatlistrefs < owner->maxcatlistrefs); - owner->catlistrefs[owner->ncatlistrefs] = list; - owner->ncatlistrefs++; + ResourceArrayAdd(&(owner->catlistrefarr), (const uint8*)&list); } /* @@ -825,25 +992,11 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list) void ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list) { - CatCList **catlistrefs = owner->catlistrefs; - int nc1 = owner->ncatlistrefs - 1; - int i; - - for (i = nc1; i >= 0; i--) - { - if (catlistrefs[i] == list) - { - while (i < nc1) - { - catlistrefs[i] = catlistrefs[i + 1]; - i++; - } - owner->ncatlistrefs = nc1; - return; - } - } - elog(ERROR, "catcache list reference %p is not owned by resource owner %s", - list, owner->name); + bool res = ResourceArrayRemove(&(owner->catlistrefarr), + (const uint8*)&list); + if(!res) + elog(ERROR, "catcache list reference %p is not owned by resource owner %s", + list, owner->name); } /* @@ -856,25 +1009,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list) void ResourceOwnerEnlargeRelationRefs(ResourceOwner owner) { - int newmax; - - if (owner->nrelrefs < owner->maxrelrefs) - return; /* nothing to do */ - - if (owner->relrefs == NULL) - { - newmax = 16; - owner->relrefs = (Relation *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation)); - owner->maxrelrefs = newmax; - } - else - { - newmax = owner->maxrelrefs * 2; - owner->relrefs = (Relation *) - repalloc(owner->relrefs, newmax * sizeof(Relation)); - owner->maxrelrefs = newmax; - } + ResourceArrayEnlarge(&(owner->relrefarr)); } /* @@ -885,9 +1020,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner) void ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel) { - Assert(owner->nrelrefs < owner->maxrelrefs); - owner->relrefs[owner->nrelrefs] = rel; - owner->nrelrefs++; + ResourceArrayAdd(&(owner->relrefarr), (const uint8*)&rel); } /* @@ -896,25 +1029,11 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel) void ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel) { - Relation *relrefs = owner->relrefs; - int nr1 = owner->nrelrefs - 1; - int i; - - for (i = nr1; i >= 0; i--) - { - if (relrefs[i] == rel) - { - while (i < nr1) - { - relrefs[i] = relrefs[i + 1]; - i++; - } - owner->nrelrefs = nr1; - return; - } - } - elog(ERROR, "relcache reference %s is not owned by resource owner %s", - RelationGetRelationName(rel), owner->name); + bool res = ResourceArrayRemove(&(owner->relrefarr), + (const uint8*)&rel); + if(!res) + elog(ERROR, "relcache reference %s is not owned by resource owner %s", + RelationGetRelationName(rel), owner->name); } /* @@ -937,25 +1056,7 @@ PrintRelCacheLeakWarning(Relation rel) void ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner) { - int newmax; - - if (owner->nplanrefs < owner->maxplanrefs) - return; /* nothing to do */ - - if (owner->planrefs == NULL) - { - newmax = 16; - owner->planrefs = (CachedPlan **) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *)); - owner->maxplanrefs = newmax; - } - else - { - newmax = owner->maxplanrefs * 2; - owner->planrefs = (CachedPlan **) - repalloc(owner->planrefs, newmax * sizeof(CachedPlan *)); - owner->maxplanrefs = newmax; - } + ResourceArrayEnlarge(&(owner->planrefarr)); } /* @@ -966,9 +1067,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner) void ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan) { - Assert(owner->nplanrefs < owner->maxplanrefs); - owner->planrefs[owner->nplanrefs] = plan; - owner->nplanrefs++; + ResourceArrayAdd(&(owner->planrefarr), (const uint8*)&plan); } /* @@ -977,25 +1076,11 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan) void ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan) { - CachedPlan **planrefs = owner->planrefs; - int np1 = owner->nplanrefs - 1; - int i; - - for (i = np1; i >= 0; i--) - { - if (planrefs[i] == plan) - { - while (i < np1) - { - planrefs[i] = planrefs[i + 1]; - i++; - } - owner->nplanrefs = np1; - return; - } - } - elog(ERROR, "plancache reference %p is not owned by resource owner %s", - plan, owner->name); + bool res = ResourceArrayRemove(&(owner->planrefarr), + (const uint8*)&plan); + if(!res) + elog(ERROR, "plancache reference %p is not owned by resource owner %s", + plan, owner->name); } /* @@ -1017,25 +1102,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan) void ResourceOwnerEnlargeTupleDescs(ResourceOwner owner) { - int newmax; - - if (owner->ntupdescs < owner->maxtupdescs) - return; /* nothing to do */ - - if (owner->tupdescs == NULL) - { - newmax = 16; - owner->tupdescs = (TupleDesc *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc)); - owner->maxtupdescs = newmax; - } - else - { - newmax = owner->maxtupdescs * 2; - owner->tupdescs = (TupleDesc *) - repalloc(owner->tupdescs, newmax * sizeof(TupleDesc)); - owner->maxtupdescs = newmax; - } + ResourceArrayEnlarge(&(owner->tupdescarr)); } /* @@ -1046,9 +1113,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner) void ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc) { - Assert(owner->ntupdescs < owner->maxtupdescs); - owner->tupdescs[owner->ntupdescs] = tupdesc; - owner->ntupdescs++; + ResourceArrayAdd(&(owner->tupdescarr), (const uint8*)&tupdesc); } /* @@ -1057,25 +1122,11 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc) void ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc) { - TupleDesc *tupdescs = owner->tupdescs; - int nt1 = owner->ntupdescs - 1; - int i; - - for (i = nt1; i >= 0; i--) - { - if (tupdescs[i] == tupdesc) - { - while (i < nt1) - { - tupdescs[i] = tupdescs[i + 1]; - i++; - } - owner->ntupdescs = nt1; - return; - } - } - elog(ERROR, "tupdesc reference %p is not owned by resource owner %s", - tupdesc, owner->name); + bool res = ResourceArrayRemove(&(owner->tupdescarr), + (const uint8*)&tupdesc); + if(!res) + elog(ERROR, "tupdesc reference %p is not owned by resource owner %s", + tupdesc, owner->name); } /* @@ -1099,25 +1150,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc) void ResourceOwnerEnlargeSnapshots(ResourceOwner owner) { - int newmax; - - if (owner->nsnapshots < owner->maxsnapshots) - return; /* nothing to do */ - - if (owner->snapshots == NULL) - { - newmax = 16; - owner->snapshots = (Snapshot *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot)); - owner->maxsnapshots = newmax; - } - else - { - newmax = owner->maxsnapshots * 2; - owner->snapshots = (Snapshot *) - repalloc(owner->snapshots, newmax * sizeof(Snapshot)); - owner->maxsnapshots = newmax; - } + ResourceArrayEnlarge(&(owner->snapshotarr)); } /* @@ -1128,9 +1161,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner) void ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot) { - Assert(owner->nsnapshots < owner->maxsnapshots); - owner->snapshots[owner->nsnapshots] = snapshot; - owner->nsnapshots++; + ResourceArrayAdd(&(owner->snapshotarr), (const uint8*)&snapshot); } /* @@ -1139,25 +1170,11 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot) void ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot) { - Snapshot *snapshots = owner->snapshots; - int ns1 = owner->nsnapshots - 1; - int i; - - for (i = ns1; i >= 0; i--) - { - if (snapshots[i] == snapshot) - { - while (i < ns1) - { - snapshots[i] = snapshots[i + 1]; - i++; - } - owner->nsnapshots = ns1; - return; - } - } - elog(ERROR, "snapshot reference %p is not owned by resource owner %s", - snapshot, owner->name); + bool res = ResourceArrayRemove(&(owner->snapshotarr), + (const uint8*)&snapshot); + if(!res) + elog(ERROR, "snapshot reference %p is not owned by resource owner %s", + snapshot, owner->name); } /* @@ -1182,25 +1199,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot) void ResourceOwnerEnlargeFiles(ResourceOwner owner) { - int newmax; - - if (owner->nfiles < owner->maxfiles) - return; /* nothing to do */ - - if (owner->files == NULL) - { - newmax = 16; - owner->files = (File *) - MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File)); - owner->maxfiles = newmax; - } - else - { - newmax = owner->maxfiles * 2; - owner->files = (File *) - repalloc(owner->files, newmax * sizeof(File)); - owner->maxfiles = newmax; - } + ResourceArrayEnlarge(&(owner->filearr)); } /* @@ -1211,9 +1210,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner) void ResourceOwnerRememberFile(ResourceOwner owner, File file) { - Assert(owner->nfiles < owner->maxfiles); - owner->files[owner->nfiles] = file; - owner->nfiles++; + ResourceArrayAdd(&(owner->filearr), (const uint8*)&file); } /* @@ -1222,25 +1219,10 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file) void ResourceOwnerForgetFile(ResourceOwner owner, File file) { - File *files = owner->files; - int ns1 = owner->nfiles - 1; - int i; - - for (i = ns1; i >= 0; i--) - { - if (files[i] == file) - { - while (i < ns1) - { - files[i] = files[i + 1]; - i++; - } - owner->nfiles = ns1; - return; - } - } - elog(ERROR, "temporery file %d is not owned by resource owner %s", - file, owner->name); + bool res = ResourceArrayRemove(&(owner->filearr), (const uint8*)&file); + if(!res) + elog(ERROR, "temporery file %d is not owned by resource owner %s", + file, owner->name); } @@ -1265,26 +1247,7 @@ PrintFileLeakWarning(File file) void ResourceOwnerEnlargeDSMs(ResourceOwner owner) { - int newmax; - - if (owner->ndsms < owner->maxdsms) - return; /* nothing to do */ - - if (owner->dsms == NULL) - { - newmax = 16; - owner->dsms = (dsm_segment **) - MemoryContextAlloc(TopMemoryContext, - newmax * sizeof(dsm_segment *)); - owner->maxdsms = newmax; - } - else - { - newmax = owner->maxdsms * 2; - owner->dsms = (dsm_segment **) - repalloc(owner->dsms, newmax * sizeof(dsm_segment *)); - owner->maxdsms = newmax; - } + ResourceArrayEnlarge(&(owner->dsmarr)); } /* @@ -1295,9 +1258,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner) void ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg) { - Assert(owner->ndsms < owner->maxdsms); - owner->dsms[owner->ndsms] = seg; - owner->ndsms++; + ResourceArrayAdd(&(owner->dsmarr), (const uint8*)&seg); } /* @@ -1306,26 +1267,10 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg) void ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg) { - dsm_segment **dsms = owner->dsms; - int ns1 = owner->ndsms - 1; - int i; - - for (i = ns1; i >= 0; i--) - { - if (dsms[i] == seg) - { - while (i < ns1) - { - dsms[i] = dsms[i + 1]; - i++; - } - owner->ndsms = ns1; - return; - } - } - elog(ERROR, - "dynamic shared memory segment %u is not owned by resource owner %s", - dsm_segment_handle(seg), owner->name); + bool res = ResourceArrayRemove(&(owner->dsmarr), (const uint8*)&seg); + if(!res) + elog(ERROR, "dynamic shared memory segment %u is not owned by resource" + " owner %s", dsm_segment_handle(seg), owner->name); }
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers