mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Use HTAB for replication slot statistics.
Previously, we used to use the array of size max_replication_slots to store stats for replication slots. But that had two problems in the cases where a message for dropping a slot gets lost: 1) the stats for the new slot are not recorded if the array is full and 2) writing beyond the end of the array if the user reduces the max_replication_slots. This commit uses HTAB for replication slot statistics, resolving both problems. Now, pgstat_vacuum_stat() search for all the dead replication slots in stats hashtable and tell the collector to remove them. To avoid showing the stats for the already-dropped slots, pg_stat_replication_slots view searches slot stats by the slot name taken from pg_replication_slots. Also, we send a message for creating a slot at slot creation, initializing the stats. This reduces the possibility that the stats are accumulated into the old slot stats when a message for dropping a slot gets lost. Reported-by: Andres Freund Author: Sawada Masahiko, test case by Vignesh C Reviewed-by: Amit Kapila, Vignesh C, Dilip Kumar Discussion: https://postgr.es/m/20210319185247.ldebgpdaxsowiflw@alap3.anarazel.de
This commit is contained in:
@ -24,6 +24,7 @@
|
||||
#include "pgstat.h"
|
||||
#include "postmaster/bgworker_internals.h"
|
||||
#include "postmaster/postmaster.h"
|
||||
#include "replication/slot.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/procarray.h"
|
||||
#include "utils/acl.h"
|
||||
@ -2207,8 +2208,33 @@ pg_stat_reset_replication_slot(PG_FUNCTION_ARGS)
|
||||
char *target = NULL;
|
||||
|
||||
if (!PG_ARGISNULL(0))
|
||||
{
|
||||
ReplicationSlot *slot;
|
||||
|
||||
target = text_to_cstring(PG_GETARG_TEXT_PP(0));
|
||||
|
||||
/*
|
||||
* Check if the slot exists with the given name. It is possible that
|
||||
* by the time this message is executed the slot is dropped but at
|
||||
* least this check will ensure that the given name is for a valid
|
||||
* slot.
|
||||
*/
|
||||
slot = SearchNamedReplicationSlot(target, true);
|
||||
|
||||
if (!slot)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("replication slot \"%s\" does not exist",
|
||||
target)));
|
||||
|
||||
/*
|
||||
* Nothing to do for physical slots as we collect stats only for
|
||||
* logical slots.
|
||||
*/
|
||||
if (SlotIsPhysical(slot))
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
pgstat_reset_replslot_counter(target);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
@ -2280,73 +2306,77 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
|
||||
}
|
||||
|
||||
/* Get the statistics for the replication slots */
|
||||
/*
|
||||
* Get the statistics for the replication slot. If the slot statistics is not
|
||||
* available, return all-zeroes stats.
|
||||
*/
|
||||
Datum
|
||||
pg_stat_get_replication_slots(PG_FUNCTION_ARGS)
|
||||
pg_stat_get_replication_slot(PG_FUNCTION_ARGS)
|
||||
{
|
||||
#define PG_STAT_GET_REPLICATION_SLOT_COLS 10
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
text *slotname_text = PG_GETARG_TEXT_P(0);
|
||||
NameData slotname;
|
||||
TupleDesc tupdesc;
|
||||
Tuplestorestate *tupstore;
|
||||
MemoryContext per_query_ctx;
|
||||
MemoryContext oldcontext;
|
||||
PgStat_ReplSlotStats *slotstats;
|
||||
int nstats;
|
||||
int i;
|
||||
Datum values[10];
|
||||
bool nulls[10];
|
||||
PgStat_StatReplSlotEntry *slotent;
|
||||
PgStat_StatReplSlotEntry allzero;
|
||||
|
||||
/* check to see if caller supports us returning a tuplestore */
|
||||
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("set-valued function called in context that cannot accept a set")));
|
||||
if (!(rsinfo->allowedModes & SFRM_Materialize))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("materialize mode required, but it is not allowed in this context")));
|
||||
/* Initialise values and NULL flags arrays */
|
||||
MemSet(values, 0, sizeof(values));
|
||||
MemSet(nulls, 0, sizeof(nulls));
|
||||
|
||||
/* Build a tuple descriptor for our result type */
|
||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
|
||||
elog(ERROR, "return type must be a row type");
|
||||
/* Initialise attributes information in the tuple descriptor */
|
||||
tupdesc = CreateTemplateTupleDesc(PG_STAT_GET_REPLICATION_SLOT_COLS);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "slot_name",
|
||||
TEXTOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "spill_txns",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "spill_count",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "spill_bytes",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "stream_txns",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "stream_count",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "stream_bytes",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "total_txns",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "total_bytes",
|
||||
INT8OID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "stats_reset",
|
||||
TIMESTAMPTZOID, -1, 0);
|
||||
BlessTupleDesc(tupdesc);
|
||||
|
||||
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
|
||||
oldcontext = MemoryContextSwitchTo(per_query_ctx);
|
||||
|
||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
||||
rsinfo->returnMode = SFRM_Materialize;
|
||||
rsinfo->setResult = tupstore;
|
||||
rsinfo->setDesc = tupdesc;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
slotstats = pgstat_fetch_replslot(&nstats);
|
||||
for (i = 0; i < nstats; i++)
|
||||
namestrcpy(&slotname, text_to_cstring(slotname_text));
|
||||
slotent = pgstat_fetch_replslot(slotname);
|
||||
if (!slotent)
|
||||
{
|
||||
Datum values[PG_STAT_GET_REPLICATION_SLOT_COLS];
|
||||
bool nulls[PG_STAT_GET_REPLICATION_SLOT_COLS];
|
||||
PgStat_ReplSlotStats *s = &(slotstats[i]);
|
||||
|
||||
MemSet(values, 0, sizeof(values));
|
||||
MemSet(nulls, 0, sizeof(nulls));
|
||||
|
||||
values[0] = CStringGetTextDatum(NameStr(s->slotname));
|
||||
values[1] = Int64GetDatum(s->spill_txns);
|
||||
values[2] = Int64GetDatum(s->spill_count);
|
||||
values[3] = Int64GetDatum(s->spill_bytes);
|
||||
values[4] = Int64GetDatum(s->stream_txns);
|
||||
values[5] = Int64GetDatum(s->stream_count);
|
||||
values[6] = Int64GetDatum(s->stream_bytes);
|
||||
values[7] = Int64GetDatum(s->total_txns);
|
||||
values[8] = Int64GetDatum(s->total_bytes);
|
||||
|
||||
if (s->stat_reset_timestamp == 0)
|
||||
nulls[9] = true;
|
||||
else
|
||||
values[9] = TimestampTzGetDatum(s->stat_reset_timestamp);
|
||||
|
||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||
/*
|
||||
* If the slot is not found, initialise its stats. This is possible if
|
||||
* the create slot message is lost.
|
||||
*/
|
||||
memset(&allzero, 0, sizeof(PgStat_StatReplSlotEntry));
|
||||
slotent = &allzero;
|
||||
}
|
||||
|
||||
tuplestore_donestoring(tupstore);
|
||||
values[0] = CStringGetTextDatum(NameStr(slotname));
|
||||
values[1] = Int64GetDatum(slotent->spill_txns);
|
||||
values[2] = Int64GetDatum(slotent->spill_count);
|
||||
values[3] = Int64GetDatum(slotent->spill_bytes);
|
||||
values[4] = Int64GetDatum(slotent->stream_txns);
|
||||
values[5] = Int64GetDatum(slotent->stream_count);
|
||||
values[6] = Int64GetDatum(slotent->stream_bytes);
|
||||
values[7] = Int64GetDatum(slotent->total_txns);
|
||||
values[8] = Int64GetDatum(slotent->total_bytes);
|
||||
|
||||
return (Datum) 0;
|
||||
if (slotent->stat_reset_timestamp == 0)
|
||||
nulls[9] = true;
|
||||
else
|
||||
values[9] = TimestampTzGetDatum(slotent->stat_reset_timestamp);
|
||||
|
||||
/* Returns the record as Datum */
|
||||
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
|
||||
}
|
||||
|
Reference in New Issue
Block a user