Ensure that chunks are aligned.

Previously, chunks could end up unaligned betweeen partitions if the chunk_time_interval
was updated between chunk creations. Now there is a check when a chunk is created whether there is
a chunk matching the time point in another partition and if there is, the new chunk is created
with the same interval.
This commit is contained in:
Olof Rensfelt 2017-06-09 14:12:19 +02:00 committed by RobAtticus
parent 73622bf1eb
commit 2a01ebca61
3 changed files with 115 additions and 8 deletions

View File

@ -18,7 +18,7 @@ $BODY$;
-- static
CREATE OR REPLACE FUNCTION _timescaledb_internal.calculate_new_chunk_times(
partition_id INT,
"time" BIGINT,
time_point BIGINT,
OUT table_start BIGINT,
OUT table_end BIGINT
)
@ -27,8 +27,11 @@ $BODY$
DECLARE
partition_epoch_row _timescaledb_catalog.partition_epoch;
chunk_row _timescaledb_catalog.chunk;
time_interval BIGINT;
time_interval BIGINT;
start_distinct_ctn BIGINT;
end_distinct_ctn BIGINT;
BEGIN
SELECT pe.*
INTO partition_epoch_row
FROM _timescaledb_catalog.partition p
@ -36,6 +39,37 @@ BEGIN
WHERE p.id = partition_id
FOR SHARE;
-- Check if there are chunks in other partitions.
-- If so, use their time range to keep chunks time aligned
SELECT COUNT(DISTINCT(start_time)), COUNT(DISTINCT(end_time))
INTO start_distinct_ctn, end_distinct_ctn
FROM _timescaledb_catalog.chunk c
INNER JOIN _timescaledb_catalog.partition p ON (p.id = c.partition_id)
WHERE c.start_time <= time_point AND
c.end_time >= time_point AND
p.epoch_id = partition_epoch_row.id;
-- Sanity check that all previous chunks between partitions are
-- aligned
IF start_distinct_ctn != 0 or end_distinct_ctn != 0 THEN
-- At least one chunk exists, check that all chunks
-- from other partitions are aligned
IF start_distinct_ctn != 1 OR end_distinct_ctn != 1 THEN
RAISE EXCEPTION 'Unaligned chunks between partitions';
END IF;
SELECT start_time, end_time
INTO table_start, table_end
FROM _timescaledb_catalog.chunk AS c
INNER JOIN _timescaledb_catalog.partition p ON (p.id = c.partition_id)
WHERE c.start_time <= time_point AND
c.end_time >= time_point AND
p.epoch_id = partition_epoch_row.id LIMIT 1;
RETURN;
END IF;
SELECT chunk_time_interval
INTO time_interval
FROM _timescaledb_catalog.hypertable ht
@ -43,7 +77,7 @@ BEGIN
-- Create start and stop times for the new chunk, subtract 1 from the end time
-- as chunk intervals are inclusive.
table_start := ("time" / time_interval) * time_interval;
table_start := (time_point / time_interval) * time_interval;
table_end := table_start + time_interval - 1;
-- Check whether the new chunk interval overlaps with existing chunks.
@ -79,7 +113,8 @@ BEGIN
END
$BODY$;
-- creates the row in the chunk table. Prerequisite: appropriate lock.
-- creates the row in the chunk table. Prerequisite: appropriate lock and check that chunk does not
-- already exist.
-- static
CREATE OR REPLACE FUNCTION _timescaledb_internal.create_chunk_row(
part_id INT,
@ -90,8 +125,8 @@ $BODY$
DECLARE
table_start BIGINT;
table_end BIGINT;
BEGIN
BEGIN
SELECT *
INTO table_start, table_end
FROM _timescaledb_internal.calculate_new_chunk_times(part_id, time_point);
@ -131,7 +166,7 @@ BEGIN
END IF;
IF chunk_row IS NULL THEN -- recheck
RAISE EXCEPTION 'Should never happen: chunk not found after creation on meta'
RAISE EXCEPTION 'Should never happen: chunk not found after creation'
USING ERRCODE = 'IO501';
END IF;

View File

@ -78,3 +78,48 @@ SELECT * FROM _timescaledb_catalog.chunk c
4 | 1 | 10 | 39 | _timescaledb_internal | _hyper_1_4_chunk | 1 | 1 | 0 | 16383 | | 1 | 1 | | | 2 | _timescaledb_internal | get_partition_for_key | 32768 | device_id | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | time | bigint | 40
(4 rows)
-- Test chunk aligning between partitions
CREATE TABLE chunk_align_test(
time BIGINT,
metric INTEGER,
device_id TEXT
);
SELECT * FROM create_hypertable('chunk_align_test', 'time', 'device_id', 2, chunk_time_interval => 10);
create_hypertable
-------------------
(1 row)
INSERT INTO chunk_align_test VALUES (1, 1, 'dev1'); -- this should create a 10 wide chunk
SELECT c.id,partition_id,c.start_time,c.end_time,h.table_name,p.epoch_id FROM _timescaledb_catalog.chunk c
LEFT JOIN _timescaledb_catalog.partition p ON (p.id = c.partition_id)
LEFT JOIN _timescaledb_catalog.partition_epoch pe ON (pe.id = p.epoch_id)
LEFT JOIN _timescaledb_catalog.hypertable h ON (pe.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_align_test'
ORDER BY c.id;
id | partition_id | start_time | end_time | table_name | epoch_id
----+--------------+------------+----------+------------------+----------
5 | 3 | 0 | 9 | chunk_align_test | 2
(1 row)
SELECT * FROM set_chunk_time_interval('chunk_align_test', 40::bigint);
set_chunk_time_interval
-------------------------
(1 row)
INSERT INTO chunk_align_test VALUES (5, 1, 'dev2'); -- this should still create a 10 wide chunk
INSERT INTO chunk_align_test VALUES (45, 1, 'dev2'); -- this should create a 40 wide chunk
SELECT c.id,partition_id,c.start_time,c.end_time,h.table_name,p.epoch_id FROM _timescaledb_catalog.chunk c
LEFT JOIN _timescaledb_catalog.partition p ON (p.id = c.partition_id)
LEFT JOIN _timescaledb_catalog.partition_epoch pe ON (pe.id = p.epoch_id)
LEFT JOIN _timescaledb_catalog.hypertable h ON (pe.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_align_test'
ORDER BY c.id;
id | partition_id | start_time | end_time | table_name | epoch_id
----+--------------+------------+----------+------------------+----------
5 | 3 | 0 | 9 | chunk_align_test | 2
6 | 4 | 0 | 9 | chunk_align_test | 2
7 | 4 | 40 | 79 | chunk_align_test | 2
(3 rows)

View File

@ -17,7 +17,6 @@ INSERT INTO chunk_test VALUES (1, 1, 'dev1'),
SELECT * FROM set_chunk_time_interval('chunk_test', 40::bigint);
INSERT INTO chunk_test VALUES(23, 3, 'dev3');
SELECT * FROM chunk_test order by time, metric, device_id;
@ -32,3 +31,31 @@ SELECT * FROM _timescaledb_catalog.chunk c
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_test'
ORDER BY c.id;
-- Test chunk aligning between partitions
CREATE TABLE chunk_align_test(
time BIGINT,
metric INTEGER,
device_id TEXT
);
SELECT * FROM create_hypertable('chunk_align_test', 'time', 'device_id', 2, chunk_time_interval => 10);
INSERT INTO chunk_align_test VALUES (1, 1, 'dev1'); -- this should create a 10 wide chunk
SELECT c.id,partition_id,c.start_time,c.end_time,h.table_name,p.epoch_id FROM _timescaledb_catalog.chunk c
LEFT JOIN _timescaledb_catalog.partition p ON (p.id = c.partition_id)
LEFT JOIN _timescaledb_catalog.partition_epoch pe ON (pe.id = p.epoch_id)
LEFT JOIN _timescaledb_catalog.hypertable h ON (pe.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_align_test'
ORDER BY c.id;
SELECT * FROM set_chunk_time_interval('chunk_align_test', 40::bigint);
INSERT INTO chunk_align_test VALUES (5, 1, 'dev2'); -- this should still create a 10 wide chunk
INSERT INTO chunk_align_test VALUES (45, 1, 'dev2'); -- this should create a 40 wide chunk
SELECT c.id,partition_id,c.start_time,c.end_time,h.table_name,p.epoch_id FROM _timescaledb_catalog.chunk c
LEFT JOIN _timescaledb_catalog.partition p ON (p.id = c.partition_id)
LEFT JOIN _timescaledb_catalog.partition_epoch pe ON (pe.id = p.epoch_id)
LEFT JOIN _timescaledb_catalog.hypertable h ON (pe.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_align_test'
ORDER BY c.id;