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)