Age Owner TLA Line data Source code
1 : /* -------------------------------------------------------------------------
2 : *
3 : * pgstat_replslot.c
4 : * Implementation of replication slot statistics.
5 : *
6 : * This file contains the implementation of replication slot statistics. It is kept
7 : * separate from pgstat.c to enforce the line between the statistics access /
8 : * storage implementation and the details about individual types of
9 : * statistics.
10 : *
11 : * Replication slot stats work a bit different than other variable-numbered
12 : * stats. Slots do not have oids (so they can be created on physical
13 : * replicas). Use the slot index as object id while running. However, the slot
14 : * index can change when restarting. That is addressed by using the name when
15 : * (de-)serializing. After a restart it is possible for slots to have been
16 : * dropped while shut down, which is addressed by not restoring stats for
17 : * slots that cannot be found by name when starting up.
18 : *
19 : * Copyright (c) 2001-2023, PostgreSQL Global Development Group
20 : *
21 : * IDENTIFICATION
22 : * src/backend/utils/activity/pgstat_replslot.c
23 : * -------------------------------------------------------------------------
24 : */
25 :
26 : #include "postgres.h"
27 :
28 : #include "replication/slot.h"
29 : #include "utils/builtins.h" /* for namestrcpy() */
30 : #include "utils/pgstat_internal.h"
31 :
32 :
33 : static int get_replslot_index(const char *name);
34 :
35 :
36 : /*
37 : * Reset counters for a single replication slot.
38 : *
39 : * Permission checking for this function is managed through the normal
40 : * GRANT system.
41 : */
42 : void
368 andres 43 CBC 4 : pgstat_reset_replslot(const char *name)
44 : {
45 : ReplicationSlot *slot;
46 :
163 peter 47 GNC 4 : Assert(name != NULL);
48 :
49 : /* Check if the slot exits with the given name. */
368 andres 50 CBC 4 : slot = SearchNamedReplicationSlot(name, true);
51 :
52 4 : if (!slot)
53 1 : ereport(ERROR,
54 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
55 : errmsg("replication slot \"%s\" does not exist",
56 : name)));
57 :
58 : /*
59 : * Nothing to do for physical slots as we collect stats only for logical
60 : * slots.
61 : */
62 3 : if (SlotIsPhysical(slot))
368 andres 63 UBC 0 : return;
64 :
65 : /* reset this one entry */
368 andres 66 CBC 3 : pgstat_reset(PGSTAT_KIND_REPLSLOT, InvalidOid,
67 3 : ReplicationSlotIndex(slot));
68 : }
69 :
70 : /*
71 : * Report replication slot statistics.
72 : *
73 : * We can rely on the stats for the slot to exist and to belong to this
74 : * slot. We can only get here if pgstat_create_replslot() or
75 : * pgstat_acquire_replslot() have already been called.
76 : */
77 : void
78 5409 : pgstat_report_replslot(ReplicationSlot *slot, const PgStat_StatReplSlotEntry *repSlotStat)
79 : {
80 : PgStat_EntryRef *entry_ref;
81 : PgStatShared_ReplSlot *shstatent;
82 : PgStat_StatReplSlotEntry *statent;
83 :
84 5409 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid,
85 5409 : ReplicationSlotIndex(slot), false);
86 5409 : shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats;
87 5409 : statent = &shstatent->stats;
88 :
89 : /* Update the replication slot statistics */
90 : #define REPLSLOT_ACC(fld) statent->fld += repSlotStat->fld
91 5409 : REPLSLOT_ACC(spill_txns);
92 5409 : REPLSLOT_ACC(spill_count);
93 5409 : REPLSLOT_ACC(spill_bytes);
94 5409 : REPLSLOT_ACC(stream_txns);
95 5409 : REPLSLOT_ACC(stream_count);
96 5409 : REPLSLOT_ACC(stream_bytes);
97 5409 : REPLSLOT_ACC(total_txns);
98 5409 : REPLSLOT_ACC(total_bytes);
99 : #undef REPLSLOT_ACC
100 :
101 5409 : pgstat_unlock_entry(entry_ref);
384 102 5409 : }
103 :
104 : /*
105 : * Report replication slot creation.
106 : *
107 : * NB: This gets called with ReplicationSlotAllocationLock already held, be
108 : * careful about calling back into slot.c.
109 : */
110 : void
368 111 344 : pgstat_create_replslot(ReplicationSlot *slot)
112 : {
113 : PgStat_EntryRef *entry_ref;
114 : PgStatShared_ReplSlot *shstatent;
115 :
116 344 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid,
117 344 : ReplicationSlotIndex(slot), false);
118 344 : shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats;
119 :
120 : /*
121 : * NB: need to accept that there might be stats from an older slot, e.g.
122 : * if we previously crashed after dropping a slot.
123 : */
124 344 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
125 :
126 344 : pgstat_unlock_entry(entry_ref);
127 344 : }
128 :
129 : /*
130 : * Report replication slot has been acquired.
131 : *
132 : * This guarantees that a stats entry exists during later
133 : * pgstat_report_replslot() calls.
134 : *
135 : * If we previously crashed, no stats data exists. But if we did not crash,
136 : * the stats do belong to this slot:
137 : * - the stats cannot belong to a dropped slot, pgstat_drop_replslot() would
138 : * have been called
139 : * - if the slot was removed while shut down,
140 : * pgstat_replslot_from_serialized_name_cb() returning false would have
141 : * caused the stats to be dropped
142 : */
143 : void
144 767 : pgstat_acquire_replslot(ReplicationSlot *slot)
145 : {
183 146 767 : pgstat_get_entry_ref(PGSTAT_KIND_REPLSLOT, InvalidOid,
147 767 : ReplicationSlotIndex(slot), true, NULL);
384 148 767 : }
149 :
150 : /*
151 : * Report replication slot drop.
152 : */
153 : void
368 154 311 : pgstat_drop_replslot(ReplicationSlot *slot)
155 : {
156 311 : pgstat_drop_entry(PGSTAT_KIND_REPLSLOT, InvalidOid,
157 311 : ReplicationSlotIndex(slot));
158 311 : }
159 :
160 : /*
161 : * Support function for the SQL-callable pgstat* functions. Returns
162 : * a pointer to the replication slot statistics struct.
163 : */
164 : PgStat_StatReplSlotEntry *
165 43 : pgstat_fetch_replslot(NameData slotname)
166 : {
167 43 : int idx = get_replslot_index(NameStr(slotname));
168 :
169 43 : if (idx == -1)
170 2 : return NULL;
171 :
172 41 : return (PgStat_StatReplSlotEntry *)
173 41 : pgstat_fetch_entry(PGSTAT_KIND_REPLSLOT, InvalidOid, idx);
174 : }
175 :
176 : void
183 177 43 : pgstat_replslot_to_serialized_name_cb(const PgStat_HashKey *key, const PgStatShared_Common *header, NameData *name)
178 : {
179 : /*
180 : * This is only called late during shutdown. The set of existing slots
181 : * isn't allowed to change at this point, we can assume that a slot exists
182 : * at the offset.
183 : */
184 43 : if (!ReplicationSlotName(key->objoid, name))
183 andres 185 UBC 0 : elog(ERROR, "could not find name for replication slot index %u",
186 : key->objoid);
368 andres 187 CBC 43 : }
188 :
189 : bool
190 17 : pgstat_replslot_from_serialized_name_cb(const NameData *name, PgStat_HashKey *key)
191 : {
192 17 : int idx = get_replslot_index(NameStr(*name));
193 :
194 : /* slot might have been deleted */
195 17 : if (idx == -1)
196 1 : return false;
197 :
198 16 : key->kind = PGSTAT_KIND_REPLSLOT;
199 16 : key->dboid = InvalidOid;
200 16 : key->objoid = idx;
201 :
202 16 : return true;
203 : }
204 :
205 : void
206 8 : pgstat_replslot_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
207 : {
208 8 : ((PgStatShared_ReplSlot *) header)->stats.stat_reset_timestamp = ts;
209 8 : }
210 :
211 : static int
212 60 : get_replslot_index(const char *name)
213 : {
214 : ReplicationSlot *slot;
215 :
163 peter 216 GNC 60 : Assert(name != NULL);
217 :
368 andres 218 CBC 60 : slot = SearchNamedReplicationSlot(name, true);
219 :
220 60 : if (!slot)
221 3 : return -1;
222 :
223 57 : return ReplicationSlotIndex(slot);
224 : }
|