timescaledb/test/expected/broken_tables.out
Mats Kindahl 769bc31dc2 Lock dimension slice tuple when scanning
In the function `ts_hypercube_from_constraints` a hypercube is build
from constraints which reference dimension slices in `dimension_slice`.
As part of a run of `drop_chunks` or when a chunk is explicitly dropped
as part of other operations, dimension slices can be removed from this
table causing the dimension slices to be removed, which makes the
hypercube reference non-existent dimension slices which subsequently
causes a crash.

This commit fixes this by adding a tuple lock on the dimension slices
that are used to build the hypercube.

If two `drop_chunks` are running concurrently, there can be a race if
dimension slices are removed as a result removing a chunk. We treat
this case in the same way as if the dimension slice was updated: report
an error that another session locked the tuple.

Fixes #1986
2020-08-26 09:44:20 +02:00

129 lines
5.9 KiB
Plaintext

-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
-- LICENSE-APACHE for a copy of the license.
-- Hypertables can break as a result of race conditions, but we should
-- still not crash when trying to truncate or delete the broken table.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE VIEW missing_slices AS
SELECT DISTINCT
dimension_slice_id,
constraint_name,
attname AS column_name,
pg_get_expr(conbin, conrelid) AS constraint_expr
FROM
_timescaledb_catalog.chunk_constraint cc
JOIN _timescaledb_catalog.chunk ch ON cc.chunk_id = ch.id
JOIN pg_constraint ON conname = constraint_name
JOIN pg_namespace ns ON connamespace = ns.oid
AND ns.nspname = ch.schema_name
JOIN pg_attribute ON attnum = conkey[1]
AND attrelid = conrelid
WHERE
dimension_slice_id NOT IN (SELECT id FROM _timescaledb_catalog.dimension_slice);
-- To drop rows from dimension_slice table, we need to remove some
-- constraints.
ALTER TABLE _timescaledb_catalog.chunk_constraint
DROP CONSTRAINT chunk_constraint_dimension_slice_id_fkey;
CREATE TABLE chunk_test_int(time integer, temp float8, tag integer, color integer);
SELECT create_hypertable('chunk_test_int', 'time', 'tag', 2, chunk_time_interval => 3);
NOTICE: adding not-null constraint to column "time"
create_hypertable
-----------------------------
(1,public,chunk_test_int,t)
(1 row)
INSERT INTO chunk_test_int VALUES
(4, 24.3, 1, 1),
(4, 24.3, 2, 1),
(10, 24.3, 2, 1);
SELECT * FROM _timescaledb_catalog.dimension_slice ORDER BY id;
id | dimension_id | range_start | range_end
----+--------------+----------------------+---------------------
1 | 1 | 3 | 6
2 | 2 | -9223372036854775808 | 1073741823
3 | 2 | 1073741823 | 9223372036854775807
4 | 1 | 9 | 12
(4 rows)
SELECT DISTINCT
chunk_id,
dimension_slice_id,
constraint_name,
pg_get_expr(conbin, conrelid) AS constraint_expr
FROM _timescaledb_catalog.chunk_constraint,
LATERAL (
SELECT *
FROM pg_constraint JOIN pg_namespace ns ON connamespace = ns.oid
WHERE conname = constraint_name
) AS con
ORDER BY chunk_id, dimension_slice_id;
chunk_id | dimension_slice_id | constraint_name | constraint_expr
----------+--------------------+-----------------+---------------------------------------------------------------
1 | 1 | constraint_1 | (("time" >= 3) AND ("time" < 6))
1 | 2 | constraint_2 | (_timescaledb_internal.get_partition_hash(tag) < 1073741823)
2 | 1 | constraint_1 | (("time" >= 3) AND ("time" < 6))
2 | 3 | constraint_3 | (_timescaledb_internal.get_partition_hash(tag) >= 1073741823)
3 | 3 | constraint_3 | (_timescaledb_internal.get_partition_hash(tag) >= 1073741823)
3 | 4 | constraint_4 | (("time" >= 9) AND ("time" < 12))
(6 rows)
DELETE FROM _timescaledb_catalog.dimension_slice WHERE id = 1;
SELECT * FROM missing_slices;
dimension_slice_id | constraint_name | column_name | constraint_expr
--------------------+-----------------+-------------+----------------------------------
1 | constraint_1 | time | (("time" >= 3) AND ("time" < 6))
(1 row)
-- Setting level to ERROR since warnings are printed in different
-- order on PG11 and PG12.
SET client_min_messages TO error;
TRUNCATE TABLE chunk_test_int;
DROP TABLE chunk_test_int;
RESET client_min_messages;
CREATE TABLE chunk_test_int(time integer, temp float8, tag integer, color integer);
SELECT create_hypertable('chunk_test_int', 'time', 'tag', 2, chunk_time_interval => 3);
NOTICE: adding not-null constraint to column "time"
create_hypertable
-----------------------------
(2,public,chunk_test_int,t)
(1 row)
INSERT INTO chunk_test_int VALUES
(4, 24.3, 1, 1),
(4, 24.3, 2, 1),
(10, 24.3, 2, 1);
SELECT DISTINCT
chunk_id,
dimension_slice_id,
constraint_name,
pg_get_expr(conbin, conrelid) AS constraint_expr
FROM _timescaledb_catalog.chunk_constraint,
LATERAL (
SELECT *
FROM pg_constraint JOIN pg_namespace ns ON connamespace = ns.oid
WHERE conname = constraint_name
) AS con
ORDER BY chunk_id, dimension_slice_id;
chunk_id | dimension_slice_id | constraint_name | constraint_expr
----------+--------------------+-----------------+---------------------------------------------------------------
4 | 5 | constraint_5 | (("time" >= 3) AND ("time" < 6))
4 | 6 | constraint_6 | (_timescaledb_internal.get_partition_hash(tag) < 1073741823)
5 | 5 | constraint_5 | (("time" >= 3) AND ("time" < 6))
5 | 7 | constraint_7 | (_timescaledb_internal.get_partition_hash(tag) >= 1073741823)
6 | 7 | constraint_7 | (_timescaledb_internal.get_partition_hash(tag) >= 1073741823)
6 | 8 | constraint_8 | (("time" >= 9) AND ("time" < 12))
(6 rows)
DELETE FROM _timescaledb_catalog.dimension_slice WHERE id = 5;
SELECT * FROM missing_slices;
dimension_slice_id | constraint_name | column_name | constraint_expr
--------------------+-----------------+-------------+----------------------------------
5 | constraint_5 | time | (("time" >= 3) AND ("time" < 6))
(1 row)
-- Setting level to ERROR since warnings are printed in different
-- order on PG11 and PG12.
SET client_min_messages TO error;
DROP TABLE chunk_test_int;
RESET client_min_messages;