From bea2613455bea01aea9530c74c605800ed99d624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Nordstr=C3=B6m?= <erik@timescale.com> Date: Tue, 6 Jul 2021 09:26:53 +0200 Subject: [PATCH] Add constraints when copying chunks across data nodes Chunk constraints are now added after a chunk has been copied from one data node to another. The constraints are added when the chunk is made visible on the destination node, i.e., after data has been copied and the chunk's metadata is created. As an alternative, the constraints could be added when the chunk table is first created, but before the metadata for the chunk is added. This would have the benefit of validating each copied (inserted) row against the constraints during the data copy phase. However, this would also necessitate decoupling the step of creating the constraint metadata from the creation of the actual constraints since the other chunk metadata that is referenced does not yet exist. Such decoupling would require validating that the metadata actually matches the constraints of the table when the metadata is later created. One downside of adding the constraints after data copying is that it necessitates validating all the chunk's rows against the constraints after insertion as opposed to during insertion. If this turns out to be a performance issue, validation could be initially deferred. This is left as a future optimization. --- src/chunk.c | 1 + tsl/src/chunk_api.c | 10 +++++ tsl/test/expected/chunk_api.out | 74 +++++++++++++++++++++++++++++---- tsl/test/sql/chunk_api.sql | 33 +++++++++++++++ 4 files changed, 109 insertions(+), 9 deletions(-) diff --git a/src/chunk.c b/src/chunk.c index 8a40c65f2..170f98a6a 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -1293,6 +1293,7 @@ chunk_create_from_hypercube_and_table_after_lock(const Hypertable *ht, Hypercube chunk_add_constraints(chunk); chunk_insert_into_metadata_after_lock(chunk); chunk_add_inheritance(chunk, ht); + chunk_create_table_constraints(chunk); return chunk; } diff --git a/tsl/src/chunk_api.c b/tsl/src/chunk_api.c index d39e8d49c..fe0976428 100644 --- a/tsl/src/chunk_api.c +++ b/tsl/src/chunk_api.c @@ -331,6 +331,16 @@ get_hypercube_from_slices(Jsonb *slices, const Hypertable *ht) return hc; } + +/* + * Create a chunk and its metadata. + * + * This function will create a chunk, either from an existing table or by + * creating a new table. If chunk_table_relid is InvalidOid, the chunk table + * will be created, otherwise the table referenced by the relid will be + * used. The chunk will be associated with the hypertable given by + * hypertable_relid. + */ Datum chunk_create(PG_FUNCTION_ARGS) { diff --git a/tsl/test/expected/chunk_api.out b/tsl/test/expected/chunk_api.out index 71c3fb8de..38937a4cb 100644 --- a/tsl/test/expected/chunk_api.out +++ b/tsl/test/expected/chunk_api.out @@ -796,6 +796,20 @@ ORDER BY chunk_name DESC LIMIT 1 \gset SELECT slices AS "SLICES" FROM _timescaledb_internal.show_chunk(:'CHUNK_SCHEMA'||'.'||:'CHUNK_NAME') \gset +-- Save the constraints info in a table for later comparison +CREATE TABLE original_chunk_constraints AS +SELECT "Constraint", "Type", "Columns", "Index"::text, "Expr", "Deferrable", "Deferred", "Validated" +FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); +-- Save contraints metadata +CREATE TABLE original_chunk_constraints_metadata AS +SELECT + chunk_id, + dimension_slice_id, + constraint_name, + hypertable_constraint_name +FROM _timescaledb_catalog.chunk_constraint con +INNER JOIN _timescaledb_catalog.chunk ch ON (con.chunk_id = ch.id) +WHERE ch.schema_name = :'CHUNK_SCHEMA' AND ch.table_name = :'CHUNK_NAME'; DROP TABLE :CHUNK_SCHEMA.:CHUNK_NAME; SELECT attach_tablespace('tablespace1', 'chunkapi'); attach_tablespace @@ -830,12 +844,51 @@ SELECT _timescaledb_internal.create_chunk('chunkapi', :'SLICES', :'CHUNK_SCHEMA' (11,10,_timescaledb_internal,_hyper_10_10_chunk,r,"{""time"": [1514419200000000, 1515024000000000]}",t) (1 row) -SELECT * FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); - Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ----------------------+------+---------+-------+--------------------------------+------------+----------+----------- - chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t -(1 row) +-- Compare original and new constraints +SELECT * FROM original_chunk_constraints; + Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated +---------------------------+------+----------+--------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- + 10_1_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t + 10_2_chunkapi_pkey | p | {time} | _timescaledb_internal."10_2_chunkapi_pkey" | | f | f | t + chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t + constraint_15 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t +(4 rows) +SELECT * FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); + Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated +---------------------------+------+----------+--------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- + 11_3_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t + 11_4_chunkapi_pkey | p | {time} | _timescaledb_internal."11_4_chunkapi_pkey" | | f | f | t + chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t + constraint_16 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t +(4 rows) + +-- Compare original and new chunk constraints metadata +SELECT * FROM original_chunk_constraints_metadata; + chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name +----------+--------------------+---------------------------+---------------------------- + 10 | 15 | constraint_15 | + 10 | | 10_1_chunkapi_device_fkey | chunkapi_device_fkey + 10 | | 10_2_chunkapi_pkey | chunkapi_pkey +(3 rows) + +SELECT + chunk_id, + dimension_slice_id, + constraint_name, + hypertable_constraint_name +FROM _timescaledb_catalog.chunk_constraint con +INNER JOIN _timescaledb_catalog.chunk ch ON (con.chunk_id = ch.id) +WHERE ch.schema_name = :'CHUNK_SCHEMA' AND ch.table_name = :'CHUNK_NAME'; + chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name +----------+--------------------+---------------------------+---------------------------- + 11 | 16 | constraint_16 | + 11 | | 11_3_chunkapi_device_fkey | chunkapi_device_fkey + 11 | | 11_4_chunkapi_pkey | chunkapi_pkey +(3 rows) + +DROP TABLE original_chunk_constraints; +DROP TABLE original_chunk_constraints_metadata; -- The chunk should inherit the hypertable SELECT relname FROM pg_catalog.pg_inherits, pg_class @@ -926,10 +979,13 @@ SELECT * FROM chunkapi ORDER BY 1,2,3; -- are specific to the chunk. Currently, foreign key, unique, and -- primary key constraints are not inherited or auto-created. SELECT * FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); - Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated ----------------------+------+---------+-------+--------------------------------+------------+----------+----------- - chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t -(1 row) + Constraint | Type | Columns | Index | Expr | Deferrable | Deferred | Validated +---------------------------+------+----------+--------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+----------- + 13_7_chunkapi_device_fkey | f | {device} | devices_pkey | | f | f | t + 13_8_chunkapi_pkey | p | {time} | _timescaledb_internal."13_8_chunkapi_pkey" | | f | f | t + chunkapi_temp_check | c | {temp} | - | (temp > (0)::double precision) | f | f | t + constraint_18 | c | {time} | - | (("time" >= 'Wed Dec 27 16:00:00 2017 PST'::timestamp with time zone) AND ("time" < 'Wed Jan 03 16:00:00 2018 PST'::timestamp with time zone)) | f | f | t +(4 rows) DROP TABLE chunkapi; \c :TEST_DBNAME :ROLE_SUPERUSER diff --git a/tsl/test/sql/chunk_api.sql b/tsl/test/sql/chunk_api.sql index a7f24eddf..51334420a 100644 --- a/tsl/test/sql/chunk_api.sql +++ b/tsl/test/sql/chunk_api.sql @@ -404,6 +404,23 @@ LIMIT 1 \gset SELECT slices AS "SLICES" FROM _timescaledb_internal.show_chunk(:'CHUNK_SCHEMA'||'.'||:'CHUNK_NAME') \gset +-- Save the constraints info in a table for later comparison +CREATE TABLE original_chunk_constraints AS +SELECT "Constraint", "Type", "Columns", "Index"::text, "Expr", "Deferrable", "Deferred", "Validated" +FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); + +-- Save contraints metadata +CREATE TABLE original_chunk_constraints_metadata AS +SELECT + chunk_id, + dimension_slice_id, + constraint_name, + hypertable_constraint_name +FROM _timescaledb_catalog.chunk_constraint con +INNER JOIN _timescaledb_catalog.chunk ch ON (con.chunk_id = ch.id) +WHERE ch.schema_name = :'CHUNK_SCHEMA' AND ch.table_name = :'CHUNK_NAME'; + + DROP TABLE :CHUNK_SCHEMA.:CHUNK_NAME; SELECT attach_tablespace('tablespace1', 'chunkapi'); @@ -418,8 +435,24 @@ SELECT tablespace FROM pg_tables WHERE tablename = :'CHUNK_NAME'; SELECT _timescaledb_internal.create_chunk('chunkapi', :'SLICES', :'CHUNK_SCHEMA', :'CHUNK_NAME', format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); +-- Compare original and new constraints +SELECT * FROM original_chunk_constraints; SELECT * FROM test.show_constraints(format('%I.%I', :'CHUNK_SCHEMA', :'CHUNK_NAME')::regclass); +-- Compare original and new chunk constraints metadata +SELECT * FROM original_chunk_constraints_metadata; +SELECT + chunk_id, + dimension_slice_id, + constraint_name, + hypertable_constraint_name +FROM _timescaledb_catalog.chunk_constraint con +INNER JOIN _timescaledb_catalog.chunk ch ON (con.chunk_id = ch.id) +WHERE ch.schema_name = :'CHUNK_SCHEMA' AND ch.table_name = :'CHUNK_NAME'; + +DROP TABLE original_chunk_constraints; +DROP TABLE original_chunk_constraints_metadata; + -- The chunk should inherit the hypertable SELECT relname FROM pg_catalog.pg_inherits, pg_class