mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 10:11:29 +08:00
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:
parent
5b2d9d5a10
commit
e08e0a59db
51
src/chunk.c
51
src/chunk.c
@ -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);
|
||||
|
||||
|
32
src/utils.c
32
src/utils.c
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user