Skip to content

Commit 7a8cb4a

Browse files
committed
Don't call palloc() while holding a spinlock, either.
Fix some more violations of the "only straight-line code inside a spinlock" rule. These are hazardous not only because they risk holding the lock for an excessively long time, but because it's possible for palloc to throw elog(ERROR), leaving a stuck spinlock behind. copy_replication_slot() had two separate places that did pallocs while holding a spinlock. We can make the code simpler and safer by copying the whole ReplicationSlot struct into a local variable while holding the spinlock, and then referencing that copy. (While that's arguably more cycles than we really need to spend holding the lock, the struct isn't all that big, and this way seems far more maintainable than copying fields piecemeal. Anyway this is surely much cheaper than a palloc.) That bug goes back to v12. InvalidateObsoleteReplicationSlots() not only did a palloc while holding a spinlock, but for extra sloppiness then leaked the memory --- probably for the lifetime of the checkpointer process, though I didn't try to verify that. Fortunately that silliness is new in HEAD. pg_get_replication_slots() had a cosmetic violation of the rule, in that it only assumed it's safe to call namecpy() while holding a spinlock. Still, that's a hazard waiting to bite somebody, and there were some other cosmetic coding-rule violations in the same function, so clean it up. I back-patched this as far as v10; the code exists before that but it looks different, and this didn't seem important enough to adapt the patch further back. Discussion: https://postgr.es/m/20200602.161518.1399689010416646074.horikyota.ntt@gmail.com
1 parent 8bc7449 commit 7a8cb4a

File tree

2 files changed

+27
-41
lines changed

2 files changed

+27
-41
lines changed

src/backend/replication/slotfuncs.c

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -228,87 +228,73 @@ pg_get_replication_slots(PG_FUNCTION_ARGS)
228228
for (slotno = 0; slotno < max_replication_slots; slotno++)
229229
{
230230
ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[slotno];
231+
ReplicationSlot slot_contents;
231232
Datum values[PG_GET_REPLICATION_SLOTS_COLS];
232233
bool nulls[PG_GET_REPLICATION_SLOTS_COLS];
233-
234-
ReplicationSlotPersistency persistency;
235-
TransactionId xmin;
236-
TransactionId catalog_xmin;
237-
XLogRecPtr restart_lsn;
238-
XLogRecPtr confirmed_flush_lsn;
239-
pid_t active_pid;
240-
Oid database;
241-
NameData slot_name;
242-
NameData plugin;
243234
int i;
244235

245236
if (!slot->in_use)
246237
continue;
247238

239+
/* Copy slot contents while holding spinlock, then examine at leisure */
248240
SpinLockAcquire(&slot->mutex);
249-
250-
xmin = slot->data.xmin;
251-
catalog_xmin = slot->data.catalog_xmin;
252-
database = slot->data.database;
253-
restart_lsn = slot->data.restart_lsn;
254-
confirmed_flush_lsn = slot->data.confirmed_flush;
255-
namecpy(&slot_name, &slot->data.name);
256-
namecpy(&plugin, &slot->data.plugin);
257-
active_pid = slot->active_pid;
258-
persistency = slot->data.persistency;
259-
241+
slot_contents = *slot;
260242
SpinLockRelease(&slot->mutex);
261243

244+
memset(values, 0, sizeof(values));
262245
memset(nulls, 0, sizeof(nulls));
263246

264247
i = 0;
265-
values[i++] = NameGetDatum(&slot_name);
248+
values[i++] = NameGetDatum(&slot_contents.data.name);
266249

267-
if (database == InvalidOid)
250+
if (slot_contents.data.database == InvalidOid)
268251
nulls[i++] = true;
269252
else
270-
values[i++] = NameGetDatum(&plugin);
253+
values[i++] = NameGetDatum(&slot_contents.data.plugin);
271254

272-
if (database == InvalidOid)
255+
if (slot_contents.data.database == InvalidOid)
273256
values[i++] = CStringGetTextDatum("physical");
274257
else
275258
values[i++] = CStringGetTextDatum("logical");
276259

277-
if (database == InvalidOid)
260+
if (slot_contents.data.database == InvalidOid)
278261
nulls[i++] = true;
279262
else
280-
values[i++] = database;
263+
values[i++] = ObjectIdGetDatum(slot_contents.data.database);
281264

282-
values[i++] = BoolGetDatum(persistency == RS_TEMPORARY);
283-
values[i++] = BoolGetDatum(active_pid != 0);
265+
values[i++] = BoolGetDatum(slot_contents.data.persistency == RS_TEMPORARY);
266+
values[i++] = BoolGetDatum(slot_contents.active_pid != 0);
284267

285-
if (active_pid != 0)
286-
values[i++] = Int32GetDatum(active_pid);
268+
if (slot_contents.active_pid != 0)
269+
values[i++] = Int32GetDatum(slot_contents.active_pid);
287270
else
288271
nulls[i++] = true;
289272

290-
if (xmin != InvalidTransactionId)
291-
values[i++] = TransactionIdGetDatum(xmin);
273+
if (slot_contents.data.xmin != InvalidTransactionId)
274+
values[i++] = TransactionIdGetDatum(slot_contents.data.xmin);
292275
else
293276
nulls[i++] = true;
294277

295-
if (catalog_xmin != InvalidTransactionId)
296-
values[i++] = TransactionIdGetDatum(catalog_xmin);
278+
if (slot_contents.data.catalog_xmin != InvalidTransactionId)
279+
values[i++] = TransactionIdGetDatum(slot_contents.data.catalog_xmin);
297280
else
298281
nulls[i++] = true;
299282

300-
if (restart_lsn != InvalidXLogRecPtr)
301-
values[i++] = LSNGetDatum(restart_lsn);
283+
if (slot_contents.data.restart_lsn != InvalidXLogRecPtr)
284+
values[i++] = LSNGetDatum(slot_contents.data.restart_lsn);
302285
else
303286
nulls[i++] = true;
304287

305-
if (confirmed_flush_lsn != InvalidXLogRecPtr)
306-
values[i++] = LSNGetDatum(confirmed_flush_lsn);
288+
if (slot_contents.data.confirmed_flush != InvalidXLogRecPtr)
289+
values[i++] = LSNGetDatum(slot_contents.data.confirmed_flush);
307290
else
308291
nulls[i++] = true;
309292

293+
Assert(i == PG_GET_REPLICATION_SLOTS_COLS);
294+
310295
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
311296
}
297+
312298
LWLockRelease(ReplicationSlotControlLock);
313299

314300
tuplestore_donestoring(tupstore);

src/include/replication/slot.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ typedef struct ReplicationSlot
151151
XLogRecPtr candidate_restart_lsn;
152152
} ReplicationSlot;
153153

154-
#define SlotIsPhysical(slot) (slot->data.database == InvalidOid)
155-
#define SlotIsLogical(slot) (slot->data.database != InvalidOid)
154+
#define SlotIsPhysical(slot) ((slot)->data.database == InvalidOid)
155+
#define SlotIsLogical(slot) ((slot)->data.database != InvalidOid)
156156

157157
/*
158158
* Shared memory control area for all of replication slots.

0 commit comments

Comments
 (0)