diff --git a/.unreleased/fix_vacuum_compressed_chunk b/.unreleased/fix_vacuum_compressed_chunk new file mode 100644 index 00000000000..a091316a48c --- /dev/null +++ b/.unreleased/fix_vacuum_compressed_chunk @@ -0,0 +1,2 @@ +Fixes: #10286 Propagate VACUUM on a chunk to the compressed relation when running on chunk directly +Thanks: @h0rn3t for reporting a problem with VACUUM not propagating to the compressed relation diff --git a/src/process_utility.c b/src/process_utility.c index 7c9f8df5b7c..78b1355ba2b 100644 --- a/src/process_utility.c +++ b/src/process_utility.c @@ -1282,6 +1282,23 @@ process_vacuum(ProcessUtilityArgs *args) ctx.ht_vacuum_rel = vacuum_rel; foreach_chunk(ht, add_chunk_to_vacuum, &ctx, false); } + else + { + /* VACUUM targets a chunk directly. */ + Chunk *chunk = ts_chunk_get_by_relid(table_relid, false); + if (chunk && ts_chunk_is_compressed(chunk)) + { + Oid compressed_relid = ts_relation_get_compressed_relid(chunk->fd.relid); + /* Compressed chunk might be missing due to concurrent operations */ + if (OidIsValid(compressed_relid)) + { + ctx.chunk_rels = + lappend(ctx.chunk_rels, + makeVacuumRelation(NULL, compressed_relid, NIL)); + } + } + register_chunk_for_rebuild_if_needed(table_relid, &ctx); + } } vacuum_rels = lappend(vacuum_rels, vacuum_rel); } diff --git a/tsl/test/expected/vacuum.out b/tsl/test/expected/vacuum.out index 733c51e0da1..1bf4a0bc716 100644 --- a/tsl/test/expected/vacuum.out +++ b/tsl/test/expected/vacuum.out @@ -300,3 +300,35 @@ DROP FUNCTION cagg_analyze_count_analyzed(name); DROP MATERIALIZED VIEW cagg_analyze_view; NOTICE: drop cascades to table _timescaledb_internal._hyper_6_9_chunk DROP TABLE cagg_analyze_src; +-- VACUUM FULL on an individual chunk should also rewrite the underlying compressed relation +-- VACUUM FULL assigns a new relfilenode, so we use that as a proxy for the +-- compressed relation actually being processed. +CREATE TABLE vacuum_chunk_test(time timestamptz NOT NULL, device int, value float); +SELECT create_hypertable('vacuum_chunk_test', 'time', chunk_time_interval => interval '1 day'); + create_hypertable +-------------------------------- + (7,public,vacuum_chunk_test,t) + +INSERT INTO vacuum_chunk_test +SELECT '2024-01-01'::timestamptz + (i || ' minute')::interval, i % 4, i::float +FROM generate_series(0, 5000) i; +ALTER TABLE vacuum_chunk_test SET (timescaledb.compress, timescaledb.compress_segmentby = 'device'); +SELECT count(compress_chunk(ch)) FROM show_chunks('vacuum_chunk_test') ch; + count +------- + 4 + +SELECT ch AS chunk FROM show_chunks('vacuum_chunk_test') ch ORDER BY ch LIMIT 1 \gset +SELECT cs.compress_relid::oid AS compressed_relid +FROM _timescaledb_catalog.compression_settings cs +WHERE cs.relid = :'chunk'::regclass \gset +SELECT relfilenode AS compressed_relfilenode_before +FROM pg_class WHERE oid = :compressed_relid \gset +VACUUM FULL :chunk; +SELECT relfilenode <> :compressed_relfilenode_before AS compressed_rel_rewritten +FROM pg_class WHERE oid = :compressed_relid; + compressed_rel_rewritten +-------------------------- + t + +DROP TABLE vacuum_chunk_test; diff --git a/tsl/test/sql/vacuum.sql b/tsl/test/sql/vacuum.sql index 56d09aae909..0ef3c1453b9 100644 --- a/tsl/test/sql/vacuum.sql +++ b/tsl/test/sql/vacuum.sql @@ -230,3 +230,32 @@ WHERE parent.hypertable_id = (SELECT mat_hypertable_id DROP FUNCTION cagg_analyze_count_analyzed(name); DROP MATERIALIZED VIEW cagg_analyze_view; DROP TABLE cagg_analyze_src; + +-- VACUUM FULL on an individual chunk should also rewrite the underlying compressed relation +-- VACUUM FULL assigns a new relfilenode, so we use that as a proxy for the +-- compressed relation actually being processed. +CREATE TABLE vacuum_chunk_test(time timestamptz NOT NULL, device int, value float); +SELECT create_hypertable('vacuum_chunk_test', 'time', chunk_time_interval => interval '1 day'); + +INSERT INTO vacuum_chunk_test +SELECT '2024-01-01'::timestamptz + (i || ' minute')::interval, i % 4, i::float +FROM generate_series(0, 5000) i; + +ALTER TABLE vacuum_chunk_test SET (timescaledb.compress, timescaledb.compress_segmentby = 'device'); +SELECT count(compress_chunk(ch)) FROM show_chunks('vacuum_chunk_test') ch; + +SELECT ch AS chunk FROM show_chunks('vacuum_chunk_test') ch ORDER BY ch LIMIT 1 \gset + +SELECT cs.compress_relid::oid AS compressed_relid +FROM _timescaledb_catalog.compression_settings cs +WHERE cs.relid = :'chunk'::regclass \gset + +SELECT relfilenode AS compressed_relfilenode_before +FROM pg_class WHERE oid = :compressed_relid \gset + +VACUUM FULL :chunk; + +SELECT relfilenode <> :compressed_relfilenode_before AS compressed_rel_rewritten +FROM pg_class WHERE oid = :compressed_relid; + +DROP TABLE vacuum_chunk_test;