Add hook for chunk creation

After data is tiered using OSM, we cannot insert data into the same
range. Need a callback that can be invoked by timescaledb to check
for range overlaps before creating a new chunk
This commit is contained in:
gayyappan 2022-10-25 10:43:33 -04:00 committed by gayyappan
parent 5b2d9d5a10
commit e08e0a59db
7 changed files with 163 additions and 1 deletions

View File

@ -1153,11 +1153,62 @@ ts_chunk_create_only_table(Hypertable *ht, Hypercube *cube, const char *schema_n
return chunk;
}
#if PG14_GE
#define OSM_CHUNK_INSERT_CHECK_HOOK "osm_chunk_insert_check_hook"
typedef int (*ts_osm_chunk_insert_hook_type)(Oid ht_oid, int64 range_start, int64 range_end);
static ts_osm_chunk_insert_hook_type
get_osm_chunk_insert_hook()
{
ts_osm_chunk_insert_hook_type *func_ptr =
(ts_osm_chunk_insert_hook_type *) find_rendezvous_variable(OSM_CHUNK_INSERT_CHECK_HOOK);
return *func_ptr;
}
#endif
static Chunk *
chunk_create_from_hypercube_after_lock(const Hypertable *ht, Hypercube *cube,
const char *schema_name, const char *table_name,
const char *prefix)
{
#if PG14_GE
ts_osm_chunk_insert_hook_type insert_func_ptr = get_osm_chunk_insert_hook();
if (insert_func_ptr)
{
/* OSM only uses first dimension . doesn't work with multinode tables yet*/
Dimension *dim = &ht->space->dimensions[0];
/* convert to PG timestamp from timescaledb internal format */
int64 range_start =
ts_internal_to_time_int64(cube->slices[0]->fd.range_start, dim->fd.column_type);
int64 range_end =
ts_internal_to_time_int64(cube->slices[0]->fd.range_end, dim->fd.column_type);
int chunk_exists = insert_func_ptr(ht->main_table_relid, range_start, range_end);
if (chunk_exists)
{
Oid outfuncid = InvalidOid;
bool isvarlena;
Datum start_ts =
ts_internal_to_time_value(cube->slices[0]->fd.range_start, dim->fd.column_type);
Datum end_ts =
ts_internal_to_time_value(cube->slices[0]->fd.range_end, dim->fd.column_type);
getTypeOutputInfo(dim->fd.column_type, &outfuncid, &isvarlena);
Assert(!isvarlena);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("distributed hypertable member cannot create chunk on its own"),
errmsg("Cannot insert into tiered chunk range of %s.%s - attempt to create "
"new chunk "
"with range [%s %s] failed",
NameStr(ht->fd.schema_name),
NameStr(ht->fd.table_name),
DatumGetCString(OidFunctionCall1(outfuncid, start_ts)),
DatumGetCString(OidFunctionCall1(outfuncid, end_ts))),
errhint(
"Hypertable has tiered data with time range that overlaps the insert")));
}
}
#endif
/* Insert any new dimension slices into metadata */
ts_dimension_slice_insert_multi(cube->slices, cube->num_slices);

View File

@ -347,6 +347,38 @@ ts_internal_to_time_value(int64 value, Oid type)
}
}
TSDLLEXPORT int64
ts_internal_to_time_int64(int64 value, Oid type)
{
if (TS_TIME_IS_NOBEGIN(value, type))
return ts_time_datum_get_nobegin(type);
if (TS_TIME_IS_NOEND(value, type))
return ts_time_datum_get_noend(type);
switch (type)
{
case INT2OID:
case INT4OID:
case INT8OID:
return value;
case TIMESTAMPOID:
case TIMESTAMPTZOID:
/* we continue ts_time_value_to_internal's incorrect handling of TIMESTAMPs for
* compatibility */
return DatumGetInt64(
DirectFunctionCall1(ts_pg_unix_microseconds_to_timestamp, Int64GetDatum(value)));
case DATEOID:
return DatumGetInt64(
DirectFunctionCall1(ts_pg_unix_microseconds_to_date, Int64GetDatum(value)));
default:
elog(ERROR,
"unknown time type \"%s\" in ts_internal_to_time_value",
format_type_be(type));
pg_unreachable();
}
}
TSDLLEXPORT char *
ts_internal_to_time_string(int64 value, Oid type)
{

View File

@ -67,6 +67,7 @@ extern TSDLLEXPORT int64 ts_interval_value_to_internal(Datum time_val, Oid type_
* Convert a column from the internal time representation into the specified type
*/
extern TSDLLEXPORT Datum ts_internal_to_time_value(int64 value, Oid type);
extern TSDLLEXPORT int64 ts_internal_to_time_int64(int64 value, Oid type);
extern TSDLLEXPORT Datum ts_internal_to_interval_value(int64 value, Oid type);
extern TSDLLEXPORT char *ts_internal_to_time_string(int64 value, Oid type);

View File

@ -27,6 +27,11 @@ GRANT SELECT on chunk_view TO PUBLIC;
CREATE SCHEMA test1;
GRANT CREATE ON SCHEMA test1 TO :ROLE_DEFAULT_PERM_USER;
GRANT USAGE ON SCHEMA test1 TO :ROLE_DEFAULT_PERM_USER;
--mock hooks for OSM intercation with timescaledb
CREATE OR REPLACE FUNCTION ts_setup_osm_hook( ) RETURNS VOID
AS :TSL_MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_undo_osm_hook( ) RETURNS VOID
AS :TSL_MODULE_PATHNAME LANGUAGE C VOLATILE;
SET ROLE :ROLE_DEFAULT_PERM_USER;
CREATE TABLE test1.hyper1 (time bigint, temp float);
SELECT create_hypertable('test1.hyper1', 'time', chunk_time_interval => 10);
@ -467,6 +472,25 @@ SELECT * from ht_try WHERE timec > '2020-01-01 01:00' ORDER BY 1;
Thu May 05 01:00:00 2022 PDT | 222 | 222
(1 row)
--TEST insert into a OSM chunk fails. actually any insert will fail. But we just need
-- to mock the hook and make sure the timescaledb code works correctly.
SELECT ts_setup_osm_hook();
ts_setup_osm_hook
-------------------
(1 row)
\set ON_ERROR_STOP 0
--the mock hook returns true always. so cannot create a new chunk on the hypertable
INSERT INTO ht_try VALUES ('2022-06-05 01:00', 222, 222);
ERROR: Cannot insert into tiered chunk range of public.ht_try - attempt to create new chunk with range [Sat Jun 04 17:00:00 2022 PDT Sun Jun 05 17:00:00 2022 PDT] failed
\set ON_ERROR_STOP 1
SELECT ts_undo_osm_hook();
ts_undo_osm_hook
------------------
(1 row)
-- TEST error have to be hypertable owner to attach a chunk to it
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
\set ON_ERROR_STOP 0

View File

@ -97,7 +97,10 @@ if(CMAKE_BUILD_TYPE MATCHES Debug)
endif(CMAKE_BUILD_TYPE MATCHES Debug)
if((${PG_VERSION_MAJOR} GREATER_EQUAL "14"))
list(APPEND TEST_FILES modify_exclusion.sql chunk_utils_internal.sql)
list(APPEND TEST_FILES modify_exclusion.sql)
if(CMAKE_BUILD_TYPE MATCHES Debug)
list(APPEND TEST_FILES chunk_utils_internal.sql)
endif()
endif()
set(SOLO_TESTS

View File

@ -31,6 +31,13 @@ CREATE SCHEMA test1;
GRANT CREATE ON SCHEMA test1 TO :ROLE_DEFAULT_PERM_USER;
GRANT USAGE ON SCHEMA test1 TO :ROLE_DEFAULT_PERM_USER;
--mock hooks for OSM intercation with timescaledb
CREATE OR REPLACE FUNCTION ts_setup_osm_hook( ) RETURNS VOID
AS :TSL_MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_undo_osm_hook( ) RETURNS VOID
AS :TSL_MODULE_PATHNAME LANGUAGE C VOLATILE;
SET ROLE :ROLE_DEFAULT_PERM_USER;
CREATE TABLE test1.hyper1 (time bigint, temp float);
@ -273,6 +280,17 @@ SELECT * from ht_try WHERE timec = '2020-01-01 01:00' ORDER BY 1;
SELECT * from ht_try WHERE timec > '2000-01-01 01:00' and timec < '2022-01-01 01:00' ORDER BY 1;
SELECT * from ht_try WHERE timec > '2020-01-01 01:00' ORDER BY 1;
--TEST insert into a OSM chunk fails. actually any insert will fail. But we just need
-- to mock the hook and make sure the timescaledb code works correctly.
SELECT ts_setup_osm_hook();
\set ON_ERROR_STOP 0
--the mock hook returns true always. so cannot create a new chunk on the hypertable
INSERT INTO ht_try VALUES ('2022-06-05 01:00', 222, 222);
\set ON_ERROR_STOP 1
SELECT ts_undo_osm_hook();
-- TEST error have to be hypertable owner to attach a chunk to it
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
\set ON_ERROR_STOP 0
@ -376,6 +394,7 @@ FROM chunk_view
WHERE hypertable_name = 'hyper_constr'
ORDER BY chunk_name;
-- clean up databases created
\c :TEST_DBNAME :ROLE_SUPERUSER
DROP DATABASE postgres_fdw_db;

View File

@ -34,3 +34,35 @@ ts_test_chunk_stats_insert(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
static int
osm_insert_hook_mock(Oid ht_oid, int64 range_start, int64 range_end)
{
/* always return true */
return 1;
}
/*
* Dummy function to mock OSM_INSERT hook called at chunk creation for tiered data
*/
TS_FUNCTION_INFO_V1(ts_setup_osm_hook);
Datum
ts_setup_osm_hook(PG_FUNCTION_ARGS)
{
typedef int (*MOCK_OSM_INSERT_HOOK)(Oid, int64, int64);
MOCK_OSM_INSERT_HOOK *var =
(MOCK_OSM_INSERT_HOOK *) find_rendezvous_variable("osm_chunk_insert_check_hook");
*var = osm_insert_hook_mock;
PG_RETURN_NULL();
}
TS_FUNCTION_INFO_V1(ts_undo_osm_hook);
Datum
ts_undo_osm_hook(PG_FUNCTION_ARGS)
{
typedef int (*MOCK_OSM_INSERT_HOOK)(Oid, int64, int64);
MOCK_OSM_INSERT_HOOK *var =
(MOCK_OSM_INSERT_HOOK *) find_rendezvous_variable("osm_chunk_insert_check_hook");
*var = NULL;
PG_RETURN_NULL();
}