2929#include "utils/snapmgr.h"
3030#include "utils/syscache.h"
3131
32+ typedef enum
33+ {
34+ ETCS_NEEDS_REBUILD ,
35+ ETCS_REBUILD_STARTED ,
36+ ETCS_VALID
37+ } EventTriggerCacheStateType ;
38+
3239typedef struct
3340{
3441 EventTriggerEvent event ;
@@ -37,6 +44,7 @@ typedef struct
3744
3845static HTAB * EventTriggerCache ;
3946static MemoryContext EventTriggerCacheContext ;
47+ static EventTriggerCacheStateType EventTriggerCacheState = ETCS_NEEDS_REBUILD ;
4048
4149static void BuildEventTriggerCache (void );
4250static void InvalidateEventCacheCallback (Datum arg ,
@@ -55,7 +63,7 @@ EventCacheLookup(EventTriggerEvent event)
5563{
5664 EventTriggerCacheEntry * entry ;
5765
58- if (EventTriggerCache == NULL )
66+ if (EventTriggerCacheState != ETCS_VALID )
5967 BuildEventTriggerCache ();
6068 entry = hash_search (EventTriggerCache , & event , HASH_FIND , NULL );
6169 return entry != NULL ? entry -> triggerlist : NULL ;
@@ -77,12 +85,9 @@ BuildEventTriggerCache(void)
7785 if (EventTriggerCacheContext != NULL )
7886 {
7987 /*
80- * The cache has been previously built, and subsequently invalidated,
81- * and now we're trying to rebuild it. Normally, there won't be
82- * anything in the context at this point, because the invalidation
83- * will have already reset it. But if the previous attempt to rebuild
84- * the cache failed, then there might be some junk lying around
85- * that we want to reclaim.
88+ * Free up any memory already allocated in EventTriggerCacheContext.
89+ * This can happen either because a previous rebuild failed, or
90+ * because an invalidation happened before the rebuild was complete.
8691 */
8792 MemoryContextResetAndDeleteChildren (EventTriggerCacheContext );
8893 }
@@ -109,12 +114,10 @@ BuildEventTriggerCache(void)
109114 /* Switch to correct memory context. */
110115 oldcontext = MemoryContextSwitchTo (EventTriggerCacheContext );
111116
112- /*
113- * Create a new hash table, but don't assign it to the global variable
114- * until it accurately represents the state of the catalogs, so that
115- * if we fail midway through this we won't end up with incorrect cache
116- * contents.
117- */
117+ /* Prevent the memory context from being nuked while we're rebuilding. */
118+ EventTriggerCacheState = ETCS_REBUILD_STARTED ;
119+
120+ /* Create new hash table. */
118121 MemSet (& ctl , 0 , sizeof (ctl ));
119122 ctl .keysize = sizeof (EventTriggerEvent );
120123 ctl .entrysize = sizeof (EventTriggerCacheEntry );
@@ -195,8 +198,17 @@ BuildEventTriggerCache(void)
195198 /* Restore previous memory context. */
196199 MemoryContextSwitchTo (oldcontext );
197200
198- /* Cache is now valid . */
201+ /* Install new cache . */
199202 EventTriggerCache = cache ;
203+
204+ /*
205+ * If the cache has been invalidated since we entered this routine, we
206+ * still use and return the cache we just finished constructing, to avoid
207+ * infinite loops, but we leave the cache marked stale so that we'll
208+ * rebuild it again on next access. Otherwise, we mark the cache valid.
209+ */
210+ if (EventTriggerCacheState == ETCS_REBUILD_STARTED )
211+ EventTriggerCacheState = ETCS_VALID ;
200212}
201213
202214/*
@@ -238,6 +250,17 @@ DecodeTextArrayToCString(Datum array, char ***cstringp)
238250static void
239251InvalidateEventCacheCallback (Datum arg , int cacheid , uint32 hashvalue )
240252{
241- MemoryContextResetAndDeleteChildren (EventTriggerCacheContext );
242- EventTriggerCache = NULL ;
253+ /*
254+ * If the cache isn't valid, then there might be a rebuild in progress,
255+ * so we can't immediately blow it away. But it's advantageous to do
256+ * this when possible, so as to immediately free memory.
257+ */
258+ if (EventTriggerCacheState == ETCS_VALID )
259+ {
260+ MemoryContextResetAndDeleteChildren (EventTriggerCacheContext );
261+ EventTriggerCache = NULL ;
262+ }
263+
264+ /* Mark cache for rebuild. */
265+ EventTriggerCacheState = ETCS_NEEDS_REBUILD ;
243266}
0 commit comments