timescaledb/src/cache_invalidate.c
Erik Nordström 882a247244 Support compiler-agnostic annotations
This change adds a compiler-agnostic annotation for fall-throughs in
switch statements. The definition is put in `annotations.h`, which can
be expanded to hold definitions for similar functionality in the
future.

The `clang` compiler (as of version 12) seems to have have dropped
support for the previous comment-based annotations to allow
fall-throughs in favor of native annotations or GCC-style attributes.
2020-09-29 06:54:24 +02:00

179 lines
5.0 KiB
C

/*
* This file and its contents are licensed under the Apache License 2.0.
* Please see the included NOTICE for copyright information and
* LICENSE-APACHE for a copy of the license.
*/
#include <postgres.h>
#include <access/xact.h>
#include <utils/lsyscache.h>
#include <utils/inval.h>
#include <catalog/namespace.h>
#include <nodes/nodes.h>
#include <miscadmin.h>
#include <utils/syscache.h>
#include "annotations.h"
#include "catalog.h"
#include "compat.h"
#include "extension.h"
#include "hypertable_cache.h"
#include "bgw/scheduler.h"
#include "cross_module_fn.h"
/*
* Notes on the way cache invalidation works.
*
* Since our caches are stored in per-process (per-backend memory), we need a
* way to signal all backends that they should invalidate their caches. For this
* we use the PostgreSQL relcache mechanism that propagates relation cache
* invalidation events to all backends. We register a callback with this
* mechanism to receive events on all backends whenever a relation cache entry
* is invalidated.
*
* To know which events should trigger invalidation of our caches, we use dummy
* (empty) tables. We can trigger relcache invalidation events for these tables
* to signal other backends. If the received table OID is a dummy table, we know
* that this is an event that we care about.
*
* Caches for catalog tables should be invalidated on:
*
* 1. INSERT/UPDATE/DELETE on a catalog table
* 2. Aborted transactions that taint the caches
*
* Generally, INSERTS do not warrant cache invalidation, unless it is an insert
* of a subobject that belongs to an object that might already be in the cache
* (e.g., a new dimension of a hypertable), or when replacing an existing entry
* (e.g., when replacing a negative hypertable entry with a positive one). Note,
* also, that INSERTS can taint the cache if the transaction that did the INSERT
* fails. This is why we also need to invalidate caches on transaction failure.
*/
void _cache_invalidate_init(void);
void _cache_invalidate_fini(void);
static inline void
cache_invalidate_relcache_all(void)
{
ts_hypertable_cache_invalidate_callback();
ts_bgw_job_cache_invalidate_callback();
}
/*
* This function is called when any relcache is invalidated.
* Should route the invalidation to the correct cache.
*/
static void
cache_invalidate_callback(Datum arg, Oid relid)
{
Catalog *catalog;
if (ts_extension_invalidate(relid))
{
cache_invalidate_relcache_all();
return;
}
if (!ts_extension_is_loaded())
return;
catalog = ts_catalog_get();
if (relid == ts_catalog_get_cache_proxy_id(catalog, CACHE_TYPE_HYPERTABLE))
ts_hypertable_cache_invalidate_callback();
if (relid == ts_catalog_get_cache_proxy_id(catalog, CACHE_TYPE_BGW_JOB))
ts_bgw_job_cache_invalidate_callback();
if (relid == InvalidOid)
cache_invalidate_relcache_all();
}
/* Registration for given cache ids happens at */
static void
cache_invalidate_syscache_callback(Datum arg, int cacheid, uint32 hashvalue)
{
Assert(cacheid == FOREIGNSERVEROID);
ts_cm_functions->cache_syscache_invalidate(arg, cacheid, hashvalue);
}
TS_FUNCTION_INFO_V1(ts_timescaledb_invalidate_cache);
/*
* Force a cache invalidation for a catalog table.
*
* This function is used for debugging purposes and triggers a cache
* invalidation.
*
* The first argument should be the catalog table that has changed, warranting a
* cache invalidation.
*/
Datum
ts_timescaledb_invalidate_cache(PG_FUNCTION_ARGS)
{
ts_catalog_invalidate_cache(PG_GETARG_OID(0), CMD_UPDATE);
PG_RETURN_VOID();
}
static void
cache_invalidate_xact_end(XactEvent event, void *arg)
{
switch (event)
{
case XACT_EVENT_ABORT:
case XACT_EVENT_PARALLEL_ABORT:
/*
* Invalidate caches on aborted transactions to purge entries that
* have been added during the transaction and are now no longer
* valid. Note that we need not signal other backends of this
* change since the transaction hasn't been committed and other
* backends cannot have the invalid state.
*/
cache_invalidate_relcache_all();
break;
default:
break;
}
}
static void
cache_invalidate_subxact_end(SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg)
{
switch (event)
{
case SUBXACT_EVENT_ABORT_SUB:
/*
* Invalidate caches on aborted sub transactions. See notes above
* in cache_invalidate_xact_end.
*/
cache_invalidate_relcache_all();
break;
default:
break;
}
}
void
_cache_invalidate_init(void)
{
RegisterXactCallback(cache_invalidate_xact_end, NULL);
RegisterSubXactCallback(cache_invalidate_subxact_end, NULL);
CacheRegisterRelcacheCallback(cache_invalidate_callback, PointerGetDatum(NULL));
/* Specific syscache callbacks */
CacheRegisterSyscacheCallback(FOREIGNSERVEROID,
cache_invalidate_syscache_callback,
PointerGetDatum(NULL));
}
void
_cache_invalidate_fini(void)
{
UnregisterXactCallback(cache_invalidate_xact_end, NULL);
UnregisterSubXactCallback(cache_invalidate_subxact_end, NULL);
/* No way to unregister relcache callback */
}