Skip to content

Commit 8efa301

Browse files
committed
Be more paranoid about OOM in search_path cache.
Recent commit f26c236 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 f26c236. 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
1 parent 3650e7a commit 8efa301

File tree

1 file changed

+36
-83
lines changed

1 file changed

+36
-83
lines changed

src/backend/catalog/namespace.c

Lines changed: 36 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -279,42 +279,23 @@ spcachekey_equal(SearchPathCacheKey a, SearchPathCacheKey b)
279279
static nsphash_hash * SearchPathCache = NULL;
280280

281281
/*
282-
* Create search path cache.
282+
* Create or reset search_path cache as necessary.
283283
*/
284284
static void
285285
spcache_init(void)
286286
{
287287
Assert(SearchPathCacheContext);
288288

289-
if (SearchPathCache)
289+
if (SearchPathCache && searchPathCacheValid &&
290+
SearchPathCache->members < SPCACHE_RESET_THRESHOLD)
290291
return;
291292

293+
MemoryContextReset(SearchPathCacheContext);
292294
/* arbitrary initial starting size of 16 elements */
293295
SearchPathCache = nsphash_create(SearchPathCacheContext, 16, NULL);
294296
searchPathCacheValid = true;
295297
}
296298

297-
/*
298-
* Reset and reinitialize search path cache.
299-
*/
300-
static void
301-
spcache_reset(void)
302-
{
303-
Assert(SearchPathCacheContext);
304-
Assert(SearchPathCache);
305-
306-
MemoryContextReset(SearchPathCacheContext);
307-
SearchPathCache = NULL;
308-
309-
spcache_init();
310-
}
311-
312-
static uint32
313-
spcache_members(void)
314-
{
315-
return SearchPathCache->members;
316-
}
317-
318299
/*
319300
* Look up or insert entry in search path cache.
320301
*
@@ -325,27 +306,25 @@ static SearchPathCacheEntry *
325306
spcache_insert(const char *searchPath, Oid roleid)
326307
{
327308
SearchPathCacheEntry *entry;
328-
bool found;
329309
SearchPathCacheKey cachekey = {
330310
.searchPath = searchPath,
331311
.roleid = roleid
332312
};
333313

334314
/*
335-
* If a new entry is created, we must ensure that it's properly
336-
* initialized. Set the cache invalid temporarily, so that if the
337-
* MemoryContextStrdup() below raises an OOM, the cache will be reset on
338-
* the next use, clearing the uninitialized entry.
315+
* searchPath is not saved in SearchPathCacheContext. First perform a
316+
* lookup, and copy searchPath only if we need to create a new entry.
339317
*/
340-
searchPathCacheValid = false;
341-
342-
entry = nsphash_insert(SearchPathCache, cachekey, &found);
318+
entry = nsphash_lookup(SearchPathCache, cachekey);
343319

344-
/* ensure that key is initialized and the rest is zeroed */
345-
if (!found)
320+
if (!entry)
346321
{
347-
entry->key.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
348-
entry->key.roleid = roleid;
322+
bool found;
323+
324+
cachekey.searchPath = MemoryContextStrdup(SearchPathCacheContext, searchPath);
325+
entry = nsphash_insert(SearchPathCache, cachekey, &found);
326+
Assert(!found);
327+
349328
entry->oidlist = NIL;
350329
entry->finalPath = NIL;
351330
entry->firstNS = InvalidOid;
@@ -354,7 +333,6 @@ spcache_insert(const char *searchPath, Oid roleid)
354333
/* do not touch entry->status, used by simplehash */
355334
}
356335

357-
searchPathCacheValid = true;
358336
return entry;
359337
}
360338

@@ -4183,31 +4161,15 @@ finalNamespacePath(List *oidlist, Oid *firstNS)
41834161
/*
41844162
* Retrieve search path information from the cache; or if not there, fill
41854163
* it. The returned entry is valid only until the next call to this function.
4186-
*
4187-
* We also determine if the newly-computed finalPath is the same as the
4188-
* prevPath passed by the caller (i.e. a no-op or a real change?). It's more
4189-
* efficient to check for a change in this function than the caller, because
4190-
* we can avoid unnecessary temporary copies of the previous path.
41914164
*/
41924165
static const SearchPathCacheEntry *
4193-
cachedNamespacePath(const char *searchPath, Oid roleid, List *prevPath,
4194-
bool *same)
4166+
cachedNamespacePath(const char *searchPath, Oid roleid)
41954167
{
41964168
MemoryContext oldcxt;
41974169
SearchPathCacheEntry *entry;
4198-
List *prevPathCopy = NIL;
41994170

42004171
spcache_init();
42014172

4202-
/* invalidate cache if necessary */
4203-
if (!searchPathCacheValid || spcache_members() >= SPCACHE_RESET_THRESHOLD)
4204-
{
4205-
/* prevPath will be destroyed; make temp copy for later comparison */
4206-
prevPathCopy = list_copy(prevPath);
4207-
prevPath = prevPathCopy;
4208-
spcache_reset();
4209-
}
4210-
42114173
entry = spcache_insert(searchPath, roleid);
42124174

42134175
/*
@@ -4232,38 +4194,22 @@ cachedNamespacePath(const char *searchPath, Oid roleid, List *prevPath,
42324194
if (entry->finalPath == NIL || object_access_hook ||
42334195
entry->forceRecompute)
42344196
{
4235-
/*
4236-
* Do not free the stale value of entry->finalPath until we've
4237-
* performed the comparison, in case it's aliased by prevPath (which
4238-
* can only happen when recomputing due to an object_access_hook).
4239-
*/
4240-
List *finalPath;
4197+
list_free(entry->finalPath);
4198+
entry->finalPath = NIL;
42414199

42424200
oldcxt = MemoryContextSwitchTo(SearchPathCacheContext);
4243-
finalPath = finalNamespacePath(entry->oidlist,
4244-
&entry->firstNS);
4201+
entry->finalPath = finalNamespacePath(entry->oidlist,
4202+
&entry->firstNS);
42454203
MemoryContextSwitchTo(oldcxt);
42464204

4247-
*same = equal(prevPath, finalPath);
4248-
4249-
list_free(entry->finalPath);
4250-
entry->finalPath = finalPath;
4251-
42524205
/*
4253-
* If an object_access_hook set when finalPath is calculated, the
4206+
* If an object_access_hook is set when finalPath is calculated, the
42544207
* result may be affected by the hook. Force recomputation of
42554208
* finalPath the next time this cache entry is used, even if the
42564209
* object_access_hook is not set at that time.
42574210
*/
42584211
entry->forceRecompute = object_access_hook ? true : false;
42594212
}
4260-
else
4261-
{
4262-
/* use cached version of finalPath */
4263-
*same = equal(prevPath, entry->finalPath);
4264-
}
4265-
4266-
list_free(prevPathCopy);
42674213

42684214
return entry;
42694215
}
@@ -4275,32 +4221,39 @@ static void
42754221
recomputeNamespacePath(void)
42764222
{
42774223
Oid roleid = GetUserId();
4278-
bool newPathEqual;
42794224
bool pathChanged;
42804225
const SearchPathCacheEntry *entry;
42814226

42824227
/* Do nothing if path is already valid. */
42834228
if (baseSearchPathValid && namespaceUser == roleid)
42844229
return;
42854230

4286-
entry = cachedNamespacePath(namespace_search_path, roleid, baseSearchPath,
4287-
&newPathEqual);
4231+
entry = cachedNamespacePath(namespace_search_path, roleid);
42884232

42894233
if (baseCreationNamespace == entry->firstNS &&
42904234
baseTempCreationPending == entry->temp_missing &&
4291-
newPathEqual)
4235+
equal(entry->finalPath, baseSearchPath))
42924236
{
42934237
pathChanged = false;
42944238
}
42954239
else
42964240
{
4241+
MemoryContext oldcxt;
4242+
List *newpath;
4243+
42974244
pathChanged = true;
4298-
}
42994245

4300-
/* Now safe to assign to state variables. */
4301-
baseSearchPath = entry->finalPath;
4302-
baseCreationNamespace = entry->firstNS;
4303-
baseTempCreationPending = entry->temp_missing;
4246+
/* Must save OID list in permanent storage. */
4247+
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
4248+
newpath = list_copy(entry->finalPath);
4249+
MemoryContextSwitchTo(oldcxt);
4250+
4251+
/* Now safe to assign to state variables. */
4252+
list_free(baseSearchPath);
4253+
baseSearchPath = newpath;
4254+
baseCreationNamespace = entry->firstNS;
4255+
baseTempCreationPending = entry->temp_missing;
4256+
}
43044257

43054258
/* Mark the path valid. */
43064259
baseSearchPathValid = true;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy