-
Notifications
You must be signed in to change notification settings - Fork 852
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix handling of chunks with no contraints
When a catalog corruption occurs, and a chunk does not contain any dimension slices, we crash in ts_dimension_slice_cmp(). This patch adds a proper check and errors out before the code path is called.
- Loading branch information
1 parent
7ffdd07
commit 6409560
Showing
4 changed files
with
272 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
-- 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_functions.get_partition_hash(tag) < 1073741823) | ||
2 | 1 | constraint_1 | (("time" >= 3) AND ("time" < 6)) | ||
2 | 3 | constraint_3 | (_timescaledb_functions.get_partition_hash(tag) >= 1073741823) | ||
3 | 3 | constraint_3 | (_timescaledb_functions.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) | ||
|
||
TRUNCATE TABLE chunk_test_int; | ||
WARNING: unexpected state for chunk _timescaledb_internal._hyper_1_1_chunk, dropping anyway | ||
WARNING: unexpected state for chunk _timescaledb_internal._hyper_1_2_chunk, dropping anyway | ||
DROP TABLE chunk_test_int; | ||
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_functions.get_partition_hash(tag) < 1073741823) | ||
5 | 5 | constraint_5 | (("time" >= 3) AND ("time" < 6)) | ||
5 | 7 | constraint_7 | (_timescaledb_functions.get_partition_hash(tag) >= 1073741823) | ||
6 | 7 | constraint_7 | (_timescaledb_functions.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) | ||
|
||
DROP TABLE chunk_test_int; | ||
WARNING: unexpected state for chunk _timescaledb_internal._hyper_2_4_chunk, dropping anyway | ||
WARNING: unexpected state for chunk _timescaledb_internal._hyper_2_5_chunk, dropping anyway | ||
--- Test handling of missing dimension slices | ||
CREATE TABLE dim_test(time TIMESTAMPTZ, device int); | ||
SELECT create_hypertable('dim_test', 'time', chunk_time_interval => INTERVAL '1 day'); | ||
NOTICE: adding not-null constraint to column "time" | ||
create_hypertable | ||
----------------------- | ||
(3,public,dim_test,t) | ||
(1 row) | ||
|
||
-- Create two chunks | ||
INSERT INTO dim_test values('2000-01-01 00:00:00', 1); | ||
INSERT INTO dim_test values('2020-01-01 00:00:00', 1); | ||
SELECT id AS dim_slice_id FROM _timescaledb_catalog.dimension_slice | ||
ORDER BY id DESC LIMIT 1 | ||
\gset | ||
-- Delete the dimension slice for the second chunk | ||
DELETE FROM _timescaledb_catalog.chunk_constraint WHERE dimension_slice_id = :dim_slice_id; | ||
\set ON_ERROR_STOP 0 | ||
-- Select data | ||
SELECT * FROM dim_test; | ||
ERROR: chunk _hyper_3_8_chunk has no dimension slices | ||
-- Select data using ordered append | ||
SELECT * FROM dim_test ORDER BY time; | ||
ERROR: chunk _hyper_3_8_chunk has no dimension slices | ||
\set ON_ERROR_STOP 1 | ||
DROP TABLE dim_test; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
-- 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); | ||
|
||
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; | ||
|
||
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; | ||
|
||
DELETE FROM _timescaledb_catalog.dimension_slice WHERE id = 1; | ||
SELECT * FROM missing_slices; | ||
|
||
TRUNCATE TABLE chunk_test_int; | ||
DROP TABLE chunk_test_int; | ||
|
||
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); | ||
|
||
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; | ||
|
||
DELETE FROM _timescaledb_catalog.dimension_slice WHERE id = 5; | ||
SELECT * FROM missing_slices; | ||
|
||
DROP TABLE chunk_test_int; | ||
|
||
--- Test handling of missing dimension slices | ||
CREATE TABLE dim_test(time TIMESTAMPTZ, device int); | ||
SELECT create_hypertable('dim_test', 'time', chunk_time_interval => INTERVAL '1 day'); | ||
|
||
-- Create two chunks | ||
INSERT INTO dim_test values('2000-01-01 00:00:00', 1); | ||
INSERT INTO dim_test values('2020-01-01 00:00:00', 1); | ||
|
||
SELECT id AS dim_slice_id FROM _timescaledb_catalog.dimension_slice | ||
ORDER BY id DESC LIMIT 1 | ||
\gset | ||
|
||
-- Delete the dimension slice for the second chunk | ||
DELETE FROM _timescaledb_catalog.chunk_constraint WHERE dimension_slice_id = :dim_slice_id; | ||
|
||
\set ON_ERROR_STOP 0 | ||
|
||
-- Select data | ||
SELECT * FROM dim_test; | ||
|
||
-- Select data using ordered append | ||
SELECT * FROM dim_test ORDER BY time; | ||
|
||
\set ON_ERROR_STOP 1 | ||
|
||
DROP TABLE dim_test; | ||
|