From 2621f9a50a2d64e32db01e305c877ae7e5bbbe2c Mon Sep 17 00:00:00 2001 From: alterego655 <824662526@qq.com> Date: Wed, 27 May 2026 18:50:51 +0800 Subject: [PATCH v2] Fix safe_wal_size for slots without restart_lsn pg_replication_slots could report a non-NULL safe_wal_size for a replication slot that had never reserved WAL, when max_slot_wal_keep_size was finite. Such a slot has no restart_lsn, so WAL availability and safe_wal_size are undefined. Return NULL for safe_wal_size when WAL availability is invalid. --- doc/src/sgml/system-views.sgml | 10 ++++++---- src/backend/replication/slotfuncs.c | 9 ++++++--- src/test/recovery/t/019_replslot_limit.pl | 21 +++++++++++++++++++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml index 2ebec6928d5..39fa955ff89 100644 --- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml @@ -3007,10 +3007,12 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx safe_wal_size int8 - The number of bytes that can be written to WAL such that this slot - is not in danger of getting in state "lost". It is NULL for lost - slots, as well as if max_slot_wal_keep_size - is -1. + The number of bytes that can be written to WAL before this slot is + in danger of becoming lost. It is + NULL for lost slots, for slots whose + restart_lsn is NULL, + and when max_slot_wal_keep_size is + -1. diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c index 16fbd383735..44de95e6fa9 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -397,10 +397,13 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) } /* - * safe_wal_size is only computed for slots that have not been lost, - * and only if there's a configured maximum size. + * safe_wal_size is only computed for slots with a valid restart_lsn + * that have not been lost, and only if there's a configured maximum + * size. */ - if (walstate == WALAVAIL_REMOVED || max_slot_wal_keep_size_mb < 0) + if (walstate == WALAVAIL_INVALID_LSN || + walstate == WALAVAIL_REMOVED || + max_slot_wal_keep_size_mb < 0) nulls[i++] = true; else { diff --git a/src/test/recovery/t/019_replslot_limit.pl b/src/test/recovery/t/019_replslot_limit.pl index a412faf51c6..9d0f49ec694 100644 --- a/src/test/recovery/t/019_replslot_limit.pl +++ b/src/test/recovery/t/019_replslot_limit.pl @@ -22,16 +22,33 @@ max_wal_size = 4MB log_checkpoints = yes )); $node_primary->start; + +# A slot that has not reserved WAL has no meaningful WAL availability or +# remaining safe WAL size, even when max_slot_wal_keep_size is finite. +$node_primary->safe_psql('postgres', + "ALTER SYSTEM SET max_slot_wal_keep_size TO '6MB'; SELECT pg_reload_conf();" +); +$node_primary->safe_psql('postgres', + "SELECT pg_create_physical_replication_slot('rep_unreserved')"); +my $result = $node_primary->safe_psql('postgres', + "SELECT restart_lsn IS NULL, wal_status IS NULL, safe_wal_size IS NULL FROM pg_replication_slots WHERE slot_name = 'rep_unreserved'" +); +is($result, "t|t|t", + 'check non-reserved slot state with finite max_slot_wal_keep_size'); +$node_primary->safe_psql('postgres', + "SELECT pg_drop_replication_slot('rep_unreserved')"); +$node_primary->safe_psql('postgres', + "ALTER SYSTEM RESET max_slot_wal_keep_size; SELECT pg_reload_conf();"); + $node_primary->safe_psql('postgres', "SELECT pg_create_physical_replication_slot('rep1')"); # The slot state and remain should be null before the first connection -my $result = $node_primary->safe_psql('postgres', +$result = $node_primary->safe_psql('postgres', "SELECT restart_lsn IS NULL, wal_status is NULL, safe_wal_size is NULL FROM pg_replication_slots WHERE slot_name = 'rep1'" ); is($result, "t|t|t", 'check the state of non-reserved slot is "unknown"'); - # Take backup my $backup_name = 'my_backup'; $node_primary->backup($backup_name); -- 2.51.0