diff options
author | Amit Kapila <akapila@postgresql.org> | 2021-04-27 09:09:11 +0530 |
---|---|---|
committer | Amit Kapila <akapila@postgresql.org> | 2021-04-27 09:09:11 +0530 |
commit | 3fa17d37716f978f80dfcdab4e7c73f3a24e7a48 (patch) | |
tree | 43a865e413ebd2852e418535b8fbbdb4a83d6d78 /src/backend/utils/adt/pgstatfuncs.c | |
parent | e7eea52b2d61917fbbdac7f3f895e4ef636e935b (diff) | |
download | postgresql-3fa17d37716f978f80dfcdab4e7c73f3a24e7a48.tar.gz postgresql-3fa17d37716f978f80dfcdab4e7c73f3a24e7a48.zip |
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
Diffstat (limited to 'src/backend/utils/adt/pgstatfuncs.c')
-rw-r--r-- | src/backend/utils/adt/pgstatfuncs.c | 146 |
1 files changed, 88 insertions, 58 deletions
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 87f02d572e6..14056f53471 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -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"))); - - /* 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"); - - 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; + /* Initialise values and NULL flags arrays */ + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); - MemoryContextSwitchTo(oldcontext); + /* 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); - 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))); } |