Skip to content

Commit

Permalink
Repair dimension slice table on update
Browse files Browse the repository at this point in the history
This is a backport of timescale#2558.

In timescale#2800 a a race condition between inserts and `drop_chunks` is fixed
and this commit will repair the dimension slices table by
re-constructing missing dimension slices from the corresponding
constraint expressions.
  • Loading branch information
mkindahl committed Jan 14, 2021
1 parent 59f07dd commit 8d509f8
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
78 changes: 78 additions & 0 deletions sql/updates/latest-dev.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
-- Recreate missing dimension slices that might be missing. If the
-- dimension slice table is broken and there are dimension slices
-- missing from the table, we will repair it by:
--
-- 1. Finding all chunk constraints that have missing dimension
-- slices and extract the constraint expression from the
-- associated constraint.
--
-- 2. Parse the constraint expression and extract the column name,
-- and upper and lower range values as text or, if it is a
-- partition constraint, pick the existing constraint (either
-- uppper or lower end of range) and make the other end open.
--
-- 3. Use the column type to construct the range values (UNIX
-- microseconds) from these strings.
INSERT INTO _timescaledb_catalog.dimension_slice
WITH
-- All dimension slices that are mentioned in the chunk_constraint
-- table but are missing from the dimension_slice table.
missing_slices AS (
SELECT hypertable_id,
chunk_id,
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)
),

-- Unparsed range start and end for each dimension slice id that
-- is missing.
unparsed_missing_slices AS (
SELECT di.id AS dimension_id,
dimension_slice_id,
constraint_name,
column_type,
column_name,
(SELECT SUBSTRING(constraint_expr, $$>=\s*'?([\w\d\s:+-]+)'?$$)) AS range_start,
(SELECT SUBSTRING(constraint_expr, $$<\s*'?([\w\d\s:+-]+)'?$$)) AS range_end
FROM missing_slices JOIN _timescaledb_catalog.dimension di USING (hypertable_id, column_name)
)
SELECT DISTINCT
dimension_slice_id,
dimension_id,
CASE
WHEN column_type = 'timestamptz'::regtype THEN
EXTRACT(EPOCH FROM range_start::timestamptz) * 1000000
WHEN column_type = 'timestamp'::regtype THEN
EXTRACT(EPOCH FROM range_start::timestamp) * 1000000
WHEN column_type = 'date'::regtype THEN
EXTRACT(EPOCH FROM range_start::date) * 1000000
ELSE
CASE
WHEN range_start IS NULL
THEN -9223372036854775808
ELSE range_start::bigint
END
END AS range_start,
CASE
WHEN column_type = 'timestamptz'::regtype THEN
EXTRACT(EPOCH FROM range_end::timestamptz) * 1000000
WHEN column_type = 'timestamp'::regtype THEN
EXTRACT(EPOCH FROM range_end::timestamp) * 1000000
WHEN column_type = 'date'::regtype THEN
EXTRACT(EPOCH FROM range_end::date) * 1000000
ELSE
CASE WHEN range_end IS NULL
THEN 9223372036854775807
ELSE range_end::bigint
END
END AS range_end
FROM unparsed_missing_slices;
8 changes: 8 additions & 0 deletions test/sql/updates/post.post_insert.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ CREATE OR REPLACE FUNCTION timescaledb_integrity_test()
RETURNS VOID LANGUAGE PLPGSQL STABLE AS
$BODY$
DECLARE
dimension_slice RECORD;
constraint_row RECORD;
index_row RECORD;
chunk_count INTEGER;
Expand Down Expand Up @@ -64,6 +65,13 @@ BEGIN
RAISE EXCEPTION 'Missing chunk constraints for %. Expected %, but found %', constraint_row.conname, chunk_count, chunk_constraint_count;
END IF;
END LOOP;

FOR dimension_slice IN
SELECT chunk_id, dimension_slice_id FROM _timescaledb_catalog.chunk_constraint
WHERE dimension_slice_id NOT IN (SELECT id FROM _timescaledb_catalog.dimension_slice)
LOOP
RAISE EXCEPTION 'Missing dimension slice with id % for chunk %.', dimension_slice.dimension_slice_id, dimension_slice.chunk_id;
END LOOP;
END;
$BODY$;

Expand Down

0 comments on commit 8d509f8

Please sign in to comment.