Created
December 16, 2025 06:32
-
-
Save cabecada/79a8d64c8c52164c510b66201304bfa0 to your computer and use it in GitHub Desktop.
triggering a wraparound by forcing some page corruption of table on disk, making autovacuum fail
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| this is just for fun. | |
| we corrupt some page of a table that is heavily bloated and autovaccum has not yet run. now the bloat is enough for autovacuum to see the threshold breach | |
| and trigger its run on the bloated table. it runs, then it finds the corrupted page and it fails silently. then it sleeps but it finds the | |
| same table again for threshold breach and runs it again only to fail again. this goes on forever, monitoring shows no new bloat, but the table | |
| does not have enough bloat to trouble other queries. (maybe a cold table). but then, txid starts growing and we now see | |
| a txid exhaustion limit approaching. it triggers forced autovaccum, but still it keeps growing. | |
| postgres@ubuntu:/mnt/VHD$ pg_ctl -D db1 -l logfile start | |
| waiting for server to start.... done | |
| server started | |
| postgres@ubuntu:/mnt/VHD$ psql | |
| psql (18.1 (Ubuntu 18.1-1.pgdg24.04+2)) | |
| Type "help" for help. | |
| postgres=# create table t(col1 int primary key, col2 int); | |
| CREATE TABLE | |
| postgres=# alter table t set (autovacuum_enabled = 0); | |
| ALTER TABLE | |
| postgres=# select pg_relation_filepath('t'); | |
| pg_relation_filepath | |
| ---------------------- | |
| base/5/16384 | |
| (1 row) | |
| postgres=# insert into t select x,x from generate_series(1, 10000) x; | |
| INSERT 0 10000 | |
| postgres=# do $$ | |
| begin | |
| for i in 1..20 loop | |
| update t set col2 = i::int; commit; | |
| end loop; | |
| end; $$; | |
| DO | |
| postgres=# select * from pg_stat_user_tables where relname = 't'; | |
| postgres=# analyze t; | |
| ANALYZE | |
| postgres=# \x | |
| Expanded display is on. | |
| postgres=# select * from pg_stat_user_tables where relname = 't'; | |
| -[ RECORD 1 ]----------+------------------------------ | |
| relid | 16384 | |
| schemaname | public | |
| relname | t | |
| seq_scan | 21 | |
| last_seq_scan | 2025-12-16 06:23:19.442824+00 | |
| seq_tup_read | 200000 | |
| idx_scan | 0 | |
| last_idx_scan | | |
| idx_tup_fetch | 0 | |
| n_tup_ins | 10000 | |
| n_tup_upd | 200000 | |
| n_tup_del | 0 | |
| n_tup_hot_upd | 1784 | |
| n_tup_newpage_upd | 198216 | |
| n_live_tup | 10000 | |
| n_dead_tup | 198522 | |
| n_mod_since_analyze | 0 | |
| n_ins_since_vacuum | 10000 | |
| last_vacuum | | |
| last_autovacuum | | |
| last_analyze | 2025-12-16 06:23:39.652082+00 | |
| last_autoanalyze | | |
| vacuum_count | 0 | |
| autovacuum_count | 0 | |
| analyze_count | 1 | |
| autoanalyze_count | 0 | |
| total_vacuum_time | 0 | |
| total_autovacuum_time | 0 | |
| total_analyze_time | 3 | |
| total_autoanalyze_time | 0 | |
| postgres=# create extension pageinspect; | |
| CREATE EXTENSION | |
| postgres=# \q | |
| postgres@ubuntu:/mnt/VHD$ ls -l db1/base/5/16384 | |
| -rw------- 1 postgres postgres 7553024 Dec 16 06:23 db1/base/5/16384 | |
| postgres@ubuntu:/mnt/VHD$ perl -le 'print 7553024/8192' | |
| 922 | |
| postgres@ubuntu:/mnt/VHD$ #dd if=/dev/urandom seek=000 bs=8k of=db1/base/5/16384 conv=notrunc,noerror count=1 | |
| postgres@ubuntu:/mnt/VHD$ psql | |
| psql (18.1 (Ubuntu 18.1-1.pgdg24.04+2)) | |
| Type "help" for help. | |
| postgres=# alter system set log_autovacuum_min_duration TO 0; | |
| ALTER SYSTEM | |
| postgres=# select pg_reload_conf(); | |
| pg_reload_conf | |
| ---------------- | |
| t | |
| (1 row) | |
| postgres=# \q | |
| postgres@ubuntu:/mnt/VHD$ pg_ctl -D db1 -l logfile stop | |
| waiting for server to shut down.... done | |
| server stopped | |
| postgres@ubuntu:/mnt/VHD$ dd if=/dev/urandom seek=900 bs=8k of=db1/base/5/16384 conv=notrunc,noerror count=1 | |
| 1+0 records in | |
| 1+0 records out | |
| 8192 bytes (8.2 kB, 8.0 KiB) copied, 8.6619e-05 s, 94.6 MB/s | |
| postgres@ubuntu:/mnt/VHD$ pg_ctl -D db1 -l logfile start | |
| waiting for server to start.... done | |
| server started | |
| postgres@ubuntu:/mnt/VHD$ psql | |
| psql (18.1 (Ubuntu 18.1-1.pgdg24.04+2)) | |
| Type "help" for help. | |
| postgres=# alter table t set (autovacuum_enabled = 1); | |
| ALTER TABLE | |
| postgres=# alter table t set (autovacuum_ | |
| autovacuum_analyze_scale_factor autovacuum_freeze_min_age autovacuum_multixact_freeze_table_age autovacuum_vacuum_insert_threshold | |
| autovacuum_analyze_threshold autovacuum_freeze_table_age autovacuum_vacuum_cost_delay autovacuum_vacuum_max_threshold | |
| autovacuum_enabled autovacuum_multixact_freeze_max_age autovacuum_vacuum_cost_limit autovacuum_vacuum_scale_factor | |
| autovacuum_freeze_max_age autovacuum_multixact_freeze_min_age autovacuum_vacuum_insert_scale_factor autovacuum_vacuum_threshold | |
| postgres=# alter table t set (autovacuum_vacuum_threshold = 1); | |
| ALTER TABLE | |
| postgres=# alter table t set (autovacuum_vacuum_scale_factor = 0); | |
| ALTER TABLE | |
| postgres=# \x | |
| Expanded display is on. | |
| postgres=# select * from pg_stat_user_tables where relname = 't'; | |
| -[ RECORD 1 ]----------+------------------------------ | |
| relid | 16384 | |
| schemaname | public | |
| relname | t | |
| seq_scan | 21 | |
| last_seq_scan | 2025-12-16 06:23:19.442824+00 | |
| seq_tup_read | 200000 | |
| idx_scan | 0 | |
| last_idx_scan | | |
| idx_tup_fetch | 0 | |
| n_tup_ins | 10000 | |
| n_tup_upd | 200000 | |
| n_tup_del | 0 | |
| n_tup_hot_upd | 1784 | |
| n_tup_newpage_upd | 198216 | |
| n_live_tup | 10000 | |
| n_dead_tup | 198522 | |
| n_mod_since_analyze | 0 | |
| n_ins_since_vacuum | 10000 | |
| last_vacuum | | |
| last_autovacuum | | |
| last_analyze | 2025-12-16 06:23:39.652082+00 | |
| last_autoanalyze | | |
| vacuum_count | 0 | |
| autovacuum_count | 0 | |
| analyze_count | 1 | |
| autoanalyze_count | 0 | |
| total_vacuum_time | 0 | |
| total_autovacuum_time | 0 | |
| total_analyze_time | 3 | |
| total_autoanalyze_time | 0 | |
| postgres=# vacuum (verbose) t; | |
| INFO: vacuuming "postgres.public.t" | |
| ERROR: invalid page in block 900 of relation "base/5/16384" | |
| CONTEXT: while scanning block 899 of relation "public.t" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment