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.
This commit is contained in:
Erik Nordström 2021-07-06 09:26:53 +02:00 committed by Dmitry Simonenko
parent b4710501dd
commit bea2613455
4 changed files with 109 additions and 9 deletions

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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

View File

@ -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