timescaledb/src/cache_invalidate.c
Erik Nordström c60b08e83a Fix DROP EXTENSION
DROP EXTENSION didn't properly reset caches and other saved state
causing various errors related to bad state when the extension was
dropped and/or recreated later.

This patch adds functionality to track the state of the extension and
also signals DROP EXTENSION to other backends that might be running,
allowing them to reset their internal extension state.
2017-03-22 19:43:40 +01:00

130 lines
3.8 KiB
C

/*
* Notes on the way caching works: Since out caches are stored in per-process
* (per-backend memory), we have to have a way to propagate invalidation
* messages to all backends, for that we use the Postgres relcache
* mechanism. Relcache is a cache that keeps internal info about
* relations(tables). Postgres has a mechanism for registering for relcache
* invalidation events that are propagated to all backends:
* CacheRegisterRelcacheCallback(). We register inval_cache_callback() with
* this mechanism and route all invalidation messages through it to the correct
* cache invalidation functions.
*
* The plan for our caches is to use (abuse) this mechanism to serve as a
* notify to invalidate our caches. Thus, we create proxy tables for each
* cache we use and attach the invalidate_relcache_trigger trigger to all
* tables whose changes should invalidate the cache. This trigger will
* invalidate the relcache for the proxy table specified as the first argument
* to the trigger (see cache.sql).
*/
#include <postgres.h>
#include <access/xact.h>
#include <utils/lsyscache.h>
#include <catalog/namespace.h>
#include <miscadmin.h>
#include <commands/trigger.h>
#include <commands/event_trigger.h>
#include <nodes/nodes.h>
#include <utils/inval.h>
#include <unistd.h>
#include "hypertable_cache.h"
#include "chunk_cache.h"
#include "catalog.h"
#include "extension.h"
void _cache_invalidate_init(void);
void _cache_invalidate_fini(void);
void _cache_invalidate_extload(void);
/*
* This function is called when any relcache is invalidated.
* Should route the invalidation to the correct cache.
*/
static void
inval_cache_callback(Datum arg, Oid relid)
{
Catalog *catalog = catalog_get();
if (!extension_is_loaded())
return;
if (extension_is_being_dropped(relid))
{
/* Extension was dropped. Reset state. */
hypertable_cache_invalidate_callback();
chunk_cache_invalidate_callback();
extension_reset();
return;
}
if (!OidIsValid(relid) || relid == catalog_get_cache_proxy_id(catalog, CACHE_TYPE_HYPERTABLE))
{
hypertable_cache_invalidate_callback();
}
if (!OidIsValid(relid) || relid == catalog_get_cache_proxy_id(catalog, CACHE_TYPE_CHUNK))
chunk_cache_invalidate_callback();
}
PG_FUNCTION_INFO_V1(invalidate_relcache_trigger);
/*
* This trigger causes the relcache for the cache_inval_proxy table (passed in
* as arg 0) to be invalidated. It should be called to invalidate the caches
* associated with a proxy table (usually each cache has it's own proxy table)
* This function is attached to the right tables in common/cache.sql
*
*/
Datum
invalidate_relcache_trigger(PG_FUNCTION_ARGS)
{
TriggerData *trigdata = (TriggerData *) fcinfo->context;
Oid proxy_oid;
Catalog *catalog = catalog_get();
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "not called by trigger manager");
/* arg 0 = relid of the proxy table */
proxy_oid = catalog_get_cache_proxy_id_by_name(catalog, trigdata->tg_trigger->tgargs[0]);
CacheInvalidateRelcacheByRelid(proxy_oid);
/* tuple to return to executor */
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
return PointerGetDatum(trigdata->tg_newtuple);
else
return PointerGetDatum(trigdata->tg_trigtuple);
}
PG_FUNCTION_INFO_V1(invalidate_relcache);
/*
* This is similar to invalidate_relcache_trigger but not a trigger.
* Not used regularly but useful for debugging.
*
*/
Datum
invalidate_relcache(PG_FUNCTION_ARGS)
{
Oid proxy_oid = PG_GETARG_OID(0);
/* arg 0 = relid of the cache_inval_proxy table */
CacheInvalidateRelcacheByRelid(proxy_oid);
/* tuple to return to executor */
return BoolGetDatum(true);
}
void
_cache_invalidate_init(void)
{
CacheRegisterRelcacheCallback(inval_cache_callback, PointerGetDatum(NULL));
}
void
_cache_invalidate_fini(void)
{
/* No way to unregister relcache callback */
}