diff --git a/.unreleased/pr_7413 b/.unreleased/pr_7413 new file mode 100644 index 00000000000..53f2a223bae --- /dev/null +++ b/.unreleased/pr_7413 @@ -0,0 +1 @@ +Implements: #7413: Add GUC for segmentwise recompression. diff --git a/src/guc.c b/src/guc.c index 7786505a49c..c4c52dd7b8d 100644 --- a/src/guc.c +++ b/src/guc.c @@ -139,6 +139,7 @@ TSDLLEXPORT bool ts_guc_enable_compression_indexscan = false; TSDLLEXPORT bool ts_guc_enable_bulk_decompression = true; TSDLLEXPORT bool ts_guc_auto_sparse_indexes = true; bool ts_guc_enable_chunk_skipping = false; +TSDLLEXPORT bool ts_guc_enable_segmentwise_recompression = true; /* Enable of disable columnar scans for columnar-oriented storage engines. If * disabled, regular sequence scans will be used instead. */ @@ -703,6 +704,17 @@ _guc_init(void) NULL, NULL); + DefineCustomBoolVariable(MAKE_EXTOPTION("enable_segmentwise_recompression"), + "Enable segmentwise recompression functionality", + "Enable segmentwise recompression", + &ts_guc_enable_segmentwise_recompression, + true, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + /* * Define the limit on number of invalidation-based refreshes we allow per * refresh call. If this limit is exceeded, fall back to a single refresh that diff --git a/src/guc.h b/src/guc.h index fa3503f6cea..7c0fb5e0606 100644 --- a/src/guc.h +++ b/src/guc.h @@ -52,6 +52,7 @@ extern bool ts_guc_enable_tss_callbacks; extern TSDLLEXPORT bool ts_guc_enable_delete_after_compression; extern TSDLLEXPORT bool ts_guc_enable_merge_on_cagg_refresh; extern bool ts_guc_enable_chunk_skipping; +extern TSDLLEXPORT bool ts_guc_enable_segmentwise_recompression; #ifdef USE_TELEMETRY typedef enum TelemetryLevel diff --git a/tsl/src/compression/api.c b/tsl/src/compression/api.c index dcb5d769077..74e626d8536 100644 --- a/tsl/src/compression/api.c +++ b/tsl/src/compression/api.c @@ -8,6 +8,7 @@ * compress and decompress chunks */ #include +#include "guc.h" #include #include #include @@ -919,12 +920,19 @@ tsl_compress_chunk_wrapper(Chunk *chunk, bool if_not_compressed, bool recompress return uncompressed_chunk_id; } - if (ts_chunk_is_partial(chunk) && get_compressed_chunk_index_for_recompression(chunk)) + if (ts_guc_enable_segmentwise_recompression && ts_chunk_is_partial(chunk) && + get_compressed_chunk_index_for_recompression(chunk)) { uncompressed_chunk_id = recompress_chunk_segmentwise_impl(chunk); } else { + if (!ts_guc_enable_segmentwise_recompression) + elog(DEBUG1, + "segmentwise recompression is disabled, performing full recompression on " + "chunk \"%s.%s\"", + NameStr(chunk->fd.schema_name), + NameStr(chunk->fd.table_name)); decompress_chunk_impl(chunk, false); compress_chunk_impl(chunk->hypertable_relid, chunk->table_id); } @@ -1257,6 +1265,14 @@ tsl_recompress_chunk_segmentwise(PG_FUNCTION_ARGS) } else { + if (!ts_guc_enable_segmentwise_recompression) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("segmentwise recompression functionality disabled, " + "enable it by first setting " + "timescaledb.enable_segmentwise_recompression to on"))); + } uncompressed_chunk_id = recompress_chunk_segmentwise_impl(chunk); } diff --git a/tsl/test/expected/recompress_chunk_segmentwise.out b/tsl/test/expected/recompress_chunk_segmentwise.out index d984b713a93..2c70890ecb7 100644 --- a/tsl/test/expected/recompress_chunk_segmentwise.out +++ b/tsl/test/expected/recompress_chunk_segmentwise.out @@ -593,3 +593,61 @@ select * from :compressed_chunk_name; 2 | 1 | | Sat Jan 01 01:00:00 2022 PST | Sat Jan 01 01:00:00 2022 PST | BAAAAneAR/JEAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAA7gAE7wCP5IgAAATvAI/kh/8= | BAEAAAAAAAAABAAAAAAAAAAEAAAAAQAAAAEAAAAAAAAABAAAAAAAAAAIAAAAAgAAAAEAAAAAAAAAAQAAAAAAAAAB (5 rows) +--- Test behaviour when enable_segmentwise_recompression GUC if OFF +CREATE TABLE guc_test(time timestamptz not null, a int, b int, c int); +SELECT create_hypertable('guc_test', by_range('time', INTERVAL '1 day')); + create_hypertable +------------------- + (18,t) +(1 row) + +ALTER TABLE guc_test set (timescaledb.compress, timescaledb.compress_segmentby = 'a, b'); +NOTICE: default order by for hypertable "guc_test" is set to ""time" DESC" +INSERT INTO guc_test VALUES ('2024-10-30 14:04:00.501519-06'::timestamptz, 1, 1, 1); +SELECT show_chunks as chunk_to_compress FROM show_chunks('guc_test') LIMIT 1 \gset +SELECT compress_chunk(:'chunk_to_compress'); + compress_chunk +------------------------------------------ + _timescaledb_internal._hyper_18_20_chunk +(1 row) + +INSERT INTO guc_test VALUES ('2024-10-30 14:14:00.501519-06'::timestamptz, 1, 1, 2); +-- When GUC is OFF, recompress function should throw an error +SET timescaledb.enable_segmentwise_recompression TO OFF; +\set ON_ERROR_STOP 0 +SELECT _timescaledb_functions.recompress_chunk_segmentwise(:'chunk_to_compress'); +ERROR: segmentwise recompression functionality disabled, enable it by first setting timescaledb.enable_segmentwise_recompression to on +\set ON_ERROR_STOP 1 +-- When GUC is OFF, entire chunk should be fully uncompressed and compressed instead +SET client_min_messages TO DEBUG; +SELECT compress_chunk(:'chunk_to_compress'); +LOG: statement: SELECT compress_chunk('_timescaledb_internal._hyper_18_20_chunk'); +DEBUG: capture_ExecutorStart - rtable #0: (null) (relid: 0) +DEBUG: segmentwise recompression is disabled, performing full recompression on chunk "_timescaledb_internal._hyper_18_20_chunk" +DEBUG: acquiring locks for decompressing "_timescaledb_internal._hyper_18_20_chunk" +DEBUG: locks acquired for decompressing "_timescaledb_internal._hyper_18_20_chunk" +DEBUG: finished decompressing 1 rows from "compress_hyper_19_21_chunk" +DEBUG: drop auto-cascades to type _timescaledb_internal.compress_hyper_19_21_chunk +DEBUG: drop auto-cascades to type _timescaledb_internal.compress_hyper_19_21_chunk[] +DEBUG: drop auto-cascades to toast table pg_toast.pg_toast_17376 +DEBUG: drop auto-cascades to index pg_toast.pg_toast_17376_index +DEBUG: drop auto-cascades to index _timescaledb_internal.compress_hyper_19_21_chunk_a_b__ts_meta_min_1__ts_meta_max__idx +DEBUG: acquiring locks for compressing "_timescaledb_internal._hyper_18_20_chunk" +DEBUG: locks acquired for compressing "_timescaledb_internal._hyper_18_20_chunk" +DEBUG: building index "pg_toast_17384_index" on table "pg_toast_17384" serially +DEBUG: index "pg_toast_17384_index" can safely use deduplication +DEBUG: building index "compress_hyper_19_22_chunk_a_b__ts_meta_min_1__ts_meta_max__idx" on table "compress_hyper_19_22_chunk" serially +DEBUG: index "compress_hyper_19_22_chunk_a_b__ts_meta_min_1__ts_meta_max__idx" can safely use deduplication +DEBUG: adding index compress_hyper_19_22_chunk_a_b__ts_meta_min_1__ts_meta_max__idx ON _timescaledb_internal.compress_hyper_19_22_chunk USING BTREE(a, b, _ts_meta_min_1 DESC, _ts_meta_max_1 DESC, ) +DEBUG: new compressed chunk "_timescaledb_internal.compress_hyper_19_22_chunk" created +DEBUG: using tuplesort to scan rows from "_hyper_18_20_chunk" for compression +DEBUG: finished compressing 2 rows from "_hyper_18_20_chunk" +DEBUG: building index "_hyper_18_20_chunk_guc_test_time_idx" on table "_hyper_18_20_chunk" serially +DEBUG: index "_hyper_18_20_chunk_guc_test_time_idx" can safely use deduplication + compress_chunk +------------------------------------------ + _timescaledb_internal._hyper_18_20_chunk +(1 row) + +RESET client_min_messages; +LOG: statement: RESET client_min_messages; diff --git a/tsl/test/sql/recompress_chunk_segmentwise.sql b/tsl/test/sql/recompress_chunk_segmentwise.sql index 4e540212275..c3994cee82e 100644 --- a/tsl/test/sql/recompress_chunk_segmentwise.sql +++ b/tsl/test/sql/recompress_chunk_segmentwise.sql @@ -290,3 +290,23 @@ select * from :compressed_chunk_name; insert into nullseg_many values (:'start_time', 1, NULL, NULL); SELECT compress_chunk(:'chunk_to_compress'); select * from :compressed_chunk_name; + +--- Test behaviour when enable_segmentwise_recompression GUC if OFF +CREATE TABLE guc_test(time timestamptz not null, a int, b int, c int); +SELECT create_hypertable('guc_test', by_range('time', INTERVAL '1 day')); + +ALTER TABLE guc_test set (timescaledb.compress, timescaledb.compress_segmentby = 'a, b'); +INSERT INTO guc_test VALUES ('2024-10-30 14:04:00.501519-06'::timestamptz, 1, 1, 1); +SELECT show_chunks as chunk_to_compress FROM show_chunks('guc_test') LIMIT 1 \gset +SELECT compress_chunk(:'chunk_to_compress'); + +INSERT INTO guc_test VALUES ('2024-10-30 14:14:00.501519-06'::timestamptz, 1, 1, 2); +-- When GUC is OFF, recompress function should throw an error +SET timescaledb.enable_segmentwise_recompression TO OFF; +\set ON_ERROR_STOP 0 +SELECT _timescaledb_functions.recompress_chunk_segmentwise(:'chunk_to_compress'); +\set ON_ERROR_STOP 1 +-- When GUC is OFF, entire chunk should be fully uncompressed and compressed instead +SET client_min_messages TO DEBUG; +SELECT compress_chunk(:'chunk_to_compress'); +RESET client_min_messages;