mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-18 19:59:48 +08:00
When executing `recompress_chunk` and a query at the same time, a deadlock can be generated because the chunk relation and the chunk index and the compressed and uncompressd chunks are locked in different orders. In particular, when `recompress_chunk` is executing, it will first decompress the chunk and as part of that lock the uncompressed chunk index in AccessExclusive mode and when trying to compress the chunk again it will try to lock the uncompressed chunk in AccessExclusive as part of truncating it. Note that `decompress_chunk` and `compress_chunk` lock the relations in the same order and the issue arises because the procedures are combined inth a single transaction. To avoid the deadlock, this commit rewrites the `recompress_chunk` to be a procedure and adds a commit between the decompression and compression. Committing the transaction after the decompress will allow reads and inserts to proceed by working on the uncompressed chunk, and the compression part of the procedure will take the necessary locks in strict order, thereby avoiding a deadlock. In addition, the isolation test is rewritten so that instead of adding a waitpoint in the PL/SQL function, we implement the isolation test by taking a lock on the compressed table after the decompression. Fixes #3846
71 lines
2.5 KiB
PL/PgSQL
71 lines
2.5 KiB
PL/PgSQL
-- 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.
|
|
|
|
-- chunk - the OID of the chunk to be CLUSTERed
|
|
-- index - the OID of the index to be CLUSTERed on, or NULL to use the index
|
|
-- last used
|
|
CREATE OR REPLACE FUNCTION reorder_chunk(
|
|
chunk REGCLASS,
|
|
index REGCLASS=NULL,
|
|
verbose BOOLEAN=FALSE
|
|
) RETURNS VOID AS '@MODULE_PATHNAME@', 'ts_reorder_chunk' LANGUAGE C VOLATILE;
|
|
|
|
CREATE OR REPLACE FUNCTION move_chunk(
|
|
chunk REGCLASS,
|
|
destination_tablespace Name,
|
|
index_destination_tablespace Name=NULL,
|
|
reorder_index REGCLASS=NULL,
|
|
verbose BOOLEAN=FALSE
|
|
) RETURNS VOID AS '@MODULE_PATHNAME@', 'ts_move_chunk' LANGUAGE C VOLATILE;
|
|
|
|
CREATE OR REPLACE FUNCTION compress_chunk(
|
|
uncompressed_chunk REGCLASS,
|
|
if_not_compressed BOOLEAN = false
|
|
) RETURNS REGCLASS AS '@MODULE_PATHNAME@', 'ts_compress_chunk' LANGUAGE C STRICT VOLATILE;
|
|
|
|
CREATE OR REPLACE FUNCTION decompress_chunk(
|
|
uncompressed_chunk REGCLASS,
|
|
if_compressed BOOLEAN = false
|
|
) RETURNS REGCLASS AS '@MODULE_PATHNAME@', 'ts_decompress_chunk' LANGUAGE C STRICT VOLATILE;
|
|
|
|
-- Recompress a chunk
|
|
--
|
|
-- Will give an error if the chunk was not already compressed. In this
|
|
-- case, the user should use compress_chunk instead. Note that this
|
|
-- function cannot be executed in an explicit transaction since it
|
|
-- contains transaction control commands.
|
|
--
|
|
-- Parameters:
|
|
-- chunk: Chunk to recompress.
|
|
-- if_not_compressed: Print notice instead of error if chunk is already compressed.
|
|
CREATE OR REPLACE PROCEDURE recompress_chunk(chunk REGCLASS,
|
|
if_not_compressed BOOLEAN = false)
|
|
AS $$
|
|
DECLARE
|
|
status INT;
|
|
chunk_name TEXT[];
|
|
BEGIN
|
|
status := _timescaledb_internal.chunk_status(chunk);
|
|
|
|
-- Chunk names are in the internal catalog, but we only care about
|
|
-- the chunk name here.
|
|
chunk_name := parse_ident(chunk::text);
|
|
CASE status
|
|
WHEN 0 THEN
|
|
RAISE EXCEPTION 'call compress_chunk instead of recompress_chunk';
|
|
WHEN 1 THEN
|
|
IF if_not_compressed THEN
|
|
RAISE NOTICE 'nothing to recompress in chunk "%"', chunk_name[array_upper(chunk_name,1)];
|
|
RETURN;
|
|
ELSE
|
|
RAISE EXCEPTION 'nothing to recompress in chunk "%"', chunk_name[array_upper(chunk_name,1)];
|
|
END IF;
|
|
WHEN 3 THEN
|
|
PERFORM decompress_chunk(chunk);
|
|
COMMIT;
|
|
END CASE;
|
|
PERFORM compress_chunk(chunk, if_not_compressed);
|
|
END
|
|
$$ LANGUAGE plpgsql;
|