Skip to content

Commit 40e1d19

Browse files
committed
Add invalidate_using to continuous aggregates
Add new storage option `timescaledb.invalidate_using` to continuous aggregates for source of invalidations: either use `trigger` to read invalidations from hypertable using a trigger, or use `wal` to read invalidations for hypertables from WAL using logical decoding. It is not possible to attach continuous aggregates to hypertables using mixed collection method and an error will be thrown if you try to attach a continuous aggregate to a hypertable that is already using a different collection method. If no `invalidate_using` option is given to the continuous aggregate, it will use whatever collection method is already used for the hypertable.
1 parent 053a913 commit 40e1d19

22 files changed

Lines changed: 1107 additions & 29 deletions

.unreleased/pr_8306

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implements: #8306 Add option for invalidation collection using WAL for continuous aggregates

sql/pre_install/types.functions.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,8 @@ CREATE OR REPLACE FUNCTION _timescaledb_functions.dimension_info_out(_timescaled
5858
-- Type for bloom filters used by the sparse indexes on compressed hypertables.
5959
CREATE OR REPLACE FUNCTION _timescaledb_functions.bloom1in(cstring) RETURNS _timescaledb_internal.bloom1 AS 'byteain' LANGUAGE INTERNAL STRICT IMMUTABLE PARALLEL SAFE;
6060
CREATE OR REPLACE FUNCTION _timescaledb_functions.bloom1out(_timescaledb_internal.bloom1) RETURNS cstring AS 'byteaout' LANGUAGE INTERNAL STRICT IMMUTABLE PARALLEL SAFE;
61+
62+
CREATE OR REPLACE FUNCTION _timescaledb_functions.has_invalidation_trigger(regclass)
63+
RETURNS bool
64+
LANGUAGE C STRICT PARALLEL SAFE
65+
AS '@MODULE_PATHNAME@', 'ts_has_invalidation_trigger';

sql/updates/reverse-dev.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
ALTER EXTENSION timescaledb DROP VIEW timescaledb_information.continuous_aggregates;
2+
3+
DROP VIEW timescaledb_information.continuous_aggregates;
4+
15
DROP FUNCTION _timescaledb_functions.cagg_parse_invalidation_record(BYTEA);
6+
DROP FUNCTION _timescaledb_functions.has_invalidation_trigger(regclass);
27

38
CREATE FUNCTION ts_hypercore_handler(internal) RETURNS table_am_handler
49
AS '@MODULE_PATHNAME@', 'ts_hypercore_handler' LANGUAGE C;

sql/views.sql

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ SELECT ht.schema_name AS hypertable_schema,
122122
mat_ht.schema_name AS materialization_hypertable_schema,
123123
mat_ht.table_name AS materialization_hypertable_name,
124124
directview.viewdefinition AS view_definition,
125-
cagg.finalized
125+
cagg.finalized,
126+
CASE WHEN _timescaledb_functions.has_invalidation_trigger(format('%I.%I', ht.schema_name, ht.table_name)::regclass)
127+
THEN 'trigger'
128+
ELSE 'wal'
129+
END AS invalidate_using
126130
FROM _timescaledb_catalog.continuous_agg cagg,
127131
_timescaledb_catalog.hypertable ht,
128132
LATERAL (

src/hypertable.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,17 @@ ts_hypertable_create_trigger(const Hypertable *ht, CreateTrigStmt *stmt, const c
607607
return root_trigger_addr;
608608
}
609609

610+
TSDLLEXPORT void
611+
ts_hypertable_drop_invalidation_replication_slot(const char *slot_name)
612+
{
613+
CatalogSecurityContext sec_ctx;
614+
NameData slot;
615+
namestrcpy(&slot, slot_name);
616+
ts_catalog_database_info_become_owner(ts_catalog_database_info_get(), &sec_ctx);
617+
DirectFunctionCall1(pg_drop_replication_slot, NameGetDatum(&slot));
618+
ts_catalog_restore_user(&sec_ctx);
619+
}
620+
610621
/* based on RemoveObjects */
611622
TSDLLEXPORT void
612623
ts_hypertable_drop_trigger(Oid relid, const char *trigger_name)

src/hypertable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ extern int ts_hypertable_delete_by_id(int32 hypertable_id);
116116
extern TSDLLEXPORT ObjectAddress ts_hypertable_create_trigger(const Hypertable *ht,
117117
CreateTrigStmt *stmt,
118118
const char *query);
119+
extern TSDLLEXPORT void ts_hypertable_drop_invalidation_replication_slot(const char *slot_name);
119120
extern TSDLLEXPORT void ts_hypertable_drop_trigger(Oid relid, const char *trigger_name);
120121
extern TSDLLEXPORT void ts_hypertable_drop(Hypertable *hypertable, DropBehavior behavior);
121122

src/ts_catalog/catalog.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,6 @@ typedef enum Anum_continuous_aggs_bucket_function_pkey
10121012
*/
10131013
#define CONTINUOUS_AGGS_HYPERTABLE_INVALIDATION_LOG_TABLE_NAME \
10141014
"continuous_aggs_hypertable_invalidation_log"
1015-
#define CONTINUOUS_AGGS_HYPERTABLE_INVALIDATION_SLOT_NAME "continuous_aggs_hypertable_invalidations"
10161015

10171016
typedef enum Anum_continuous_aggs_hypertable_invalidation_log
10181017
{

src/ts_catalog/continuous_agg.c

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@
1010
*/
1111

1212
#include <postgres.h>
13+
1314
#include <access/htup_details.h>
1415
#include <catalog/dependency.h>
1516
#include <catalog/namespace.h>
1617
#include <catalog/pg_trigger.h>
1718
#include <commands/trigger.h>
19+
#include <executor/spi.h>
1820
#include <fmgr.h>
21+
#include <lib/stringinfo.h>
1922
#include <nodes/makefuncs.h>
23+
#include <replication/slot.h>
2024
#include <storage/lmgr.h>
2125
#include <utils/acl.h>
2226
#include <utils/builtins.h>
@@ -46,6 +50,14 @@
4650
#define BUCKET_FUNCTION_SERIALIZE_VERSION 1
4751
#define CHECK_NAME_MATCH(name1, name2) (namestrcmp(name1, name2) == 0)
4852

53+
TS_FUNCTION_INFO_V1(ts_has_invalidation_trigger);
54+
55+
Datum
56+
ts_has_invalidation_trigger(PG_FUNCTION_ARGS)
57+
{
58+
PG_RETURN_BOOL(has_invalidation_trigger(PG_GETARG_OID(0)));
59+
}
60+
4961
static void
5062
init_scan_by_mat_hypertable_id(ScanIterator *iterator, const int32 mat_hypertable_id)
5163
{
@@ -193,6 +205,12 @@ hypertable_invalidation_log_delete(int32 raw_hypertable_id)
193205
}
194206
}
195207

208+
void
209+
ts_get_invalidation_replication_slot_name(char *slotname, Size szslot)
210+
{
211+
snprintf(slotname, szslot, "ts_%u_cagg", MyDatabaseId);
212+
}
213+
196214
void
197215
ts_materialization_invalidation_log_delete_inner(int32 mat_hypertable_id)
198216
{
@@ -502,6 +520,37 @@ ts_continuous_agg_get_all_caggs_info(int32 raw_hypertable_id)
502520
return all_caggs_info;
503521
}
504522

523+
/*
524+
* Return true if there is any continuous aggregate that is using WAL-based
525+
* invalidation collection.
526+
*
527+
* A hypertable is using the WAL-based invalidation collection if it has a
528+
* attached continuous aggregate but does not have an invalidation trigger.
529+
*/
530+
static bool
531+
hypertable_invalidation_slot_used(void)
532+
{
533+
ScanIterator iterator =
534+
ts_scan_iterator_create(CONTINUOUS_AGG, AccessShareLock, CurrentMemoryContext);
535+
ts_scanner_foreach(&iterator)
536+
{
537+
bool isnull;
538+
Datum datum = slot_getattr(ts_scan_iterator_slot(&iterator),
539+
Anum_continuous_agg_raw_hypertable_id,
540+
&isnull);
541+
542+
Assert(!isnull);
543+
Oid relid = ts_hypertable_id_to_relid(DatumGetInt32(datum), true);
544+
if (!has_invalidation_trigger(relid))
545+
{
546+
ts_scan_iterator_close(&iterator);
547+
return true;
548+
}
549+
}
550+
ts_scan_iterator_close(&iterator);
551+
return false;
552+
}
553+
505554
TSDLLEXPORT ContinuousAggHypertableStatus
506555
ts_continuous_agg_hypertable_status(int32 hypertable_id)
507556
{
@@ -829,6 +878,10 @@ drop_continuous_agg(FormData_continuous_agg *cadata, bool drop_user_view)
829878
*
830879
* AccessExclusiveLock is needed to drop triggers and also prevent
831880
* concurrent DML commands.
881+
*
882+
* It is needed also in the case that we are using WAL-based invalidation
883+
* collection since we want to serialize create and drop of continuous
884+
* aggregates.
832885
*/
833886
if (drop_user_view)
834887
user_view = get_and_lock_rel_by_name(&cadata->user_view_schema,
@@ -856,17 +909,13 @@ drop_continuous_agg(FormData_continuous_agg *cadata, bool drop_user_view)
856909
LockRelationOid(catalog_get_table_id(catalog, CONTINUOUS_AGGS_INVALIDATION_THRESHOLD),
857910
RowExclusiveLock);
858911

859-
/* The trigger will be dropped if the hypertable still exists and no other
912+
/* The trigger will be dropped if it exists (it does not for WAL-based
913+
* invalidation collection), the hypertable still exists and no other
860914
* caggs attached. */
861-
if (OidIsValid(raw_hypertable.objectId))
915+
Oid tgoid = get_trigger_oid(raw_hypertable.objectId, CAGGINVAL_TRIGGER_NAME, true);
916+
if (OidIsValid(raw_hypertable.objectId) && OidIsValid(tgoid))
862917
{
863-
ObjectAddressSet(raw_hypertable_trig,
864-
TriggerRelationId,
865-
get_trigger_oid(raw_hypertable.objectId,
866-
CAGGINVAL_TRIGGER_NAME,
867-
false));
868-
869-
/* Raw hypertable is locked above */
918+
ObjectAddressSet(raw_hypertable_trig, TriggerRelationId, tgoid);
870919
LockRelationOid(raw_hypertable_trig.objectId, AccessExclusiveLock);
871920
}
872921
}
@@ -924,6 +973,18 @@ drop_continuous_agg(FormData_continuous_agg *cadata, bool drop_user_view)
924973
ts_hypertable_drop_trigger(raw_hypertable.objectId, CAGGINVAL_TRIGGER_NAME);
925974
}
926975

976+
/*
977+
* Drop invalidation slot if there are no hypertables using WAL-based
978+
* invalidation collection.
979+
*
980+
* This is important since there is no actor that reads the slot, which
981+
* means that the WAL cannot be pruned.
982+
*/
983+
char slot_name[TS_INVALIDATION_SLOT_NAME_MAX];
984+
ts_get_invalidation_replication_slot_name(slot_name, sizeof(slot_name));
985+
if (!hypertable_invalidation_slot_used() && SearchNamedReplicationSlot(slot_name, true) != NULL)
986+
ts_hypertable_drop_invalidation_replication_slot(slot_name);
987+
927988
if (OidIsValid(mat_hypertable.objectId))
928989
{
929990
performDeletion(&mat_hypertable, DROP_CASCADE, 0);

src/ts_catalog/continuous_agg.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "with_clause/with_clause_parser.h"
1717

1818
#define CAGGINVAL_TRIGGER_NAME "ts_cagg_invalidation_trigger"
19+
#define TS_INVALIDATION_SLOT_NAME_MAX (32)
1920

2021
/*switch to ts user for _timescaledb_internal access */
2122
#define SWITCH_TO_TS_USER(schemaname, newuid, saved_uid, saved_secctx) \
@@ -54,6 +55,13 @@ typedef enum ContinuousAggViewType
5455
ContinuousAggAnyView
5556
} ContinuousAggViewType;
5657

58+
typedef enum ContinuousAggInvalidateUsing
59+
{
60+
ContinuousAggInvalidateUsingDefault = 0,
61+
ContinuousAggInvalidateUsingTrigger,
62+
ContinuousAggInvalidateUsingWal,
63+
} ContinuousAggInvalidateUsing;
64+
5765
/*
5866
* Information about the bucketing function.
5967
*/
@@ -146,6 +154,12 @@ typedef struct CaggPolicyOffset
146154
const char *name;
147155
} CaggPolicyOffset;
148156

157+
static inline bool
158+
has_invalidation_trigger(Oid relid)
159+
{
160+
return OidIsValid(get_trigger_oid(relid, CAGGINVAL_TRIGGER_NAME, true));
161+
}
162+
149163
extern TSDLLEXPORT Oid ts_cagg_permissions_check(Oid cagg_oid, Oid userid);
150164

151165
extern TSDLLEXPORT CaggsInfo ts_continuous_agg_get_all_caggs_info(int32 raw_hypertable_id);
@@ -199,3 +213,4 @@ extern TSDLLEXPORT int64
199213
ts_continuous_agg_fixed_bucket_width(const ContinuousAggsBucketFunction *bucket_function);
200214
extern TSDLLEXPORT int64
201215
ts_continuous_agg_bucket_width(const ContinuousAggsBucketFunction *bucket_function);
216+
extern TSDLLEXPORT void ts_get_invalidation_replication_slot_name(char *slotname, Size szslot);

src/with_clause/create_materialized_view_with_clause.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ static const WithClauseDefinition continuous_aggregate_with_clause_def[] = {
5959
.arg_names = {"compress_chunk_interval", "compress_chunk_time_interval", NULL},
6060
.type_id = INTERVALOID,
6161
},
62+
[CreateMaterializedViewFlagInvalidateUsing] = {
63+
.arg_names = {"invalidate_using", NULL},
64+
.type_id = TEXTOID,
65+
.default_val = (Datum) 0,
66+
},
6267
};
6368

6469
WithClauseResult *

0 commit comments

Comments
 (0)