Be more paranoid about OOM in search_path cache.
Recent commitf26c2368dc
introduced a search_path cache, but left some potential out-of-memory hazards. Simplify the code and make it safer against OOM. This change reintroduces one list_copy(), losing a small amount of the performance gained inf26c2368dc
. A future change may optimize away the list_copy() again if it can be done in a safer way. Discussion: https://postgr.es/m/e6fded24cb8a2c53d4ef069d9f69cc7baaafe9ef.camel@j-davis.com
This commit is contained in:
parent
3650e7a393
commit
8efa301532
|
@ -279,42 +279,23 @@ spcachekey_equal(SearchPathCacheKey a, SearchPathCacheKey b)
|
|||
static nsphash_hash * SearchPathCache = NULL;
|
||||
|
||||
/*
|
||||
* Create search path cache.
|
||||
* Create or reset search_path cache as necessary.
|
||||
*/
|
||||
static void
|
||||
spcache_init(void)
|
||||
{
|
||||
Assert(SearchPathCacheContext);
|
||||
|
||||
if (SearchPathCache)
|
||||
if (SearchPathCache && searchPathCacheValid &&
|
||||
SearchPathCache->members < SPCACHE_RESET_THRESHOLD)
|
||||
return;
|
||||
|
||||
MemoryContextReset(SearchPathCacheContext);
|
||||
/* arbitrary initial starting size of 16 elements */
|
||||
SearchPathCache = nsphash_create(SearchPathCacheContext, 16, NULL);
|
||||
searchPathCacheValid = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset and reinitialize search path cache.
|
||||
*/
|
||||
static void
|
||||
spcache_reset(void)
|
||||
{
|
||||
Assert(SearchPathCacheContext);
|
||||
Assert(SearchPathCache);
|
||||
|
||||
MemoryContextReset(SearchPathCacheContext);
|
||||
SearchPathCache = NULL;
|
||||
|
||||
spcache_init();
|
||||
}
|
||||
|
||||
static uint32
|
||||
spcache_members(void)
|
||||
{
|
||||
return SearchPathCache->members;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up or insert entry in search path cache.
|
||||
*
|
||||
|
@ -325,27 +306,25 @@ static SearchPathCacheEntry *
|
|||
spcache_insert(const char *searchPath, Oid roleid)
|
||||
{
|
||||
SearchPathCacheEntry *entry;
|
||||
bool found;
|
||||
SearchPathCacheKey cachekey = {
|
||||
.searchPath = searchPath,
|
||||
.roleid = roleid
|
||||
};
|
||||
|
||||
/*
|
||||
* If a new entry is created, we must ensure that it's properly
|
||||
* initialized. Set the cache invalid temporarily, so that if the
|
||||
* MemoryContextStrdup() below raises an OOM, the cache will be reset on
|
||||
* the next use, clearing the uninitialized entry.
|
||||
* searchPath is not saved in SearchPathCacheContext. First perform a
|
||||
* lookup, and copy searchPath only if we need to create a new entry.
|
||||
*/
|
||||
searchPathCacheValid = false;
|
||||
entry = nsphash_lookup(SearchPathCache, cachekey);
|
||||
|
||||
entry = nsphash_insert(SearchPathCache, cachekey, &found);
|
||||
|
||||
/* ensure that key is initialized and the rest is zeroed */
|
||||
if (!found)
|
||||
if (!entry)
|
||||
{
|
||||
entry->key.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
|
||||
entry->key.roleid = roleid;
|
||||
bool found;
|
||||
|
||||
cachekey.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
|
||||
entry = nsphash_insert(SearchPathCache, cachekey, &found);
|
||||
Assert(!found);
|
||||
|
||||
entry->oidlist = NIL;
|
||||
entry->finalPath = NIL;
|
||||
entry->firstNS = InvalidOid;
|
||||
|
@ -354,7 +333,6 @@ spcache_insert(const char *searchPath, Oid roleid)
|
|||
/* do not touch entry->status, used by simplehash */
|
||||
}
|
||||
|
||||
searchPathCacheValid = true;
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
@ -4183,31 +4161,15 @@ finalNamespacePath(List *oidlist, Oid *firstNS)
|
|||
/*
|
||||
* Retrieve search path information from the cache; or if not there, fill
|
||||
* it. The returned entry is valid only until the next call to this function.
|
||||
*
|
||||
* We also determine if the newly-computed finalPath is the same as the
|
||||
* prevPath passed by the caller (i.e. a no-op or a real change?). It's more
|
||||
* efficient to check for a change in this function than the caller, because
|
||||
* we can avoid unnecessary temporary copies of the previous path.
|
||||
*/
|
||||
static const SearchPathCacheEntry *
|
||||
cachedNamespacePath(const char *searchPath, Oid roleid, List *prevPath,
|
||||
bool *same)
|
||||
cachedNamespacePath(const char *searchPath, Oid roleid)
|
||||
{
|
||||
MemoryContext oldcxt;
|
||||
SearchPathCacheEntry *entry;
|
||||
List *prevPathCopy = NIL;
|
||||
|
||||
spcache_init();
|
||||
|
||||
/* invalidate cache if necessary */
|
||||
if (!searchPathCacheValid || spcache_members() >= SPCACHE_RESET_THRESHOLD)
|
||||
{
|
||||
/* prevPath will be destroyed; make temp copy for later comparison */
|
||||
prevPathCopy = list_copy(prevPath);
|
||||
prevPath = prevPathCopy;
|
||||
spcache_reset();
|
||||
}
|
||||
|
||||
entry = spcache_insert(searchPath, roleid);
|
||||
|
||||
/*
|
||||
|
@ -4232,38 +4194,22 @@ cachedNamespacePath(const char *searchPath, Oid roleid, List *prevPath,
|
|||
if (entry->finalPath == NIL || object_access_hook ||
|
||||
entry->forceRecompute)
|
||||
{
|
||||
/*
|
||||
* Do not free the stale value of entry->finalPath until we've
|
||||
* performed the comparison, in case it's aliased by prevPath (which
|
||||
* can only happen when recomputing due to an object_access_hook).
|
||||
*/
|
||||
List *finalPath;
|
||||
list_free(entry->finalPath);
|
||||
entry->finalPath = NIL;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
|
||||
finalPath = finalNamespacePath(entry->oidlist,
|
||||
&entry->firstNS);
|
||||
entry->finalPath = finalNamespacePath(entry->oidlist,
|
||||
&entry->firstNS);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
*same = equal(prevPath, finalPath);
|
||||
|
||||
list_free(entry->finalPath);
|
||||
entry->finalPath = finalPath;
|
||||
|
||||
/*
|
||||
* If an object_access_hook set when finalPath is calculated, the
|
||||
* If an object_access_hook is set when finalPath is calculated, the
|
||||
* result may be affected by the hook. Force recomputation of
|
||||
* finalPath the next time this cache entry is used, even if the
|
||||
* object_access_hook is not set at that time.
|
||||
*/
|
||||
entry->forceRecompute = object_access_hook ? true : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* use cached version of finalPath */
|
||||
*same = equal(prevPath, entry->finalPath);
|
||||
}
|
||||
|
||||
list_free(prevPathCopy);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
@ -4275,7 +4221,6 @@ static void
|
|||
recomputeNamespacePath(void)
|
||||
{
|
||||
Oid roleid = GetUserId();
|
||||
bool newPathEqual;
|
||||
bool pathChanged;
|
||||
const SearchPathCacheEntry *entry;
|
||||
|
||||
|
@ -4283,24 +4228,32 @@ recomputeNamespacePath(void)
|
|||
if (baseSearchPathValid && namespaceUser == roleid)
|
||||
return;
|
||||
|
||||
entry = cachedNamespacePath(namespace_search_path, roleid, baseSearchPath,
|
||||
&newPathEqual);
|
||||
entry = cachedNamespacePath(namespace_search_path, roleid);
|
||||
|
||||
if (baseCreationNamespace == entry->firstNS &&
|
||||
baseTempCreationPending == entry->temp_missing &&
|
||||
newPathEqual)
|
||||
equal(entry->finalPath, baseSearchPath))
|
||||
{
|
||||
pathChanged = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathChanged = true;
|
||||
}
|
||||
MemoryContext oldcxt;
|
||||
List *newpath;
|
||||
|
||||
/* Now safe to assign to state variables. */
|
||||
baseSearchPath = entry->finalPath;
|
||||
baseCreationNamespace = entry->firstNS;
|
||||
baseTempCreationPending = entry->temp_missing;
|
||||
pathChanged = true;
|
||||
|
||||
/* Must save OID list in permanent storage. */
|
||||
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
|
||||
newpath = list_copy(entry->finalPath);
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
/* Now safe to assign to state variables. */
|
||||
list_free(baseSearchPath);
|
||||
baseSearchPath = newpath;
|
||||
baseCreationNamespace = entry->firstNS;
|
||||
baseTempCreationPending = entry->temp_missing;
|
||||
}
|
||||
|
||||
/* Mark the path valid. */
|
||||
baseSearchPathValid = true;
|
||||
|
|
Loading…
Reference in New Issue