Branch data 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-2026, 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/pgstat_internal.h"
30 : :
31 : :
32 : : static int get_replslot_index(const char *name, bool need_lock);
33 : :
34 : :
35 : : /*
36 : : * Reset counters for a single replication slot.
37 : : *
38 : : * Permission checking for this function is managed through the normal
39 : : * GRANT system.
40 : : */
41 : : void
42 : 0 : pgstat_reset_replslot(const char *name)
43 : : {
44 : 0 : ReplicationSlot *slot;
45 : :
46 [ # # ]: 0 : Assert(name != NULL);
47 : :
48 : 0 : LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
49 : :
50 : : /* Check if the slot exists with the given name. */
51 : 0 : slot = SearchNamedReplicationSlot(name, false);
52 [ # # ]: 0 : if (!slot)
53 [ # # # # ]: 0 : ereport(ERROR,
54 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
55 : : errmsg("replication slot \"%s\" does not exist",
56 : : name)));
57 : :
58 : : /*
59 : : * Reset stats if it is a logical slot. Nothing to do for physical slots
60 : : * as we collect stats only for logical slots.
61 : : */
62 [ # # ]: 0 : if (SlotIsLogical(slot))
63 : 0 : pgstat_reset(PGSTAT_KIND_REPLSLOT, InvalidOid,
64 : 0 : ReplicationSlotIndex(slot));
65 : :
66 : 0 : LWLockRelease(ReplicationSlotControlLock);
67 : 0 : }
68 : :
69 : : /*
70 : : * Report replication slot statistics.
71 : : *
72 : : * We can rely on the stats for the slot to exist and to belong to this
73 : : * slot. We can only get here if pgstat_create_replslot() or
74 : : * pgstat_acquire_replslot() have already been called.
75 : : */
76 : : void
77 : 0 : pgstat_report_replslot(ReplicationSlot *slot, const PgStat_StatReplSlotEntry *repSlotStat)
78 : : {
79 : 0 : PgStat_EntryRef *entry_ref;
80 : 0 : PgStatShared_ReplSlot *shstatent;
81 : 0 : PgStat_StatReplSlotEntry *statent;
82 : :
83 : 0 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid,
84 : 0 : ReplicationSlotIndex(slot), false);
85 : 0 : shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats;
86 : 0 : statent = &shstatent->stats;
87 : :
88 : : /* Update the replication slot statistics */
89 : : #define REPLSLOT_ACC(fld) statent->fld += repSlotStat->fld
90 : 0 : REPLSLOT_ACC(spill_txns);
91 : 0 : REPLSLOT_ACC(spill_count);
92 : 0 : REPLSLOT_ACC(spill_bytes);
93 : 0 : REPLSLOT_ACC(stream_txns);
94 : 0 : REPLSLOT_ACC(stream_count);
95 : 0 : REPLSLOT_ACC(stream_bytes);
96 : 0 : REPLSLOT_ACC(mem_exceeded_count);
97 : 0 : REPLSLOT_ACC(total_txns);
98 : 0 : REPLSLOT_ACC(total_bytes);
99 : : #undef REPLSLOT_ACC
100 : :
101 : 0 : pgstat_unlock_entry(entry_ref);
102 : 0 : }
103 : :
104 : : /*
105 : : * Report replication slot sync skip statistics.
106 : : *
107 : : * Similar to pgstat_report_replslot(), we can rely on the stats for the
108 : : * slot to exist and to belong to this slot.
109 : : */
110 : : void
111 : 0 : pgstat_report_replslotsync(ReplicationSlot *slot)
112 : : {
113 : 0 : PgStat_EntryRef *entry_ref;
114 : 0 : PgStatShared_ReplSlot *shstatent;
115 : 0 : PgStat_StatReplSlotEntry *statent;
116 : :
117 : : /* Slot sync stats are valid only for synced logical slots on standby. */
118 [ # # ]: 0 : Assert(slot->data.synced);
119 [ # # ]: 0 : Assert(RecoveryInProgress());
120 : :
121 : 0 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid,
122 : 0 : ReplicationSlotIndex(slot), false);
123 [ # # ]: 0 : Assert(entry_ref != NULL);
124 : :
125 : 0 : shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats;
126 : 0 : statent = &shstatent->stats;
127 : :
128 : 0 : statent->slotsync_skip_count += 1;
129 : 0 : statent->slotsync_last_skip = GetCurrentTimestamp();
130 : :
131 : 0 : pgstat_unlock_entry(entry_ref);
132 : 0 : }
133 : :
134 : : /*
135 : : * Report replication slot creation.
136 : : *
137 : : * NB: This gets called with ReplicationSlotAllocationLock already held, be
138 : : * careful about calling back into slot.c.
139 : : */
140 : : void
141 : 0 : pgstat_create_replslot(ReplicationSlot *slot)
142 : : {
143 : 0 : PgStat_EntryRef *entry_ref;
144 : 0 : PgStatShared_ReplSlot *shstatent;
145 : :
146 [ # # ]: 0 : Assert(LWLockHeldByMeInMode(ReplicationSlotAllocationLock, LW_EXCLUSIVE));
147 : :
148 : 0 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_REPLSLOT, InvalidOid,
149 : 0 : ReplicationSlotIndex(slot), false);
150 : 0 : shstatent = (PgStatShared_ReplSlot *) entry_ref->shared_stats;
151 : :
152 : : /*
153 : : * NB: need to accept that there might be stats from an older slot, e.g.
154 : : * if we previously crashed after dropping a slot.
155 : : */
156 : 0 : memset(&shstatent->stats, 0, sizeof(shstatent->stats));
157 : :
158 : 0 : pgstat_unlock_entry(entry_ref);
159 : 0 : }
160 : :
161 : : /*
162 : : * Report replication slot has been acquired.
163 : : *
164 : : * This guarantees that a stats entry exists during later
165 : : * pgstat_report_replslot() or pgstat_report_replslotsync() calls.
166 : : *
167 : : * If we previously crashed, no stats data exists. But if we did not crash,
168 : : * the stats do belong to this slot:
169 : : * - the stats cannot belong to a dropped slot, pgstat_drop_replslot() would
170 : : * have been called
171 : : * - if the slot was removed while shut down,
172 : : * pgstat_replslot_from_serialized_name_cb() returning false would have
173 : : * caused the stats to be dropped
174 : : */
175 : : void
176 : 0 : pgstat_acquire_replslot(ReplicationSlot *slot)
177 : : {
178 : 0 : pgstat_get_entry_ref(PGSTAT_KIND_REPLSLOT, InvalidOid,
179 : 0 : ReplicationSlotIndex(slot), true, NULL);
180 : 0 : }
181 : :
182 : : /*
183 : : * Report replication slot drop.
184 : : */
185 : : void
186 : 0 : pgstat_drop_replslot(ReplicationSlot *slot)
187 : : {
188 [ # # ]: 0 : Assert(LWLockHeldByMeInMode(ReplicationSlotAllocationLock, LW_EXCLUSIVE));
189 : :
190 [ # # ]: 0 : if (!pgstat_drop_entry(PGSTAT_KIND_REPLSLOT, InvalidOid,
191 : 0 : ReplicationSlotIndex(slot)))
192 : 0 : pgstat_request_entry_refs_gc();
193 : 0 : }
194 : :
195 : : /*
196 : : * Support function for the SQL-callable pgstat* functions. Returns
197 : : * a pointer to the replication slot statistics struct.
198 : : */
199 : : PgStat_StatReplSlotEntry *
200 : 0 : pgstat_fetch_replslot(NameData slotname)
201 : : {
202 : 0 : int idx;
203 : 0 : PgStat_StatReplSlotEntry *slotentry = NULL;
204 : :
205 : 0 : LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
206 : :
207 : 0 : idx = get_replslot_index(NameStr(slotname), false);
208 : :
209 [ # # ]: 0 : if (idx != -1)
210 : 0 : slotentry = (PgStat_StatReplSlotEntry *) pgstat_fetch_entry(PGSTAT_KIND_REPLSLOT,
211 : 0 : InvalidOid, idx);
212 : :
213 : 0 : LWLockRelease(ReplicationSlotControlLock);
214 : :
215 : 0 : return slotentry;
216 : 0 : }
217 : :
218 : : void
219 : 0 : pgstat_replslot_to_serialized_name_cb(const PgStat_HashKey *key, const PgStatShared_Common *header, NameData *name)
220 : : {
221 : : /*
222 : : * This is only called late during shutdown. The set of existing slots
223 : : * isn't allowed to change at this point, we can assume that a slot exists
224 : : * at the offset.
225 : : */
226 [ # # ]: 0 : if (!ReplicationSlotName(key->objid, name))
227 [ # # # # ]: 0 : elog(ERROR, "could not find name for replication slot index %" PRIu64,
228 : : key->objid);
229 : 0 : }
230 : :
231 : : bool
232 : 0 : pgstat_replslot_from_serialized_name_cb(const NameData *name, PgStat_HashKey *key)
233 : : {
234 : 0 : int idx = get_replslot_index(NameStr(*name), true);
235 : :
236 : : /* slot might have been deleted */
237 [ # # ]: 0 : if (idx == -1)
238 : 0 : return false;
239 : :
240 : 0 : key->kind = PGSTAT_KIND_REPLSLOT;
241 : 0 : key->dboid = InvalidOid;
242 : 0 : key->objid = idx;
243 : :
244 : 0 : return true;
245 : 0 : }
246 : :
247 : : void
248 : 0 : pgstat_replslot_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts)
249 : : {
250 : 0 : ((PgStatShared_ReplSlot *) header)->stats.stat_reset_timestamp = ts;
251 : 0 : }
252 : :
253 : : static int
254 : 0 : get_replslot_index(const char *name, bool need_lock)
255 : : {
256 : 0 : ReplicationSlot *slot;
257 : :
258 [ # # ]: 0 : Assert(name != NULL);
259 : :
260 : 0 : slot = SearchNamedReplicationSlot(name, need_lock);
261 : :
262 [ # # ]: 0 : if (!slot)
263 : 0 : return -1;
264 : :
265 : 0 : return ReplicationSlotIndex(slot);
266 : 0 : }
|