mirror of
https://github.com/postgres/postgres.git
synced 2025-06-02 00:01:40 -04:00
Be more paranoid about OOM in search_path cache.
Recent commit f26c2368dc 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 in f26c2368dc. 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;
|
static nsphash_hash * SearchPathCache = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create search path cache.
|
* Create or reset search_path cache as necessary.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
spcache_init(void)
|
spcache_init(void)
|
||||||
{
|
{
|
||||||
Assert(SearchPathCacheContext);
|
Assert(SearchPathCacheContext);
|
||||||
|
|
||||||
if (SearchPathCache)
|
if (SearchPathCache && searchPathCacheValid &&
|
||||||
|
SearchPathCache->members < SPCACHE_RESET_THRESHOLD)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
MemoryContextReset(SearchPathCacheContext);
|
||||||
/* arbitrary initial starting size of 16 elements */
|
/* arbitrary initial starting size of 16 elements */
|
||||||
SearchPathCache = nsphash_create(SearchPathCacheContext, 16, NULL);
|
SearchPathCache = nsphash_create(SearchPathCacheContext, 16, NULL);
|
||||||
searchPathCacheValid = true;
|
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.
|
* Look up or insert entry in search path cache.
|
||||||
*
|
*
|
||||||
@ -325,27 +306,25 @@ static SearchPathCacheEntry *
|
|||||||
spcache_insert(const char *searchPath, Oid roleid)
|
spcache_insert(const char *searchPath, Oid roleid)
|
||||||
{
|
{
|
||||||
SearchPathCacheEntry *entry;
|
SearchPathCacheEntry *entry;
|
||||||
bool found;
|
|
||||||
SearchPathCacheKey cachekey = {
|
SearchPathCacheKey cachekey = {
|
||||||
.searchPath = searchPath,
|
.searchPath = searchPath,
|
||||||
.roleid = roleid
|
.roleid = roleid
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a new entry is created, we must ensure that it's properly
|
* searchPath is not saved in SearchPathCacheContext. First perform a
|
||||||
* initialized. Set the cache invalid temporarily, so that if the
|
* lookup, and copy searchPath only if we need to create a new entry.
|
||||||
* MemoryContextStrdup() below raises an OOM, the cache will be reset on
|
|
||||||
* the next use, clearing the uninitialized entry.
|
|
||||||
*/
|
*/
|
||||||
searchPathCacheValid = false;
|
entry = nsphash_lookup(SearchPathCache, cachekey);
|
||||||
|
|
||||||
entry = nsphash_insert(SearchPathCache, cachekey, &found);
|
if (!entry)
|
||||||
|
|
||||||
/* ensure that key is initialized and the rest is zeroed */
|
|
||||||
if (!found)
|
|
||||||
{
|
{
|
||||||
entry->key.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
|
bool found;
|
||||||
entry->key.roleid = roleid;
|
|
||||||
|
cachekey.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
|
||||||
|
entry = nsphash_insert(SearchPathCache, cachekey, &found);
|
||||||
|
Assert(!found);
|
||||||
|
|
||||||
entry->oidlist = NIL;
|
entry->oidlist = NIL;
|
||||||
entry->finalPath = NIL;
|
entry->finalPath = NIL;
|
||||||
entry->firstNS = InvalidOid;
|
entry->firstNS = InvalidOid;
|
||||||
@ -354,7 +333,6 @@ spcache_insert(const char *searchPath, Oid roleid)
|
|||||||
/* do not touch entry->status, used by simplehash */
|
/* do not touch entry->status, used by simplehash */
|
||||||
}
|
}
|
||||||
|
|
||||||
searchPathCacheValid = true;
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4183,31 +4161,15 @@ finalNamespacePath(List *oidlist, Oid *firstNS)
|
|||||||
/*
|
/*
|
||||||
* Retrieve search path information from the cache; or if not there, fill
|
* 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.
|
* 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 *
|
static const SearchPathCacheEntry *
|
||||||
cachedNamespacePath(const char *searchPath, Oid roleid, List *prevPath,
|
cachedNamespacePath(const char *searchPath, Oid roleid)
|
||||||
bool *same)
|
|
||||||
{
|
{
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
SearchPathCacheEntry *entry;
|
SearchPathCacheEntry *entry;
|
||||||
List *prevPathCopy = NIL;
|
|
||||||
|
|
||||||
spcache_init();
|
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);
|
entry = spcache_insert(searchPath, roleid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4232,38 +4194,22 @@ cachedNamespacePath(const char *searchPath, Oid roleid, List *prevPath,
|
|||||||
if (entry->finalPath == NIL || object_access_hook ||
|
if (entry->finalPath == NIL || object_access_hook ||
|
||||||
entry->forceRecompute)
|
entry->forceRecompute)
|
||||||
{
|
{
|
||||||
/*
|
list_free(entry->finalPath);
|
||||||
* Do not free the stale value of entry->finalPath until we've
|
entry->finalPath = NIL;
|
||||||
* performed the comparison, in case it's aliased by prevPath (which
|
|
||||||
* can only happen when recomputing due to an object_access_hook).
|
|
||||||
*/
|
|
||||||
List *finalPath;
|
|
||||||
|
|
||||||
oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
|
oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
|
||||||
finalPath = finalNamespacePath(entry->oidlist,
|
entry->finalPath = finalNamespacePath(entry->oidlist,
|
||||||
&entry->firstNS);
|
&entry->firstNS);
|
||||||
MemoryContextSwitchTo(oldcxt);
|
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
|
* result may be affected by the hook. Force recomputation of
|
||||||
* finalPath the next time this cache entry is used, even if the
|
* finalPath the next time this cache entry is used, even if the
|
||||||
* object_access_hook is not set at that time.
|
* object_access_hook is not set at that time.
|
||||||
*/
|
*/
|
||||||
entry->forceRecompute = object_access_hook ? true : false;
|
entry->forceRecompute = object_access_hook ? true : false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
/* use cached version of finalPath */
|
|
||||||
*same = equal(prevPath, entry->finalPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_free(prevPathCopy);
|
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@ -4275,7 +4221,6 @@ static void
|
|||||||
recomputeNamespacePath(void)
|
recomputeNamespacePath(void)
|
||||||
{
|
{
|
||||||
Oid roleid = GetUserId();
|
Oid roleid = GetUserId();
|
||||||
bool newPathEqual;
|
|
||||||
bool pathChanged;
|
bool pathChanged;
|
||||||
const SearchPathCacheEntry *entry;
|
const SearchPathCacheEntry *entry;
|
||||||
|
|
||||||
@ -4283,24 +4228,32 @@ recomputeNamespacePath(void)
|
|||||||
if (baseSearchPathValid && namespaceUser == roleid)
|
if (baseSearchPathValid && namespaceUser == roleid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
entry = cachedNamespacePath(namespace_search_path, roleid, baseSearchPath,
|
entry = cachedNamespacePath(namespace_search_path, roleid);
|
||||||
&newPathEqual);
|
|
||||||
|
|
||||||
if (baseCreationNamespace == entry->firstNS &&
|
if (baseCreationNamespace == entry->firstNS &&
|
||||||
baseTempCreationPending == entry->temp_missing &&
|
baseTempCreationPending == entry->temp_missing &&
|
||||||
newPathEqual)
|
equal(entry->finalPath, baseSearchPath))
|
||||||
{
|
{
|
||||||
pathChanged = false;
|
pathChanged = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pathChanged = true;
|
MemoryContext oldcxt;
|
||||||
}
|
List *newpath;
|
||||||
|
|
||||||
/* Now safe to assign to state variables. */
|
pathChanged = true;
|
||||||
baseSearchPath = entry->finalPath;
|
|
||||||
baseCreationNamespace = entry->firstNS;
|
/* Must save OID list in permanent storage. */
|
||||||
baseTempCreationPending = entry->temp_missing;
|
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. */
|
/* Mark the path valid. */
|
||||||
baseSearchPathValid = true;
|
baseSearchPathValid = true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user