mirror of
https://github.com/postgres/postgres.git
synced 2025-05-21 15:54:08 +03:00
Track invalidation_reason in pg_replication_slots.
Till now, the reason for replication slot invalidation is not tracked directly in pg_replication_slots. A recent commit 007693f2a3 added 'conflict_reason' to show the reasons for slot conflict/invalidation, but only for logical slots. This commit adds a new column 'invalidation_reason' to show invalidation reasons for both physical and logical slots. And, this commit also turns 'conflict_reason' text column to 'conflicting' boolean column (effectively reverting commit 007693f2a3). The 'conflicting' column is true for invalidation reasons 'rows_removed' and 'wal_level_insufficient' because those make the slot conflict with recovery. When 'conflicting' is true, one can now look at the new 'invalidation_reason' column for the reason for the logical slot's conflict with recovery. The new 'invalidation_reason' column will also be useful to track other invalidation reasons in the future commit. Author: Bharath Rupireddy Reviewed-by: Bertrand Drouvot, Amit Kapila, Shveta Malik Discussion: https://www.postgresql.org/message-id/ZfR7HuzFEswakt/a%40ip-10-97-1-34.eu-west-3.compute.internal Discussion: https://www.postgresql.org/message-id/CALj2ACW4aUe-_uFQOjdWCEN-xXoLGhmvRFnL8SNw_TZ5nJe+aw@mail.gmail.com
This commit is contained in:
parent
b4080fa3dc
commit
6ae701b437
@ -453,8 +453,8 @@ make prefix=/usr/local/pgsql.new install
|
|||||||
<para>
|
<para>
|
||||||
All slots on the old cluster must be usable, i.e., there are no slots
|
All slots on the old cluster must be usable, i.e., there are no slots
|
||||||
whose
|
whose
|
||||||
<link linkend="view-pg-replication-slots">pg_replication_slots</link>.<structfield>conflict_reason</structfield>
|
<link linkend="view-pg-replication-slots">pg_replication_slots</link>.<structfield>conflicting</structfield>
|
||||||
is not <literal>NULL</literal>.
|
is not <literal>true</literal>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -2525,13 +2525,24 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
|
|||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
<structfield>conflict_reason</structfield> <type>text</type>
|
<structfield>conflicting</structfield> <type>bool</type>
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
The reason for the logical slot's conflict with recovery. It is always
|
True if this logical slot conflicted with recovery (and so is now
|
||||||
NULL for physical slots, as well as for logical slots which are not
|
invalidated). When this column is true, check
|
||||||
invalidated. The non-NULL values indicate that the slot is marked
|
<structfield>invalidation_reason</structfield> column for the conflict
|
||||||
as invalidated. Possible values are:
|
reason. Always NULL for physical slots.
|
||||||
|
</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
|
<structfield>invalidation_reason</structfield> <type>text</type>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The reason for the slot's invalidation. It is set for both logical and
|
||||||
|
physical slots. <literal>NULL</literal> if the slot is not invalidated.
|
||||||
|
Possible values are:
|
||||||
<itemizedlist spacing="compact">
|
<itemizedlist spacing="compact">
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
@ -2542,14 +2553,14 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<literal>rows_removed</literal> means that the required rows have
|
<literal>rows_removed</literal> means that the required rows have
|
||||||
been removed.
|
been removed. It is set only for logical slots.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<literal>wal_level_insufficient</literal> means that the
|
<literal>wal_level_insufficient</literal> means that the
|
||||||
primary doesn't have a <xref linkend="guc-wal-level"/> sufficient to
|
primary doesn't have a <xref linkend="guc-wal-level"/> sufficient to
|
||||||
perform logical decoding.
|
perform logical decoding. It is set only for logical slots.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
@ -1023,7 +1023,8 @@ CREATE VIEW pg_replication_slots AS
|
|||||||
L.wal_status,
|
L.wal_status,
|
||||||
L.safe_wal_size,
|
L.safe_wal_size,
|
||||||
L.two_phase,
|
L.two_phase,
|
||||||
L.conflict_reason,
|
L.conflicting,
|
||||||
|
L.invalidation_reason,
|
||||||
L.failover,
|
L.failover,
|
||||||
L.synced
|
L.synced
|
||||||
FROM pg_get_replication_slots() AS L
|
FROM pg_get_replication_slots() AS L
|
||||||
|
@ -663,7 +663,7 @@ synchronize_slots(WalReceiverConn *wrconn)
|
|||||||
bool started_tx = false;
|
bool started_tx = false;
|
||||||
const char *query = "SELECT slot_name, plugin, confirmed_flush_lsn,"
|
const char *query = "SELECT slot_name, plugin, confirmed_flush_lsn,"
|
||||||
" restart_lsn, catalog_xmin, two_phase, failover,"
|
" restart_lsn, catalog_xmin, two_phase, failover,"
|
||||||
" database, conflict_reason"
|
" database, invalidation_reason"
|
||||||
" FROM pg_catalog.pg_replication_slots"
|
" FROM pg_catalog.pg_replication_slots"
|
||||||
" WHERE failover and NOT temporary";
|
" WHERE failover and NOT temporary";
|
||||||
|
|
||||||
|
@ -1525,14 +1525,14 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
XLogRecPtr initial_effective_xmin = InvalidXLogRecPtr;
|
XLogRecPtr initial_effective_xmin = InvalidXLogRecPtr;
|
||||||
XLogRecPtr initial_catalog_effective_xmin = InvalidXLogRecPtr;
|
XLogRecPtr initial_catalog_effective_xmin = InvalidXLogRecPtr;
|
||||||
XLogRecPtr initial_restart_lsn = InvalidXLogRecPtr;
|
XLogRecPtr initial_restart_lsn = InvalidXLogRecPtr;
|
||||||
ReplicationSlotInvalidationCause conflict_prev PG_USED_FOR_ASSERTS_ONLY = RS_INVAL_NONE;
|
ReplicationSlotInvalidationCause invalidation_cause_prev PG_USED_FOR_ASSERTS_ONLY = RS_INVAL_NONE;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
XLogRecPtr restart_lsn;
|
XLogRecPtr restart_lsn;
|
||||||
NameData slotname;
|
NameData slotname;
|
||||||
int active_pid = 0;
|
int active_pid = 0;
|
||||||
ReplicationSlotInvalidationCause conflict = RS_INVAL_NONE;
|
ReplicationSlotInvalidationCause invalidation_cause = RS_INVAL_NONE;
|
||||||
|
|
||||||
Assert(LWLockHeldByMeInMode(ReplicationSlotControlLock, LW_SHARED));
|
Assert(LWLockHeldByMeInMode(ReplicationSlotControlLock, LW_SHARED));
|
||||||
|
|
||||||
@ -1554,17 +1554,14 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
|
|
||||||
restart_lsn = s->data.restart_lsn;
|
restart_lsn = s->data.restart_lsn;
|
||||||
|
|
||||||
/*
|
/* we do nothing if the slot is already invalid */
|
||||||
* If the slot is already invalid or is a non conflicting slot, we
|
|
||||||
* don't need to do anything.
|
|
||||||
*/
|
|
||||||
if (s->data.invalidated == RS_INVAL_NONE)
|
if (s->data.invalidated == RS_INVAL_NONE)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The slot's mutex will be released soon, and it is possible that
|
* The slot's mutex will be released soon, and it is possible that
|
||||||
* those values change since the process holding the slot has been
|
* those values change since the process holding the slot has been
|
||||||
* terminated (if any), so record them here to ensure that we
|
* terminated (if any), so record them here to ensure that we
|
||||||
* would report the correct conflict cause.
|
* would report the correct invalidation cause.
|
||||||
*/
|
*/
|
||||||
if (!terminated)
|
if (!terminated)
|
||||||
{
|
{
|
||||||
@ -1578,7 +1575,7 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
case RS_INVAL_WAL_REMOVED:
|
case RS_INVAL_WAL_REMOVED:
|
||||||
if (initial_restart_lsn != InvalidXLogRecPtr &&
|
if (initial_restart_lsn != InvalidXLogRecPtr &&
|
||||||
initial_restart_lsn < oldestLSN)
|
initial_restart_lsn < oldestLSN)
|
||||||
conflict = cause;
|
invalidation_cause = cause;
|
||||||
break;
|
break;
|
||||||
case RS_INVAL_HORIZON:
|
case RS_INVAL_HORIZON:
|
||||||
if (!SlotIsLogical(s))
|
if (!SlotIsLogical(s))
|
||||||
@ -1589,15 +1586,15 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
if (TransactionIdIsValid(initial_effective_xmin) &&
|
if (TransactionIdIsValid(initial_effective_xmin) &&
|
||||||
TransactionIdPrecedesOrEquals(initial_effective_xmin,
|
TransactionIdPrecedesOrEquals(initial_effective_xmin,
|
||||||
snapshotConflictHorizon))
|
snapshotConflictHorizon))
|
||||||
conflict = cause;
|
invalidation_cause = cause;
|
||||||
else if (TransactionIdIsValid(initial_catalog_effective_xmin) &&
|
else if (TransactionIdIsValid(initial_catalog_effective_xmin) &&
|
||||||
TransactionIdPrecedesOrEquals(initial_catalog_effective_xmin,
|
TransactionIdPrecedesOrEquals(initial_catalog_effective_xmin,
|
||||||
snapshotConflictHorizon))
|
snapshotConflictHorizon))
|
||||||
conflict = cause;
|
invalidation_cause = cause;
|
||||||
break;
|
break;
|
||||||
case RS_INVAL_WAL_LEVEL:
|
case RS_INVAL_WAL_LEVEL:
|
||||||
if (SlotIsLogical(s))
|
if (SlotIsLogical(s))
|
||||||
conflict = cause;
|
invalidation_cause = cause;
|
||||||
break;
|
break;
|
||||||
case RS_INVAL_NONE:
|
case RS_INVAL_NONE:
|
||||||
pg_unreachable();
|
pg_unreachable();
|
||||||
@ -1605,14 +1602,14 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The conflict cause recorded previously should not change while the
|
* The invalidation cause recorded previously should not change while
|
||||||
* process owning the slot (if any) has been terminated.
|
* the process owning the slot (if any) has been terminated.
|
||||||
*/
|
*/
|
||||||
Assert(!(conflict_prev != RS_INVAL_NONE && terminated &&
|
Assert(!(invalidation_cause_prev != RS_INVAL_NONE && terminated &&
|
||||||
conflict_prev != conflict));
|
invalidation_cause_prev != invalidation_cause));
|
||||||
|
|
||||||
/* if there's no conflict, we're done */
|
/* if there's no invalidation, we're done */
|
||||||
if (conflict == RS_INVAL_NONE)
|
if (invalidation_cause == RS_INVAL_NONE)
|
||||||
{
|
{
|
||||||
SpinLockRelease(&s->mutex);
|
SpinLockRelease(&s->mutex);
|
||||||
if (released_lock)
|
if (released_lock)
|
||||||
@ -1632,13 +1629,13 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
{
|
{
|
||||||
MyReplicationSlot = s;
|
MyReplicationSlot = s;
|
||||||
s->active_pid = MyProcPid;
|
s->active_pid = MyProcPid;
|
||||||
s->data.invalidated = conflict;
|
s->data.invalidated = invalidation_cause;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: We should consider not overwriting restart_lsn and instead
|
* XXX: We should consider not overwriting restart_lsn and instead
|
||||||
* just rely on .invalidated.
|
* just rely on .invalidated.
|
||||||
*/
|
*/
|
||||||
if (conflict == RS_INVAL_WAL_REMOVED)
|
if (invalidation_cause == RS_INVAL_WAL_REMOVED)
|
||||||
s->data.restart_lsn = InvalidXLogRecPtr;
|
s->data.restart_lsn = InvalidXLogRecPtr;
|
||||||
|
|
||||||
/* Let caller know */
|
/* Let caller know */
|
||||||
@ -1681,7 +1678,7 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
*/
|
*/
|
||||||
if (last_signaled_pid != active_pid)
|
if (last_signaled_pid != active_pid)
|
||||||
{
|
{
|
||||||
ReportSlotInvalidation(conflict, true, active_pid,
|
ReportSlotInvalidation(invalidation_cause, true, active_pid,
|
||||||
slotname, restart_lsn,
|
slotname, restart_lsn,
|
||||||
oldestLSN, snapshotConflictHorizon);
|
oldestLSN, snapshotConflictHorizon);
|
||||||
|
|
||||||
@ -1694,7 +1691,7 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
|
|
||||||
last_signaled_pid = active_pid;
|
last_signaled_pid = active_pid;
|
||||||
terminated = true;
|
terminated = true;
|
||||||
conflict_prev = conflict;
|
invalidation_cause_prev = invalidation_cause;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait until the slot is released. */
|
/* Wait until the slot is released. */
|
||||||
@ -1727,7 +1724,7 @@ InvalidatePossiblyObsoleteSlot(ReplicationSlotInvalidationCause cause,
|
|||||||
ReplicationSlotSave();
|
ReplicationSlotSave();
|
||||||
ReplicationSlotRelease();
|
ReplicationSlotRelease();
|
||||||
|
|
||||||
ReportSlotInvalidation(conflict, false, active_pid,
|
ReportSlotInvalidation(invalidation_cause, false, active_pid,
|
||||||
slotname, restart_lsn,
|
slotname, restart_lsn,
|
||||||
oldestLSN, snapshotConflictHorizon);
|
oldestLSN, snapshotConflictHorizon);
|
||||||
|
|
||||||
@ -2356,21 +2353,21 @@ RestoreSlotFromDisk(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maps a conflict reason for a replication slot to
|
* Maps an invalidation reason for a replication slot to
|
||||||
* ReplicationSlotInvalidationCause.
|
* ReplicationSlotInvalidationCause.
|
||||||
*/
|
*/
|
||||||
ReplicationSlotInvalidationCause
|
ReplicationSlotInvalidationCause
|
||||||
GetSlotInvalidationCause(const char *conflict_reason)
|
GetSlotInvalidationCause(const char *invalidation_reason)
|
||||||
{
|
{
|
||||||
ReplicationSlotInvalidationCause cause;
|
ReplicationSlotInvalidationCause cause;
|
||||||
ReplicationSlotInvalidationCause result = RS_INVAL_NONE;
|
ReplicationSlotInvalidationCause result = RS_INVAL_NONE;
|
||||||
bool found PG_USED_FOR_ASSERTS_ONLY = false;
|
bool found PG_USED_FOR_ASSERTS_ONLY = false;
|
||||||
|
|
||||||
Assert(conflict_reason);
|
Assert(invalidation_reason);
|
||||||
|
|
||||||
for (cause = RS_INVAL_NONE; cause <= RS_INVAL_MAX_CAUSES; cause++)
|
for (cause = RS_INVAL_NONE; cause <= RS_INVAL_MAX_CAUSES; cause++)
|
||||||
{
|
{
|
||||||
if (strcmp(SlotInvalidationCauses[cause], conflict_reason) == 0)
|
if (strcmp(SlotInvalidationCauses[cause], invalidation_reason) == 0)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
result = cause;
|
result = cause;
|
||||||
|
@ -239,7 +239,7 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS)
|
|||||||
Datum
|
Datum
|
||||||
pg_get_replication_slots(PG_FUNCTION_ARGS)
|
pg_get_replication_slots(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
#define PG_GET_REPLICATION_SLOTS_COLS 17
|
#define PG_GET_REPLICATION_SLOTS_COLS 18
|
||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
XLogRecPtr currlsn;
|
XLogRecPtr currlsn;
|
||||||
int slotno;
|
int slotno;
|
||||||
@ -263,6 +263,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
|
|||||||
bool nulls[PG_GET_REPLICATION_SLOTS_COLS];
|
bool nulls[PG_GET_REPLICATION_SLOTS_COLS];
|
||||||
WALAvailability walstate;
|
WALAvailability walstate;
|
||||||
int i;
|
int i;
|
||||||
|
ReplicationSlotInvalidationCause cause;
|
||||||
|
|
||||||
if (!slot->in_use)
|
if (!slot->in_use)
|
||||||
continue;
|
continue;
|
||||||
@ -409,17 +410,27 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
values[i++] = BoolGetDatum(slot_contents.data.two_phase);
|
values[i++] = BoolGetDatum(slot_contents.data.two_phase);
|
||||||
|
|
||||||
if (slot_contents.data.database == InvalidOid)
|
cause = slot_contents.data.invalidated;
|
||||||
|
|
||||||
|
if (SlotIsPhysical(&slot_contents))
|
||||||
nulls[i++] = true;
|
nulls[i++] = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReplicationSlotInvalidationCause cause = slot_contents.data.invalidated;
|
/*
|
||||||
|
* rows_removed and wal_level_insufficient are the only two
|
||||||
|
* reasons for the logical slot's conflict with recovery.
|
||||||
|
*/
|
||||||
|
if (cause == RS_INVAL_HORIZON ||
|
||||||
|
cause == RS_INVAL_WAL_LEVEL)
|
||||||
|
values[i++] = BoolGetDatum(true);
|
||||||
|
else
|
||||||
|
values[i++] = BoolGetDatum(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (cause == RS_INVAL_NONE)
|
if (cause == RS_INVAL_NONE)
|
||||||
nulls[i++] = true;
|
nulls[i++] = true;
|
||||||
else
|
else
|
||||||
values[i++] = CStringGetTextDatum(SlotInvalidationCauses[cause]);
|
values[i++] = CStringGetTextDatum(SlotInvalidationCauses[cause]);
|
||||||
}
|
|
||||||
|
|
||||||
values[i++] = BoolGetDatum(slot_contents.data.failover);
|
values[i++] = BoolGetDatum(slot_contents.data.failover);
|
||||||
|
|
||||||
|
@ -676,13 +676,13 @@ get_old_cluster_logical_slot_infos(DbInfo *dbinfo, bool live_check)
|
|||||||
* removed.
|
* removed.
|
||||||
*/
|
*/
|
||||||
res = executeQueryOrDie(conn, "SELECT slot_name, plugin, two_phase, failover, "
|
res = executeQueryOrDie(conn, "SELECT slot_name, plugin, two_phase, failover, "
|
||||||
"%s as caught_up, conflict_reason IS NOT NULL as invalid "
|
"%s as caught_up, invalidation_reason IS NOT NULL as invalid "
|
||||||
"FROM pg_catalog.pg_replication_slots "
|
"FROM pg_catalog.pg_replication_slots "
|
||||||
"WHERE slot_type = 'logical' AND "
|
"WHERE slot_type = 'logical' AND "
|
||||||
"database = current_database() AND "
|
"database = current_database() AND "
|
||||||
"temporary IS FALSE;",
|
"temporary IS FALSE;",
|
||||||
live_check ? "FALSE" :
|
live_check ? "FALSE" :
|
||||||
"(CASE WHEN conflict_reason IS NOT NULL THEN FALSE "
|
"(CASE WHEN invalidation_reason IS NOT NULL THEN FALSE "
|
||||||
"ELSE (SELECT pg_catalog.binary_upgrade_logical_slot_has_caught_up(slot_name)) "
|
"ELSE (SELECT pg_catalog.binary_upgrade_logical_slot_has_caught_up(slot_name)) "
|
||||||
"END)");
|
"END)");
|
||||||
|
|
||||||
|
@ -57,6 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202403221
|
#define CATALOG_VERSION_NO 202403222
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -11133,9 +11133,9 @@
|
|||||||
proname => 'pg_get_replication_slots', prorows => '10', proisstrict => 'f',
|
proname => 'pg_get_replication_slots', prorows => '10', proisstrict => 'f',
|
||||||
proretset => 't', provolatile => 's', prorettype => 'record',
|
proretset => 't', provolatile => 's', prorettype => 'record',
|
||||||
proargtypes => '',
|
proargtypes => '',
|
||||||
proallargtypes => '{name,name,text,oid,bool,bool,int4,xid,xid,pg_lsn,pg_lsn,text,int8,bool,text,bool,bool}',
|
proallargtypes => '{name,name,text,oid,bool,bool,int4,xid,xid,pg_lsn,pg_lsn,text,int8,bool,bool,text,bool,bool}',
|
||||||
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
|
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
|
||||||
proargnames => '{slot_name,plugin,slot_type,datoid,temporary,active,active_pid,xmin,catalog_xmin,restart_lsn,confirmed_flush_lsn,wal_status,safe_wal_size,two_phase,conflict_reason,failover,synced}',
|
proargnames => '{slot_name,plugin,slot_type,datoid,temporary,active,active_pid,xmin,catalog_xmin,restart_lsn,confirmed_flush_lsn,wal_status,safe_wal_size,two_phase,conflicting,invalidation_reason,failover,synced}',
|
||||||
prosrc => 'pg_get_replication_slots' },
|
prosrc => 'pg_get_replication_slots' },
|
||||||
{ oid => '3786', descr => 'set up a logical replication slot',
|
{ oid => '3786', descr => 'set up a logical replication slot',
|
||||||
proname => 'pg_create_logical_replication_slot', provolatile => 'v',
|
proname => 'pg_create_logical_replication_slot', provolatile => 'v',
|
||||||
|
@ -273,7 +273,7 @@ extern void CheckPointReplicationSlots(bool is_shutdown);
|
|||||||
extern void CheckSlotRequirements(void);
|
extern void CheckSlotRequirements(void);
|
||||||
extern void CheckSlotPermissions(void);
|
extern void CheckSlotPermissions(void);
|
||||||
extern ReplicationSlotInvalidationCause
|
extern ReplicationSlotInvalidationCause
|
||||||
GetSlotInvalidationCause(const char *conflict_reason);
|
GetSlotInvalidationCause(const char *invalidation_reason);
|
||||||
|
|
||||||
extern bool SlotExistsInStandbySlotNames(const char *slot_name);
|
extern bool SlotExistsInStandbySlotNames(const char *slot_name);
|
||||||
extern bool StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel);
|
extern bool StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel);
|
||||||
|
@ -168,7 +168,7 @@ sub change_hot_standby_feedback_and_wait_for_xmins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check conflict_reason in pg_replication_slots.
|
# Check reason for conflict in pg_replication_slots.
|
||||||
sub check_slots_conflict_reason
|
sub check_slots_conflict_reason
|
||||||
{
|
{
|
||||||
my ($slot_prefix, $reason) = @_;
|
my ($slot_prefix, $reason) = @_;
|
||||||
@ -178,15 +178,15 @@ sub check_slots_conflict_reason
|
|||||||
|
|
||||||
$res = $node_standby->safe_psql(
|
$res = $node_standby->safe_psql(
|
||||||
'postgres', qq(
|
'postgres', qq(
|
||||||
select conflict_reason from pg_replication_slots where slot_name = '$active_slot';));
|
select invalidation_reason from pg_replication_slots where slot_name = '$active_slot' and conflicting;));
|
||||||
|
|
||||||
is($res, "$reason", "$active_slot conflict_reason is $reason");
|
is($res, "$reason", "$active_slot reason for conflict is $reason");
|
||||||
|
|
||||||
$res = $node_standby->safe_psql(
|
$res = $node_standby->safe_psql(
|
||||||
'postgres', qq(
|
'postgres', qq(
|
||||||
select conflict_reason from pg_replication_slots where slot_name = '$inactive_slot';));
|
select invalidation_reason from pg_replication_slots where slot_name = '$inactive_slot' and conflicting;));
|
||||||
|
|
||||||
is($res, "$reason", "$inactive_slot conflict_reason is $reason");
|
is($res, "$reason", "$inactive_slot reason for conflict is $reason");
|
||||||
}
|
}
|
||||||
|
|
||||||
# Drop the slots, re-create them, change hot_standby_feedback,
|
# Drop the slots, re-create them, change hot_standby_feedback,
|
||||||
@ -293,13 +293,13 @@ $node_primary->safe_psql('testdb',
|
|||||||
qq[SELECT * FROM pg_create_physical_replication_slot('$primary_slotname');]
|
qq[SELECT * FROM pg_create_physical_replication_slot('$primary_slotname');]
|
||||||
);
|
);
|
||||||
|
|
||||||
# Check conflict_reason is NULL for physical slot
|
# Check conflicting is NULL for physical slot
|
||||||
$res = $node_primary->safe_psql(
|
$res = $node_primary->safe_psql(
|
||||||
'postgres', qq[
|
'postgres', qq[
|
||||||
SELECT conflict_reason is null FROM pg_replication_slots where slot_name = '$primary_slotname';]
|
SELECT conflicting is null FROM pg_replication_slots where slot_name = '$primary_slotname';]
|
||||||
);
|
);
|
||||||
|
|
||||||
is($res, 't', "Physical slot reports conflict_reason as NULL");
|
is($res, 't', "Physical slot reports conflicting as NULL");
|
||||||
|
|
||||||
my $backup_name = 'b1';
|
my $backup_name = 'b1';
|
||||||
$node_primary->backup($backup_name);
|
$node_primary->backup($backup_name);
|
||||||
@ -524,7 +524,7 @@ $node_primary->wait_for_replay_catchup($node_standby);
|
|||||||
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
||||||
check_for_invalidation('vacuum_full_', 1, 'with vacuum FULL on pg_class');
|
check_for_invalidation('vacuum_full_', 1, 'with vacuum FULL on pg_class');
|
||||||
|
|
||||||
# Verify conflict_reason is 'rows_removed' in pg_replication_slots
|
# Verify reason for conflict is 'rows_removed' in pg_replication_slots
|
||||||
check_slots_conflict_reason('vacuum_full_', 'rows_removed');
|
check_slots_conflict_reason('vacuum_full_', 'rows_removed');
|
||||||
|
|
||||||
# Ensure that replication slot stats are not removed after invalidation.
|
# Ensure that replication slot stats are not removed after invalidation.
|
||||||
@ -551,7 +551,7 @@ change_hot_standby_feedback_and_wait_for_xmins(1, 1);
|
|||||||
##################################################
|
##################################################
|
||||||
$node_standby->restart;
|
$node_standby->restart;
|
||||||
|
|
||||||
# Verify conflict_reason is retained across a restart.
|
# Verify reason for conflict is retained across a restart.
|
||||||
check_slots_conflict_reason('vacuum_full_', 'rows_removed');
|
check_slots_conflict_reason('vacuum_full_', 'rows_removed');
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
@ -560,7 +560,8 @@ check_slots_conflict_reason('vacuum_full_', 'rows_removed');
|
|||||||
|
|
||||||
# Get the restart_lsn from an invalidated slot
|
# Get the restart_lsn from an invalidated slot
|
||||||
my $restart_lsn = $node_standby->safe_psql('postgres',
|
my $restart_lsn = $node_standby->safe_psql('postgres',
|
||||||
"SELECT restart_lsn from pg_replication_slots WHERE slot_name = 'vacuum_full_activeslot' and conflict_reason is not null;"
|
"SELECT restart_lsn FROM pg_replication_slots
|
||||||
|
WHERE slot_name = 'vacuum_full_activeslot' AND conflicting;"
|
||||||
);
|
);
|
||||||
|
|
||||||
chomp($restart_lsn);
|
chomp($restart_lsn);
|
||||||
@ -611,7 +612,7 @@ $node_primary->wait_for_replay_catchup($node_standby);
|
|||||||
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
||||||
check_for_invalidation('row_removal_', $logstart, 'with vacuum on pg_class');
|
check_for_invalidation('row_removal_', $logstart, 'with vacuum on pg_class');
|
||||||
|
|
||||||
# Verify conflict_reason is 'rows_removed' in pg_replication_slots
|
# Verify reason for conflict is 'rows_removed' in pg_replication_slots
|
||||||
check_slots_conflict_reason('row_removal_', 'rows_removed');
|
check_slots_conflict_reason('row_removal_', 'rows_removed');
|
||||||
|
|
||||||
$handle =
|
$handle =
|
||||||
@ -647,7 +648,7 @@ $node_primary->wait_for_replay_catchup($node_standby);
|
|||||||
check_for_invalidation('shared_row_removal_', $logstart,
|
check_for_invalidation('shared_row_removal_', $logstart,
|
||||||
'with vacuum on pg_authid');
|
'with vacuum on pg_authid');
|
||||||
|
|
||||||
# Verify conflict_reason is 'rows_removed' in pg_replication_slots
|
# Verify reason for conflict is 'rows_removed' in pg_replication_slots
|
||||||
check_slots_conflict_reason('shared_row_removal_', 'rows_removed');
|
check_slots_conflict_reason('shared_row_removal_', 'rows_removed');
|
||||||
|
|
||||||
$handle = make_slot_active($node_standby, 'shared_row_removal_', 0, \$stdout,
|
$handle = make_slot_active($node_standby, 'shared_row_removal_', 0, \$stdout,
|
||||||
@ -700,8 +701,8 @@ ok( $node_standby->poll_query_until(
|
|||||||
is( $node_standby->safe_psql(
|
is( $node_standby->safe_psql(
|
||||||
'postgres',
|
'postgres',
|
||||||
q[select bool_or(conflicting) from
|
q[select bool_or(conflicting) from
|
||||||
(select conflict_reason is not NULL as conflicting
|
(select conflicting from pg_replication_slots
|
||||||
from pg_replication_slots WHERE slot_type = 'logical')]),
|
where slot_type = 'logical')]),
|
||||||
'f',
|
'f',
|
||||||
'Logical slots are reported as non conflicting');
|
'Logical slots are reported as non conflicting');
|
||||||
|
|
||||||
@ -739,7 +740,7 @@ $node_primary->wait_for_replay_catchup($node_standby);
|
|||||||
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
||||||
check_for_invalidation('pruning_', $logstart, 'with on-access pruning');
|
check_for_invalidation('pruning_', $logstart, 'with on-access pruning');
|
||||||
|
|
||||||
# Verify conflict_reason is 'rows_removed' in pg_replication_slots
|
# Verify reason for conflict is 'rows_removed' in pg_replication_slots
|
||||||
check_slots_conflict_reason('pruning_', 'rows_removed');
|
check_slots_conflict_reason('pruning_', 'rows_removed');
|
||||||
|
|
||||||
$handle = make_slot_active($node_standby, 'pruning_', 0, \$stdout, \$stderr);
|
$handle = make_slot_active($node_standby, 'pruning_', 0, \$stdout, \$stderr);
|
||||||
@ -783,7 +784,7 @@ $node_primary->wait_for_replay_catchup($node_standby);
|
|||||||
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
# Check invalidation in the logfile and in pg_stat_database_conflicts
|
||||||
check_for_invalidation('wal_level_', $logstart, 'due to wal_level');
|
check_for_invalidation('wal_level_', $logstart, 'due to wal_level');
|
||||||
|
|
||||||
# Verify conflict_reason is 'wal_level_insufficient' in pg_replication_slots
|
# Verify reason for conflict is 'wal_level_insufficient' in pg_replication_slots
|
||||||
check_slots_conflict_reason('wal_level_', 'wal_level_insufficient');
|
check_slots_conflict_reason('wal_level_', 'wal_level_insufficient');
|
||||||
|
|
||||||
$handle =
|
$handle =
|
||||||
|
@ -228,7 +228,7 @@ $standby1->safe_psql('postgres', "CHECKPOINT");
|
|||||||
# Check if the synced slot is invalidated
|
# Check if the synced slot is invalidated
|
||||||
is( $standby1->safe_psql(
|
is( $standby1->safe_psql(
|
||||||
'postgres',
|
'postgres',
|
||||||
q{SELECT conflict_reason = 'wal_removed' FROM pg_replication_slots WHERE slot_name = 'lsub1_slot';}
|
q{SELECT invalidation_reason = 'wal_removed' FROM pg_replication_slots WHERE slot_name = 'lsub1_slot';}
|
||||||
),
|
),
|
||||||
"t",
|
"t",
|
||||||
'synchronized slot has been invalidated');
|
'synchronized slot has been invalidated');
|
||||||
@ -274,7 +274,7 @@ $standby1->wait_for_log(qr/dropped replication slot "lsub1_slot" of dbid [0-9]+/
|
|||||||
# flagged as 'synced'
|
# flagged as 'synced'
|
||||||
is( $standby1->safe_psql(
|
is( $standby1->safe_psql(
|
||||||
'postgres',
|
'postgres',
|
||||||
q{SELECT conflict_reason IS NULL AND synced AND NOT temporary FROM pg_replication_slots WHERE slot_name = 'lsub1_slot';}
|
q{SELECT invalidation_reason IS NULL AND synced AND NOT temporary FROM pg_replication_slots WHERE slot_name = 'lsub1_slot';}
|
||||||
),
|
),
|
||||||
"t",
|
"t",
|
||||||
'logical slot is re-synced');
|
'logical slot is re-synced');
|
||||||
|
@ -1473,10 +1473,11 @@ pg_replication_slots| SELECT l.slot_name,
|
|||||||
l.wal_status,
|
l.wal_status,
|
||||||
l.safe_wal_size,
|
l.safe_wal_size,
|
||||||
l.two_phase,
|
l.two_phase,
|
||||||
l.conflict_reason,
|
l.conflicting,
|
||||||
|
l.invalidation_reason,
|
||||||
l.failover,
|
l.failover,
|
||||||
l.synced
|
l.synced
|
||||||
FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, temporary, active, active_pid, xmin, catalog_xmin, restart_lsn, confirmed_flush_lsn, wal_status, safe_wal_size, two_phase, conflict_reason, failover, synced)
|
FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, temporary, active, active_pid, xmin, catalog_xmin, restart_lsn, confirmed_flush_lsn, wal_status, safe_wal_size, two_phase, conflicting, invalidation_reason, failover, synced)
|
||||||
LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
|
LEFT JOIN pg_database d ON ((l.datoid = d.oid)));
|
||||||
pg_roles| SELECT pg_authid.rolname,
|
pg_roles| SELECT pg_authid.rolname,
|
||||||
pg_authid.rolsuper,
|
pg_authid.rolsuper,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user