Propagate vacuum/analyze to compressed chunks

With recent changes, we enabled analyze on uncompressed chunk tables
for compressed chunks. This change includes analyzing the compressed
chunks table when analyzing the hypertable and its chunks,
enabling us to remove the generating stats when compressing chunks.
This commit is contained in:
Ante Kresic 2023-03-30 12:46:44 +02:00 committed by Ante Kresic
parent 3f9cb3c27a
commit 464d20fb41
4 changed files with 104 additions and 54 deletions

View File

@ -18,6 +18,7 @@ accidentally triggering the load of a previous DB version.**
* #5417 Refactor and optimize distributed COPY
* #5454 Add support for ON CONFLICT DO UPDATE for compressed hypertables
* #5547 Skip Ordered Append when only 1 child node is present
* #5510 Propagate vacuum/analyze to compressed chunks
**Bugfixes**
* #5233 Out of on_proc_exit slots on guc license change

View File

@ -759,15 +759,8 @@ typedef struct VacuumCtx
{
VacuumRelation *ht_vacuum_rel;
List *chunk_rels;
List *chunk_pairs;
} VacuumCtx;
typedef struct ChunkPair
{
Oid uncompressed_relid;
Oid compressed_relid;
} ChunkPair;
/* Adds a chunk to the list of tables to be vacuumed */
static void
add_chunk_to_vacuum(Hypertable *ht, Oid chunk_relid, void *arg)
@ -783,6 +776,18 @@ add_chunk_to_vacuum(Hypertable *ht, Oid chunk_relid, void *arg)
chunk_vacuum_rel =
makeVacuumRelation(chunk_range_var, chunk_relid, ctx->ht_vacuum_rel->va_cols);
ctx->chunk_rels = lappend(ctx->chunk_rels, chunk_vacuum_rel);
/* If we have a compressed chunk, make sure to analyze it as well */
if (chunk->fd.compressed_chunk_id != INVALID_CHUNK_ID)
{
Chunk *comp_chunk = ts_chunk_get_by_id(chunk->fd.compressed_chunk_id, false);
/* Compressed chunk might be missing due to concurrent operations */
if (comp_chunk)
{
chunk_vacuum_rel = makeVacuumRelation(NULL, comp_chunk->table_id, NIL);
ctx->chunk_rels = lappend(ctx->chunk_rels, chunk_vacuum_rel);
}
}
}
/*
@ -856,7 +861,6 @@ process_vacuum(ProcessUtilityArgs *args)
VacuumCtx ctx = {
.ht_vacuum_rel = NULL,
.chunk_rels = NIL,
.chunk_pairs = NIL,
};
ListCell *lc;
Hypertable *ht;
@ -1119,7 +1123,7 @@ process_truncate(ProcessUtilityArgs *args)
{
Chunk *compressed_chunk =
ts_chunk_get_by_id(chunk->fd.compressed_chunk_id, false);
if (compressed_chunk != NULL)
if (compressed_chunk != NULL && !compressed_chunk->fd.dropped)
{
/* Create list item into the same context of the list. */
oldctx = MemoryContextSwitchTo(parsetreectx);

View File

@ -1245,7 +1245,7 @@ WHERE series_id IN (SELECT series_id FROM compressed);
DROP TABLE compressed_ht;
DROP TABLE uncompressed_ht;
-- Test that pg_stats and pg_class stats for uncompressed chunks are frozen at compression time
-- Test that pg_stats and pg_class stats for uncompressed chunks are correctly updated after compression.
-- Note that approximate_row_count pulls from pg_class
CREATE TABLE stattest(time TIMESTAMPTZ NOT NULL, c1 int);
SELECT create_hypertable('stattest', 'time');
@ -1281,6 +1281,7 @@ SELECT approximate_row_count('stattest');
0
(1 row)
-- Uncompressed chunk table is empty since we just compressed the chunk and moved everything to compressed chunk table.
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :statchunk;
relpages | reltuples
@ -1300,13 +1301,13 @@ FROM _timescaledb_catalog.hypertable ht, _timescaledb_catalog.chunk ch
WHERE ht.table_name = 'stattest' AND ch.hypertable_id = ht.id
AND compch.id = ch.compressed_chunk_id AND ch.compressed_chunk_id > 0 \gset
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END AS reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
relpages | reltuples
----------+-----------
0 | 0
(1 row)
-- Now verify stats are not changed when we analyze the hypertable
-- Now verify stats are updated on compressed chunk table when we analyze the hypertable.
ANALYZE stattest;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
histogram_bounds
@ -1320,38 +1321,51 @@ SELECT histogram_bounds FROM pg_stats WHERE tablename = 'stattest' AND attname =
------------------
(0 rows)
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :statchunk;
relpages | reltuples
----------+-----------
0 | 0
(1 row)
-- verify that corresponding compressed chunk's stats is updated as well.
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
relpages | reltuples
----------+-----------
0 | 0
(1 row)
-- Verify that even a global analyze doesn't affect the chunk stats, changing message scope here
-- to hide WARNINGs for skipped tables
SET client_min_messages TO ERROR;
ANALYZE;
SET client_min_messages TO NOTICE;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
histogram_bounds
-------------------------------------------------------------------------------------------------------------------------------
{0,250,500,750,1000,1250,1500,1750,2000,2250,2500,2750,3000,3250,3500,3750,4000,4250,4500,4750,5000,5250,5500,5750,6000,6250}
(1 row)
SELECT relpages, reltuples FROM pg_class WHERE relname = :statchunk;
relpages | reltuples
----------+-----------
0 | 0
(1 row)
-- verify that corresponding compressed chunk table stats is updated as well.
SELECT relpages, reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
relpages | reltuples
----------+-----------
1 | 1
(1 row)
-- Verify partial chunk stats are handled correctly when analyzing
-- for both uncompressed and compressed chunk tables
INSERT INTO stattest SELECT '2020/02/20 01:00'::TIMESTAMPTZ + ('1 hour'::interval * v), 250 * v FROM generate_series(25,50) v;
ANALYZE stattest;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
histogram_bounds
------------------------------------------------------------------------------------------------------------------------------------------------
{6250,6500,6750,7000,7250,7500,7750,8000,8250,8500,8750,9000,9250,9500,9750,10000,10250,10500,10750,11000,11250,11500,11750,12000,12250,12500}
(1 row)
-- Hypertable will now see the histogram bounds since we have data in the uncompressed chunk table.
SELECT histogram_bounds FROM pg_stats WHERE tablename = 'stattest' AND attname = 'c1';
histogram_bounds
------------------------------------------------------------------------------------------------------------------------------------------------
{6250,6500,6750,7000,7250,7500,7750,8000,8250,8500,8750,9000,9250,9500,9750,10000,10250,10500,10750,11000,11250,11500,11750,12000,12250,12500}
(1 row)
-- verify that corresponding uncompressed chunk table stats is updated as well.
SELECT relpages, reltuples FROM pg_class WHERE relname = :statchunk;
relpages | reltuples
----------+-----------
1 | 26
(1 row)
-- verify that corresponding compressed chunk table stats have not changed since
-- we didn't compress anything new.
SELECT relpages, reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
relpages | reltuples
----------+-----------
1 | 1
(1 row)
-- Verify that decompressing the chunk restores autoanalyze to the hypertable's setting
SELECT reloptions FROM pg_class WHERE relname = :statchunk;
reloptions
@ -1396,6 +1410,23 @@ SELECT reloptions FROM pg_class WHERE relname = :statchunk;
{autovacuum_enabled=false}
(1 row)
-- Verify that even a global analyze works as well, changing message scope here
-- to hide WARNINGs for skipped tables
SET client_min_messages TO ERROR;
ANALYZE;
SET client_min_messages TO NOTICE;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk and attname = 'c1';
histogram_bounds
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{0,250,500,750,1000,1250,1500,1750,2000,2250,2500,2750,3000,3250,3500,3750,4000,4250,4500,4750,5000,5250,5500,5750,6000,6500,6750,7000,7250,7500,7750,8000,8250,8500,8750,9000,9250,9500,9750,10000,10250,10500,10750,11000,11250,11500,11750,12000,12250,12500}
(1 row)
SELECT relpages, reltuples FROM pg_class WHERE relname = :statchunk;
relpages | reltuples
----------+-----------
1 | 52
(1 row)
DROP TABLE stattest;
--- Test that analyze on compression internal table updates stats on original chunks
CREATE TABLE stattest2(time TIMESTAMPTZ NOT NULL, c1 int, c2 int);

View File

@ -524,7 +524,7 @@ WHERE series_id IN (SELECT series_id FROM compressed);
DROP TABLE compressed_ht;
DROP TABLE uncompressed_ht;
-- Test that pg_stats and pg_class stats for uncompressed chunks are frozen at compression time
-- Test that pg_stats and pg_class stats for uncompressed chunks are correctly updated after compression.
-- Note that approximate_row_count pulls from pg_class
CREATE TABLE stattest(time TIMESTAMPTZ NOT NULL, c1 int);
SELECT create_hypertable('stattest', 'time');
@ -537,6 +537,7 @@ ALTER TABLE stattest SET (timescaledb.compress);
SELECT approximate_row_count('stattest');
SELECT compress_chunk(c) FROM show_chunks('stattest') c;
SELECT approximate_row_count('stattest');
-- Uncompressed chunk table is empty since we just compressed the chunk and moved everything to compressed chunk table.
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :statchunk;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
@ -548,28 +549,32 @@ FROM _timescaledb_catalog.hypertable ht, _timescaledb_catalog.chunk ch
AND compch.id = ch.compressed_chunk_id AND ch.compressed_chunk_id > 0 \gset
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END AS reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
-- Now verify stats are not changed when we analyze the hypertable
-- Now verify stats are updated on compressed chunk table when we analyze the hypertable.
ANALYZE stattest;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
-- Unfortunately, the stats on the hypertable won't find any rows to sample from the chunk
SELECT histogram_bounds FROM pg_stats WHERE tablename = 'stattest' AND attname = 'c1';
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :statchunk;
-- verify that corresponding compressed chunk's stats is updated as well.
-- reltuples is initially -1 on PG14 before VACUUM/ANALYZE was run
SELECT relpages, CASE WHEN reltuples > 0 THEN reltuples ELSE 0 END as reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
-- Verify that even a global analyze doesn't affect the chunk stats, changing message scope here
-- to hide WARNINGs for skipped tables
SET client_min_messages TO ERROR;
ANALYZE;
SET client_min_messages TO NOTICE;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
SELECT relpages, reltuples FROM pg_class WHERE relname = :statchunk;
-- verify that corresponding compressed chunk table stats is updated as well.
SELECT relpages, reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
-- Verify partial chunk stats are handled correctly when analyzing
-- for both uncompressed and compressed chunk tables
INSERT INTO stattest SELECT '2020/02/20 01:00'::TIMESTAMPTZ + ('1 hour'::interval * v), 250 * v FROM generate_series(25,50) v;
ANALYZE stattest;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
-- Hypertable will now see the histogram bounds since we have data in the uncompressed chunk table.
SELECT histogram_bounds FROM pg_stats WHERE tablename = 'stattest' AND attname = 'c1';
-- verify that corresponding uncompressed chunk table stats is updated as well.
SELECT relpages, reltuples FROM pg_class WHERE relname = :statchunk;
-- verify that corresponding compressed chunk table stats have not changed since
-- we didn't compress anything new.
SELECT relpages, reltuples FROM pg_class WHERE relname = :'STAT_COMP_CHUNK_NAME';
-- Verify that decompressing the chunk restores autoanalyze to the hypertable's setting
SELECT reloptions FROM pg_class WHERE relname = :statchunk;
SELECT decompress_chunk(c) FROM show_chunks('stattest') c;
@ -579,6 +584,15 @@ SELECT reloptions FROM pg_class WHERE relname = :statchunk;
ALTER TABLE stattest SET (autovacuum_enabled = false);
SELECT decompress_chunk(c) FROM show_chunks('stattest') c;
SELECT reloptions FROM pg_class WHERE relname = :statchunk;
-- Verify that even a global analyze works as well, changing message scope here
-- to hide WARNINGs for skipped tables
SET client_min_messages TO ERROR;
ANALYZE;
SET client_min_messages TO NOTICE;
SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk and attname = 'c1';
SELECT relpages, reltuples FROM pg_class WHERE relname = :statchunk;
DROP TABLE stattest;
--- Test that analyze on compression internal table updates stats on original chunks