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