(auto)vacuuming columnar tables leave pg_class.relfrozenxid as is and doesn't overwrite it with newer txid value. This eventually causes txid wraparound issue and needs to be resolved by setting a database to single user mode and running vacuum(full) on the problematic tables. Vacuum (full) recreates the table and resets pg_class.relfrozenxid value.
Vacuum (freeze) nor columnar.vacuum don't work in this case.
Heap tables always update pg_class.relfrozenxid when vacuum (freeze) is run.
Tested using docker ghcr.io/hydradatabase/hydra:latest
select extversion from pg_catalog.pg_extension where extname = 'columnar';
/*
extversion|
----------+
11.1-10 |
*/
Repro steps:
create table x () using columnar;
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x'
;
/*
relfrozenxid|
------------+
737 |
*/
select txid_current(); --increase txid counter
/*
txid_current|
------------+
738|
*/
vacuum(verbose, freeze) x;
/*
statistics for "x":
storage id: 10000000000
total file size: 16384, total data size: 0
compression rate: 1.00x
total row count: 0, stripe count: 0, average rows per stripe: 0
chunk count: 0, containing data for dropped columns: 0
"x": stopping vacuum due to zero column table
*/
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x'
;
/*
relfrozenxid|
------------+
737 |
*/
------------------------------------------------------------
create table y () using heap;
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'y'
;
/*
relfrozenxid|
------------+
740 |
*/
select txid_current(); --increase txid counter
/*
txid_current|
------------+
741|
*/
vacuum(verbose, freeze) y;
/*
aggressively vacuuming "public.y"
table "y": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
*/
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'y'
;
/*
relfrozenxid|
------------+
742 |
*/
------------------------------------------------------------
create table x1 (i int) using columnar;
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x1'
;
/*
relfrozenxid|
------------+
742 |
*/
select txid_current(); --increase txid counter
/*
txid_current|
------------+
743|
*/
vacuum(verbose, freeze) x1;
/*
statistics for "x1":
storage id: 10000000001
total file size: 16384, total data size: 0
compression rate: 1.00x
total row count: 0, stripe count: 0, average rows per stripe: 0
chunk count: 0, containing data for dropped columns: 0
"x1": stopping vacuum due to empty table
*/
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x1'
;
/*
relfrozenxid|
------------+
742 |
*/
------------------------------------------------------------
create table y1 (i int) using heap;
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'y1'
;
/*
relfrozenxid|
------------+
740 |
*/
select txid_current(); --increase txid counter
/*
relfrozenxid|
------------+
745 |
*/
vacuum(verbose, freeze) y1;
/*
aggressively vacuuming "public.y1"
table "y1": found 0 removable, 0 nonremovable row versions in 0 out of 0 pages
*/
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'y1'
;
/*
relfrozenxid|
------------+
746 |
*/
------------------------------------------------------------
create table x2 (i int) using columnar;
insert into x2(i) values(1);
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x2'
;
/*
relfrozenxid|
------------+
746 |
*/
select txid_current(); --increase txid counter
/*
txid_current|
------------+
748|
*/
vacuum(verbose, freeze) x2;
/*
statistics for "x2":
storage id: 10000000002
total file size: 24576, total data size: 14
compression rate: 0.36x
total row count: 1, stripe count: 1, average rows per stripe: 1
chunk count: 1, containing data for dropped columns: 0, zstd compressed: 1
*/
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x2'
;
/*
relfrozenxid|
------------+
746 |
*/
------------------------------------------------------------
------------------------------------------------------------
create table x3 (i int) using columnar;
insert into x3(i) values(1);
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x3'
;
/*
relfrozenxid|
------------+
750 |
*/
select txid_current(); --increase txid counter
/*
txid_current|
------------+
752|
*/
select columnar.vacuum('x3'::regclass);
/*
vacuum|
------+
0|
*/
SELECT c.relfrozenxid
FROM pg_class AS c
WHERE oid::regclass::text = 'x3'
;
/*
relfrozenxid|
------------+
750 |
*/
------------------------------------------------------------
What's wrong?
(auto)vacuuming columnar tables leave pg_class.relfrozenxid as is and doesn't overwrite it with newer txid value. This eventually causes txid wraparound issue and needs to be resolved by setting a database to single user mode and running vacuum(full) on the problematic tables. Vacuum (full) recreates the table and resets pg_class.relfrozenxid value. Vacuum (freeze) nor columnar.vacuum don't work in this case.
Heap tables always update pg_class.relfrozenxid when vacuum (freeze) is run.
Tested using docker ghcr.io/hydradatabase/hydra:latest
Repro steps: