Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/replication/slot.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * slot.c
4
 *     Replication slot management.
5
 *
6
 *
7
 * Copyright (c) 2012-2026, PostgreSQL Global Development Group
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/replication/slot.c
12
 *
13
 * NOTES
14
 *
15
 * Replication slots are used to keep state about replication streams
16
 * originating from this cluster.  Their primary purpose is to prevent the
17
 * premature removal of WAL or of old tuple versions in a manner that would
18
 * interfere with replication; they are also useful for monitoring purposes.
19
 * Slots need to be permanent (to allow restarts), crash-safe, and allocatable
20
 * on standbys (to support cascading setups).  The requirement that slots be
21
 * usable on standbys precludes storing them in the system catalogs.
22
 *
23
 * Each replication slot gets its own directory inside the directory
24
 * $PGDATA / PG_REPLSLOT_DIR.  Inside that directory the state file will
25
 * contain the slot's own data.  Additional data can be stored alongside that
26
 * file if required.  While the server is running, the state data is also
27
 * cached in memory for efficiency.
28
 *
29
 * ReplicationSlotAllocationLock must be taken in exclusive mode to allocate
30
 * or free a slot. ReplicationSlotControlLock must be taken in shared mode
31
 * to iterate over the slots, and in exclusive mode to change the in_use flag
32
 * of a slot.  The remaining data in each slot is protected by its mutex.
33
 *
34
 *-------------------------------------------------------------------------
35
 */
36
37
#include "postgres.h"
38
39
#include <unistd.h>
40
#include <sys/stat.h>
41
42
#include "access/transam.h"
43
#include "access/xlog_internal.h"
44
#include "access/xlogrecovery.h"
45
#include "common/file_utils.h"
46
#include "common/string.h"
47
#include "miscadmin.h"
48
#include "pgstat.h"
49
#include "postmaster/interrupt.h"
50
#include "replication/logicallauncher.h"
51
#include "replication/slotsync.h"
52
#include "replication/slot.h"
53
#include "replication/walsender_private.h"
54
#include "storage/fd.h"
55
#include "storage/ipc.h"
56
#include "storage/proc.h"
57
#include "storage/procarray.h"
58
#include "storage/subsystems.h"
59
#include "utils/builtins.h"
60
#include "utils/guc_hooks.h"
61
#include "utils/injection_point.h"
62
#include "utils/varlena.h"
63
#include "utils/wait_event.h"
64
65
/*
66
 * Replication slot on-disk data structure.
67
 */
68
typedef struct ReplicationSlotOnDisk
69
{
70
  /* first part of this struct needs to be version independent */
71
72
  /* data not covered by checksum */
73
  uint32    magic;
74
  pg_crc32c checksum;
75
76
  /* data covered by checksum */
77
  uint32    version;
78
  uint32    length;
79
80
  /*
81
   * The actual data in the slot that follows can differ based on the above
82
   * 'version'.
83
   */
84
85
  ReplicationSlotPersistentData slotdata;
86
} ReplicationSlotOnDisk;
87
88
/*
89
 * Struct for the configuration of synchronized_standby_slots.
90
 *
91
 * Note: this must be a flat representation that can be held in a single chunk
92
 * of guc_malloc'd memory, so that it can be stored as the "extra" data for the
93
 * synchronized_standby_slots GUC.
94
 */
95
typedef struct
96
{
97
  /* Number of slot names in the slot_names[] */
98
  int     nslotnames;
99
100
  /*
101
   * slot_names contains 'nslotnames' consecutive null-terminated C strings.
102
   */
103
  char    slot_names[FLEXIBLE_ARRAY_MEMBER];
104
} SyncStandbySlotsConfigData;
105
106
/*
107
 * Lookup table for slot invalidation causes.
108
 */
109
typedef struct SlotInvalidationCauseMap
110
{
111
  ReplicationSlotInvalidationCause cause;
112
  const char *cause_name;
113
} SlotInvalidationCauseMap;
114
115
static const SlotInvalidationCauseMap SlotInvalidationCauses[] = {
116
  {RS_INVAL_NONE, "none"},
117
  {RS_INVAL_WAL_REMOVED, "wal_removed"},
118
  {RS_INVAL_HORIZON, "rows_removed"},
119
  {RS_INVAL_WAL_LEVEL, "wal_level_insufficient"},
120
  {RS_INVAL_IDLE_TIMEOUT, "idle_timeout"},
121
};
122
123
/*
124
 * Ensure that the lookup table is up-to-date with the enums defined in
125
 * ReplicationSlotInvalidationCause.
126
 */
127
StaticAssertDecl(lengthof(SlotInvalidationCauses) == (RS_INVAL_MAX_CAUSES + 1),
128
         "array length mismatch");
129
130
/* size of version independent data */
131
#define ReplicationSlotOnDiskConstantSize \
132
0
  offsetof(ReplicationSlotOnDisk, slotdata)
133
/* size of the part of the slot not covered by the checksum */
134
#define ReplicationSlotOnDiskNotChecksummedSize  \
135
  offsetof(ReplicationSlotOnDisk, version)
136
/* size of the part covered by the checksum */
137
#define ReplicationSlotOnDiskChecksummedSize \
138
  sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskNotChecksummedSize
139
/* size of the slot data that is version dependent */
140
#define ReplicationSlotOnDiskV2Size \
141
0
  sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskConstantSize
142
143
0
#define SLOT_MAGIC    0x1051CA1  /* format identifier */
144
0
#define SLOT_VERSION  5    /* version for new files */
145
146
/* Control array for replication slot management */
147
ReplicationSlotCtlData *ReplicationSlotCtl = NULL;
148
149
static void ReplicationSlotsShmemRequest(void *arg);
150
static void ReplicationSlotsShmemInit(void *arg);
151
152
const ShmemCallbacks ReplicationSlotsShmemCallbacks = {
153
  .request_fn = ReplicationSlotsShmemRequest,
154
  .init_fn = ReplicationSlotsShmemInit,
155
};
156
157
/* My backend's replication slot in the shared memory array */
158
ReplicationSlot *MyReplicationSlot = NULL;
159
160
/* GUC variables */
161
int     max_replication_slots = 10; /* the maximum number of replication
162
                     * slots */
163
int     max_repack_replication_slots = 5; /* the maximum number of slots
164
                         * for REPACK */
165
166
/*
167
 * Invalidate replication slots that have remained idle longer than this
168
 * duration; '0' disables it.
169
 */
170
int     idle_replication_slot_timeout_secs = 0;
171
172
/*
173
 * This GUC lists streaming replication standby server slot names that
174
 * logical WAL sender processes will wait for.
175
 */
176
char     *synchronized_standby_slots;
177
178
/* This is the parsed and cached configuration for synchronized_standby_slots */
179
static SyncStandbySlotsConfigData *synchronized_standby_slots_config;
180
181
/*
182
 * Oldest LSN that has been confirmed to be flushed to the standbys
183
 * corresponding to the physical slots specified in the synchronized_standby_slots GUC.
184
 */
185
static XLogRecPtr ss_oldest_flush_lsn = InvalidXLogRecPtr;
186
187
static void ReplicationSlotShmemExit(int code, Datum arg);
188
static bool IsSlotForConflictCheck(const char *name);
189
static void ReplicationSlotDropPtr(ReplicationSlot *slot);
190
191
/* internal persistency functions */
192
static void RestoreSlotFromDisk(const char *name);
193
static void CreateSlotOnDisk(ReplicationSlot *slot);
194
static void SaveSlotToPath(ReplicationSlot *slot, const char *dir, int elevel);
195
196
/*
197
 * Register shared memory space needed for replication slots.
198
 */
199
static void
200
ReplicationSlotsShmemRequest(void *arg)
201
0
{
202
0
  Size    size;
203
204
0
  if (max_replication_slots + max_repack_replication_slots == 0)
205
0
    return;
206
207
0
  size = offsetof(ReplicationSlotCtlData, replication_slots);
208
0
  size = add_size(size,
209
0
          mul_size(max_replication_slots + max_repack_replication_slots,
210
0
               sizeof(ReplicationSlot)));
211
0
  ShmemRequestStruct(.name = "ReplicationSlot Ctl",
212
0
             .size = size,
213
0
             .ptr = (void **) &ReplicationSlotCtl,
214
0
    );
215
0
}
216
217
/*
218
 * Initialize shared memory for replication slots.
219
 */
220
static void
221
ReplicationSlotsShmemInit(void *arg)
222
0
{
223
0
  for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
224
0
  {
225
0
    ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[i];
226
227
    /* everything else is zeroed by the memset above */
228
0
    slot->active_proc = INVALID_PROC_NUMBER;
229
0
    SpinLockInit(&slot->mutex);
230
0
    LWLockInitialize(&slot->io_in_progress_lock,
231
0
             LWTRANCHE_REPLICATION_SLOT_IO);
232
0
    ConditionVariableInit(&slot->active_cv);
233
0
  }
234
0
}
235
236
/*
237
 * Register the callback for replication slot cleanup and releasing.
238
 */
239
void
240
ReplicationSlotInitialize(void)
241
0
{
242
0
  before_shmem_exit(ReplicationSlotShmemExit, 0);
243
0
}
244
245
/*
246
 * Release and cleanup replication slots.
247
 */
248
static void
249
ReplicationSlotShmemExit(int code, Datum arg)
250
0
{
251
  /* Make sure active replication slots are released */
252
0
  if (MyReplicationSlot != NULL)
253
0
    ReplicationSlotRelease();
254
255
  /* Also cleanup all the temporary slots. */
256
0
  ReplicationSlotCleanup(false);
257
0
}
258
259
/*
260
 * Check whether the passed slot name is valid and report errors at elevel.
261
 *
262
 * See comments for ReplicationSlotValidateNameInternal().
263
 */
264
bool
265
ReplicationSlotValidateName(const char *name, bool allow_reserved_name,
266
              int elevel)
267
0
{
268
0
  int     err_code;
269
0
  char     *err_msg = NULL;
270
0
  char     *err_hint = NULL;
271
272
0
  if (!ReplicationSlotValidateNameInternal(name, allow_reserved_name,
273
0
                       &err_code, &err_msg, &err_hint))
274
0
  {
275
    /*
276
     * Use errmsg_internal() and errhint_internal() instead of errmsg()
277
     * and errhint(), since the messages from
278
     * ReplicationSlotValidateNameInternal() are already translated. This
279
     * avoids double translation.
280
     */
281
0
    ereport(elevel,
282
0
        errcode(err_code),
283
0
        errmsg_internal("%s", err_msg),
284
0
        (err_hint != NULL) ? errhint_internal("%s", err_hint) : 0);
285
286
0
    pfree(err_msg);
287
0
    if (err_hint != NULL)
288
0
      pfree(err_hint);
289
0
    return false;
290
0
  }
291
292
0
  return true;
293
0
}
294
295
/*
296
 * Check whether the passed slot name is valid.
297
 *
298
 * An error will be reported for a reserved replication slot name if
299
 * allow_reserved_name is set to false.
300
 *
301
 * Slot names may consist out of [a-z0-9_]{1,NAMEDATALEN-1} which should allow
302
 * the name to be used as a directory name on every supported OS.
303
 *
304
 * Returns true if the slot name is valid. Otherwise, returns false and stores
305
 * the error code, error message, and optional hint in err_code, err_msg, and
306
 * err_hint, respectively. The caller is responsible for freeing err_msg and
307
 * err_hint, which are palloc'd.
308
 */
309
bool
310
ReplicationSlotValidateNameInternal(const char *name, bool allow_reserved_name,
311
                  int *err_code, char **err_msg, char **err_hint)
312
0
{
313
0
  const char *cp;
314
315
0
  if (strlen(name) == 0)
316
0
  {
317
0
    *err_code = ERRCODE_INVALID_NAME;
318
0
    *err_msg = psprintf(_("replication slot name \"%s\" is too short"), name);
319
0
    *err_hint = NULL;
320
0
    return false;
321
0
  }
322
323
0
  if (strlen(name) >= NAMEDATALEN)
324
0
  {
325
0
    *err_code = ERRCODE_NAME_TOO_LONG;
326
0
    *err_msg = psprintf(_("replication slot name \"%s\" is too long"), name);
327
0
    *err_hint = NULL;
328
0
    return false;
329
0
  }
330
331
0
  for (cp = name; *cp; cp++)
332
0
  {
333
0
    if (!((*cp >= 'a' && *cp <= 'z')
334
0
        || (*cp >= '0' && *cp <= '9')
335
0
        || (*cp == '_')))
336
0
    {
337
0
      *err_code = ERRCODE_INVALID_NAME;
338
0
      *err_msg = psprintf(_("replication slot name \"%s\" contains invalid character"), name);
339
0
      *err_hint = psprintf(_("Replication slot names may only contain lower case letters, numbers, and the underscore character."));
340
0
      return false;
341
0
    }
342
0
  }
343
344
0
  if (!allow_reserved_name && IsSlotForConflictCheck(name))
345
0
  {
346
0
    *err_code = ERRCODE_RESERVED_NAME;
347
0
    *err_msg = psprintf(_("replication slot name \"%s\" is reserved"), name);
348
0
    *err_hint = psprintf(_("The name \"%s\" is reserved for the conflict detection slot."),
349
0
               CONFLICT_DETECTION_SLOT);
350
0
    return false;
351
0
  }
352
353
0
  return true;
354
0
}
355
356
/*
357
 * Return true if the replication slot name is "pg_conflict_detection".
358
 */
359
static bool
360
IsSlotForConflictCheck(const char *name)
361
0
{
362
0
  return (strcmp(name, CONFLICT_DETECTION_SLOT) == 0);
363
0
}
364
365
/*
366
 * Create a new replication slot and mark it as used by this backend.
367
 *
368
 * name: Name of the slot
369
 * db_specific: logical decoding is db specific; if the slot is going to
370
 *     be used for that pass true, otherwise false.
371
 * two_phase: If enabled, allows decoding of prepared transactions.
372
 * repack: If true, use a slot from the pool for REPACK.
373
 * failover: If enabled, allows the slot to be synced to standbys so
374
 *     that logical replication can be resumed after failover.
375
 * synced: True if the slot is synchronized from the primary server.
376
 */
377
void
378
ReplicationSlotCreate(const char *name, bool db_specific,
379
            ReplicationSlotPersistency persistency,
380
            bool two_phase, bool repack, bool failover, bool synced)
381
0
{
382
0
  ReplicationSlot *slot = NULL;
383
0
  int     startpoint,
384
0
        endpoint;
385
386
0
  Assert(MyReplicationSlot == NULL);
387
388
  /*
389
   * The logical launcher or pg_upgrade may create or migrate an internal
390
   * slot, so using a reserved name is allowed in these cases.
391
   */
392
0
  ReplicationSlotValidateName(name, IsBinaryUpgrade || IsLogicalLauncher(),
393
0
                ERROR);
394
395
0
  if (failover)
396
0
  {
397
    /*
398
     * Do not allow users to create the failover enabled slots on the
399
     * standby as we do not support sync to the cascading standby.
400
     *
401
     * However, failover enabled slots can be created during slot
402
     * synchronization because we need to retain the same values as the
403
     * remote slot.
404
     */
405
0
    if (RecoveryInProgress() && !IsSyncingReplicationSlots())
406
0
      ereport(ERROR,
407
0
          errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
408
0
          errmsg("cannot enable failover for a replication slot created on the standby"));
409
410
    /*
411
     * Do not allow users to create failover enabled temporary slots,
412
     * because temporary slots will not be synced to the standby.
413
     *
414
     * However, failover enabled temporary slots can be created during
415
     * slot synchronization. See the comments atop slotsync.c for details.
416
     */
417
0
    if (persistency == RS_TEMPORARY && !IsSyncingReplicationSlots())
418
0
      ereport(ERROR,
419
0
          errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
420
0
          errmsg("cannot enable failover for a temporary replication slot"));
421
0
  }
422
423
0
  INJECTION_POINT("replication-slot-create-begin", NULL);
424
425
  /*
426
   * If some other backend ran this code concurrently with us, we'd likely
427
   * both allocate the same slot, and that would be bad.  We'd also be at
428
   * risk of missing a name collision.  Also, we don't want to try to create
429
   * a new slot while somebody's busy cleaning up an old one, because we
430
   * might both be monkeying with the same directory.
431
   */
432
0
  LWLockAcquire(ReplicationSlotAllocationLock, LW_EXCLUSIVE);
433
434
  /*
435
   * Check for name collision (across the whole array), and identify an
436
   * allocatable slot (in the array slice specific to our current use case:
437
   * either general, or REPACK only).  We need to hold
438
   * ReplicationSlotControlLock in shared mode for this, so that nobody else
439
   * can change the in_use flags while we're looking at them.
440
   */
441
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
442
0
  startpoint = !repack ? 0 : max_replication_slots;
443
0
  endpoint = max_replication_slots + (repack ? max_repack_replication_slots : 0);
444
0
  for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
445
0
  {
446
0
    ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
447
448
0
    if (s->in_use && strcmp(name, NameStr(s->data.name)) == 0)
449
0
      ereport(ERROR,
450
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
451
0
           errmsg("replication slot \"%s\" already exists", name)));
452
453
0
    if (i >= startpoint && i < endpoint &&
454
0
      !s->in_use && slot == NULL)
455
0
      slot = s;
456
0
  }
457
0
  LWLockRelease(ReplicationSlotControlLock);
458
459
  /* If all slots are in use, we're out of luck. */
460
0
  if (slot == NULL)
461
0
    ereport(ERROR,
462
0
        (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
463
0
         errmsg("all replication slots are in use"),
464
0
         errhint("Free one or increase \"%s\".",
465
0
             repack ? "max_repack_replication_slots" : "max_replication_slots")));
466
467
  /*
468
   * Since this slot is not in use, nobody should be looking at any part of
469
   * it other than the in_use field unless they're trying to allocate it.
470
   * And since we hold ReplicationSlotAllocationLock, nobody except us can
471
   * be doing that.  So it's safe to initialize the slot.
472
   */
473
0
  Assert(!slot->in_use);
474
0
  Assert(slot->active_proc == INVALID_PROC_NUMBER);
475
476
  /* first initialize persistent data */
477
0
  memset(&slot->data, 0, sizeof(ReplicationSlotPersistentData));
478
0
  namestrcpy(&slot->data.name, name);
479
0
  slot->data.database = db_specific ? MyDatabaseId : InvalidOid;
480
0
  slot->data.persistency = persistency;
481
0
  slot->data.two_phase = two_phase;
482
0
  slot->data.two_phase_at = InvalidXLogRecPtr;
483
0
  slot->data.failover = failover;
484
0
  slot->data.synced = synced;
485
486
  /* and then data only present in shared memory */
487
0
  slot->just_dirtied = false;
488
0
  slot->dirty = false;
489
0
  slot->effective_xmin = InvalidTransactionId;
490
0
  slot->effective_catalog_xmin = InvalidTransactionId;
491
0
  slot->candidate_catalog_xmin = InvalidTransactionId;
492
0
  slot->candidate_xmin_lsn = InvalidXLogRecPtr;
493
0
  slot->candidate_restart_valid = InvalidXLogRecPtr;
494
0
  slot->candidate_restart_lsn = InvalidXLogRecPtr;
495
0
  slot->last_saved_confirmed_flush = InvalidXLogRecPtr;
496
0
  slot->last_saved_restart_lsn = InvalidXLogRecPtr;
497
0
  slot->inactive_since = 0;
498
0
  slot->slotsync_skip_reason = SS_SKIP_NONE;
499
500
  /*
501
   * Create the slot on disk.  We haven't actually marked the slot allocated
502
   * yet, so no special cleanup is required if this errors out.
503
   */
504
0
  CreateSlotOnDisk(slot);
505
506
  /*
507
   * We need to briefly prevent any other backend from iterating over the
508
   * slots while we flip the in_use flag. We also need to set the active
509
   * flag while holding the ControlLock as otherwise a concurrent
510
   * ReplicationSlotAcquire() could acquire the slot as well.
511
   */
512
0
  LWLockAcquire(ReplicationSlotControlLock, LW_EXCLUSIVE);
513
514
0
  slot->in_use = true;
515
516
  /* We can now mark the slot active, and that makes it our slot. */
517
0
  SpinLockAcquire(&slot->mutex);
518
0
  Assert(slot->active_proc == INVALID_PROC_NUMBER);
519
0
  slot->active_proc = MyProcNumber;
520
0
  SpinLockRelease(&slot->mutex);
521
0
  MyReplicationSlot = slot;
522
523
0
  LWLockRelease(ReplicationSlotControlLock);
524
525
  /*
526
   * Create statistics entry for the new logical slot. We don't collect any
527
   * stats for physical slots, so no need to create an entry for the same.
528
   * See ReplicationSlotDropPtr for why we need to do this before releasing
529
   * ReplicationSlotAllocationLock.
530
   */
531
0
  if (SlotIsLogical(slot))
532
0
    pgstat_create_replslot(slot);
533
534
  /*
535
   * Now that the slot has been marked as in_use and active, it's safe to
536
   * let somebody else try to allocate a slot.
537
   */
538
0
  LWLockRelease(ReplicationSlotAllocationLock);
539
540
  /* Let everybody know we've modified this slot */
541
0
  ConditionVariableBroadcast(&slot->active_cv);
542
0
}
543
544
/*
545
 * Search for the named replication slot.
546
 *
547
 * Return the replication slot if found, otherwise NULL.
548
 */
549
ReplicationSlot *
550
SearchNamedReplicationSlot(const char *name, bool need_lock)
551
0
{
552
0
  int     i;
553
0
  ReplicationSlot *slot = NULL;
554
555
0
  if (need_lock)
556
0
    LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
557
558
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
559
0
  {
560
0
    ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
561
562
0
    if (s->in_use && strcmp(name, NameStr(s->data.name)) == 0)
563
0
    {
564
0
      slot = s;
565
0
      break;
566
0
    }
567
0
  }
568
569
0
  if (need_lock)
570
0
    LWLockRelease(ReplicationSlotControlLock);
571
572
0
  return slot;
573
0
}
574
575
/*
576
 * Return the index of the replication slot in
577
 * ReplicationSlotCtl->replication_slots.
578
 *
579
 * This is mainly useful to have an efficient key for storing replication slot
580
 * stats.
581
 */
582
int
583
ReplicationSlotIndex(ReplicationSlot *slot)
584
0
{
585
0
  Assert(slot >= ReplicationSlotCtl->replication_slots &&
586
0
       slot < ReplicationSlotCtl->replication_slots +
587
0
       (max_replication_slots + max_repack_replication_slots));
588
589
0
  return slot - ReplicationSlotCtl->replication_slots;
590
0
}
591
592
/*
593
 * If the slot at 'index' is unused, return false. Otherwise 'name' is set to
594
 * the slot's name and true is returned.
595
 *
596
 * This likely is only useful for pgstat_replslot.c during shutdown, in other
597
 * cases there are obvious TOCTOU issues.
598
 */
599
bool
600
ReplicationSlotName(int index, Name name)
601
0
{
602
0
  ReplicationSlot *slot;
603
0
  bool    found;
604
605
0
  slot = &ReplicationSlotCtl->replication_slots[index];
606
607
  /*
608
   * Ensure that the slot cannot be dropped while we copy the name. Don't
609
   * need the spinlock as the name of an existing slot cannot change.
610
   */
611
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
612
0
  found = slot->in_use;
613
0
  if (slot->in_use)
614
0
    namestrcpy(name, NameStr(slot->data.name));
615
0
  LWLockRelease(ReplicationSlotControlLock);
616
617
0
  return found;
618
0
}
619
620
/*
621
 * Find a previously created slot and mark it as used by this process.
622
 *
623
 * An error is raised if nowait is true and the slot is currently in use. If
624
 * nowait is false, we sleep until the slot is released by the owning process.
625
 *
626
 * An error is raised if error_if_invalid is true and the slot is found to
627
 * be invalid. It should always be set to true, except when we are temporarily
628
 * acquiring the slot and don't intend to change it.
629
 */
630
void
631
ReplicationSlotAcquire(const char *name, bool nowait, bool error_if_invalid)
632
0
{
633
0
  ReplicationSlot *s;
634
0
  ProcNumber  active_proc;
635
0
  int     active_pid;
636
637
0
  Assert(name != NULL);
638
639
0
retry:
640
0
  Assert(MyReplicationSlot == NULL);
641
642
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
643
644
  /* Check if the slot exists with the given name. */
645
0
  s = SearchNamedReplicationSlot(name, false);
646
0
  if (s == NULL || !s->in_use)
647
0
  {
648
0
    LWLockRelease(ReplicationSlotControlLock);
649
650
0
    ereport(ERROR,
651
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
652
0
         errmsg("replication slot \"%s\" does not exist",
653
0
            name)));
654
0
  }
655
656
  /*
657
   * Do not allow users to acquire the reserved slot. This scenario may
658
   * occur if the launcher that owns the slot has terminated unexpectedly
659
   * due to an error, and a backend process attempts to reuse the slot.
660
   */
661
0
  if (!IsLogicalLauncher() && IsSlotForConflictCheck(name))
662
0
    ereport(ERROR,
663
0
        errcode(ERRCODE_UNDEFINED_OBJECT),
664
0
        errmsg("cannot acquire replication slot \"%s\"", name),
665
0
        errdetail("The slot is reserved for conflict detection and can only be acquired by logical replication launcher."));
666
667
  /*
668
   * This is the slot we want; check if it's active under some other
669
   * process.  In single user mode, we don't need this check.
670
   */
671
0
  if (IsUnderPostmaster)
672
0
  {
673
    /*
674
     * Get ready to sleep on the slot in case it is active.  (We may end
675
     * up not sleeping, but we don't want to do this while holding the
676
     * spinlock.)
677
     */
678
0
    if (!nowait)
679
0
      ConditionVariablePrepareToSleep(&s->active_cv);
680
681
    /*
682
     * It is important to reset the inactive_since under spinlock here to
683
     * avoid race conditions with slot invalidation. See comments related
684
     * to inactive_since in InvalidatePossiblyObsoleteSlot.
685
     */
686
0
    SpinLockAcquire(&s->mutex);
687
0
    if (s->active_proc == INVALID_PROC_NUMBER)
688
0
      s->active_proc = MyProcNumber;
689
0
    active_proc = s->active_proc;
690
0
    ReplicationSlotSetInactiveSince(s, 0, false);
691
0
    SpinLockRelease(&s->mutex);
692
0
  }
693
0
  else
694
0
  {
695
0
    s->active_proc = active_proc = MyProcNumber;
696
0
    ReplicationSlotSetInactiveSince(s, 0, true);
697
0
  }
698
0
  active_pid = GetPGProcByNumber(active_proc)->pid;
699
0
  LWLockRelease(ReplicationSlotControlLock);
700
701
  /*
702
   * If we found the slot but it's already active in another process, we
703
   * wait until the owning process signals us that it's been released, or
704
   * error out.
705
   */
706
0
  if (active_proc != MyProcNumber)
707
0
  {
708
0
    if (!nowait)
709
0
    {
710
      /* Wait here until we get signaled, and then restart */
711
0
      ConditionVariableSleep(&s->active_cv,
712
0
                   WAIT_EVENT_REPLICATION_SLOT_DROP);
713
0
      ConditionVariableCancelSleep();
714
0
      goto retry;
715
0
    }
716
717
0
    ereport(ERROR,
718
0
        (errcode(ERRCODE_OBJECT_IN_USE),
719
0
         errmsg("replication slot \"%s\" is active for PID %d",
720
0
            NameStr(s->data.name), active_pid)));
721
0
  }
722
0
  else if (!nowait)
723
0
    ConditionVariableCancelSleep(); /* no sleep needed after all */
724
725
  /* We made this slot active, so it's ours now. */
726
0
  MyReplicationSlot = s;
727
728
  /*
729
   * We need to check for invalidation after making the slot ours to avoid
730
   * the possible race condition with the checkpointer that can otherwise
731
   * invalidate the slot immediately after the check.
732
   */
733
0
  if (error_if_invalid && s->data.invalidated != RS_INVAL_NONE)
734
0
    ereport(ERROR,
735
0
        errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
736
0
        errmsg("can no longer access replication slot \"%s\"",
737
0
             NameStr(s->data.name)),
738
0
        errdetail("This replication slot has been invalidated due to \"%s\".",
739
0
              GetSlotInvalidationCauseName(s->data.invalidated)));
740
741
  /* Let everybody know we've modified this slot */
742
0
  ConditionVariableBroadcast(&s->active_cv);
743
744
  /*
745
   * The call to pgstat_acquire_replslot() protects against stats for a
746
   * different slot, from before a restart or such, being present during
747
   * pgstat_report_replslot().
748
   */
749
0
  if (SlotIsLogical(s))
750
0
    pgstat_acquire_replslot(s);
751
752
753
0
  if (am_walsender)
754
0
  {
755
0
    ereport(log_replication_commands ? LOG : DEBUG1,
756
0
        SlotIsLogical(s)
757
0
        ? errmsg("acquired logical replication slot \"%s\"",
758
0
             NameStr(s->data.name))
759
0
        : errmsg("acquired physical replication slot \"%s\"",
760
0
             NameStr(s->data.name)));
761
0
  }
762
0
}
763
764
/*
765
 * Release the replication slot that this backend considers to own.
766
 *
767
 * This or another backend can re-acquire the slot later.
768
 * Resources this slot requires will be preserved.
769
 */
770
void
771
ReplicationSlotRelease(void)
772
0
{
773
0
  ReplicationSlot *slot = MyReplicationSlot;
774
0
  char     *slotname = NULL; /* keep compiler quiet */
775
0
  bool    is_logical;
776
0
  TimestampTz now = 0;
777
778
0
  Assert(slot != NULL && slot->active_proc != INVALID_PROC_NUMBER);
779
780
0
  is_logical = SlotIsLogical(slot);
781
782
0
  if (am_walsender)
783
0
    slotname = pstrdup(NameStr(slot->data.name));
784
785
0
  if (slot->data.persistency == RS_EPHEMERAL)
786
0
  {
787
    /*
788
     * If slot is ephemeral, we drop it upon release, and request logical
789
     * decoding be disabled.
790
     */
791
0
    ReplicationSlotDropAcquired(is_logical);
792
0
  }
793
0
  else
794
0
  {
795
    /*
796
     * If slot needed to temporarily restrain both data and catalog xmin
797
     * to create the catalog snapshot, remove that temporary constraint.
798
     * Snapshots can only be exported while the initial snapshot is still
799
     * acquired.
800
     */
801
0
    if (!TransactionIdIsValid(slot->data.xmin) &&
802
0
      TransactionIdIsValid(slot->effective_xmin))
803
0
    {
804
0
      SpinLockAcquire(&slot->mutex);
805
0
      slot->effective_xmin = InvalidTransactionId;
806
0
      SpinLockRelease(&slot->mutex);
807
0
      ReplicationSlotsComputeRequiredXmin(false);
808
0
    }
809
810
    /*
811
     * Set the time since the slot has become inactive. We get the current
812
     * time beforehand to avoid system call while holding the spinlock.
813
     */
814
0
    now = GetCurrentTimestamp();
815
816
0
    if (slot->data.persistency == RS_PERSISTENT)
817
0
    {
818
      /*
819
       * Mark persistent slot inactive.  We're not freeing it, just
820
       * disconnecting, but wake up others that may be waiting for it.
821
       */
822
0
      SpinLockAcquire(&slot->mutex);
823
0
      slot->active_proc = INVALID_PROC_NUMBER;
824
0
      ReplicationSlotSetInactiveSince(slot, now, false);
825
0
      SpinLockRelease(&slot->mutex);
826
0
      ConditionVariableBroadcast(&slot->active_cv);
827
0
    }
828
0
    else
829
0
      ReplicationSlotSetInactiveSince(slot, now, true);
830
831
0
    MyReplicationSlot = NULL;
832
0
  }
833
834
  /* might not have been set when we've been a plain slot */
835
0
  LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
836
0
  MyProc->statusFlags &= ~PROC_IN_LOGICAL_DECODING;
837
0
  ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
838
0
  LWLockRelease(ProcArrayLock);
839
840
0
  if (am_walsender)
841
0
  {
842
0
    ereport(log_replication_commands ? LOG : DEBUG1,
843
0
        is_logical
844
0
        ? errmsg("released logical replication slot \"%s\"",
845
0
             slotname)
846
0
        : errmsg("released physical replication slot \"%s\"",
847
0
             slotname));
848
849
0
    pfree(slotname);
850
0
  }
851
0
}
852
853
/*
854
 * Cleanup temporary slots created in current session.
855
 *
856
 * Cleanup only synced temporary slots if 'synced_only' is true, else
857
 * cleanup all temporary slots.
858
 *
859
 * If it drops the last logical slot in the cluster, requests to disable
860
 * logical decoding.
861
 */
862
void
863
ReplicationSlotCleanup(bool synced_only)
864
0
{
865
0
  int     i;
866
0
  bool    found_valid_logicalslot;
867
0
  bool    dropped_logical = false;
868
869
0
  Assert(MyReplicationSlot == NULL);
870
871
0
restart:
872
0
  found_valid_logicalslot = false;
873
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
874
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
875
0
  {
876
0
    ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
877
878
0
    if (!s->in_use)
879
0
      continue;
880
881
0
    SpinLockAcquire(&s->mutex);
882
883
0
    found_valid_logicalslot |=
884
0
      (SlotIsLogical(s) && s->data.invalidated == RS_INVAL_NONE);
885
886
0
    if ((s->active_proc == MyProcNumber &&
887
0
       (!synced_only || s->data.synced)))
888
0
    {
889
0
      Assert(s->data.persistency == RS_TEMPORARY);
890
0
      SpinLockRelease(&s->mutex);
891
0
      LWLockRelease(ReplicationSlotControlLock);  /* avoid deadlock */
892
893
0
      if (SlotIsLogical(s))
894
0
        dropped_logical = true;
895
896
0
      ReplicationSlotDropPtr(s);
897
898
0
      ConditionVariableBroadcast(&s->active_cv);
899
0
      goto restart;
900
0
    }
901
0
    else
902
0
      SpinLockRelease(&s->mutex);
903
0
  }
904
905
0
  LWLockRelease(ReplicationSlotControlLock);
906
907
0
  if (dropped_logical && !found_valid_logicalslot)
908
0
    RequestDisableLogicalDecoding();
909
0
}
910
911
/*
912
 * Permanently drop the replication slot identified by the passed-in name.
913
 *
914
 * If this is a logical slot, request that logical decoding be disabled.
915
 */
916
void
917
ReplicationSlotDrop(const char *name, bool nowait)
918
0
{
919
0
  ReplicationSlotAcquire(name, nowait, false);
920
921
  /*
922
   * Do not allow users to drop the slots which are currently being synced
923
   * from the primary to the standby.
924
   */
925
0
  if (RecoveryInProgress() && MyReplicationSlot->data.synced)
926
0
    ereport(ERROR,
927
0
        errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
928
0
        errmsg("cannot drop replication slot \"%s\"", name),
929
0
        errdetail("This replication slot is being synchronized from the primary server."));
930
931
0
  ReplicationSlotDropAcquired(SlotIsLogical(MyReplicationSlot));
932
0
}
933
934
/*
935
 * Change the definition of the slot identified by the specified name.
936
 *
937
 * Altering the two_phase property of a slot requires caution on the
938
 * client-side. Enabling it at any random point during decoding has the
939
 * risk that transactions prepared before this change may be skipped by
940
 * the decoder, leading to missing prepare records on the client. So, we
941
 * enable it for subscription related slots only once the initial tablesync
942
 * is finished. See comments atop worker.c. Disabling it is safe only when
943
 * there are no pending prepared transaction, otherwise, the changes of
944
 * already prepared transactions can be replicated again along with their
945
 * corresponding commit leading to duplicate data or errors.
946
 */
947
void
948
ReplicationSlotAlter(const char *name, const bool *failover,
949
           const bool *two_phase)
950
0
{
951
0
  bool    update_slot = false;
952
953
0
  Assert(MyReplicationSlot == NULL);
954
0
  Assert(failover || two_phase);
955
956
0
  ReplicationSlotAcquire(name, false, true);
957
958
0
  if (SlotIsPhysical(MyReplicationSlot))
959
0
    ereport(ERROR,
960
0
        errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
961
0
        errmsg("cannot use %s with a physical replication slot",
962
0
             "ALTER_REPLICATION_SLOT"));
963
964
0
  if (RecoveryInProgress())
965
0
  {
966
    /*
967
     * Do not allow users to alter the slots which are currently being
968
     * synced from the primary to the standby.
969
     */
970
0
    if (MyReplicationSlot->data.synced)
971
0
      ereport(ERROR,
972
0
          errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
973
0
          errmsg("cannot alter replication slot \"%s\"", name),
974
0
          errdetail("This replication slot is being synchronized from the primary server."));
975
976
    /*
977
     * Do not allow users to enable failover on the standby as we do not
978
     * support sync to the cascading standby.
979
     */
980
0
    if (failover && *failover)
981
0
      ereport(ERROR,
982
0
          errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
983
0
          errmsg("cannot enable failover for a replication slot"
984
0
               " on the standby"));
985
0
  }
986
987
0
  if (failover)
988
0
  {
989
    /*
990
     * Do not allow users to enable failover for temporary slots as we do
991
     * not support syncing temporary slots to the standby.
992
     */
993
0
    if (*failover && MyReplicationSlot->data.persistency == RS_TEMPORARY)
994
0
      ereport(ERROR,
995
0
          errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
996
0
          errmsg("cannot enable failover for a temporary replication slot"));
997
998
0
    if (MyReplicationSlot->data.failover != *failover)
999
0
    {
1000
0
      SpinLockAcquire(&MyReplicationSlot->mutex);
1001
0
      MyReplicationSlot->data.failover = *failover;
1002
0
      SpinLockRelease(&MyReplicationSlot->mutex);
1003
1004
0
      update_slot = true;
1005
0
    }
1006
0
  }
1007
1008
0
  if (two_phase && MyReplicationSlot->data.two_phase != *two_phase)
1009
0
  {
1010
0
    SpinLockAcquire(&MyReplicationSlot->mutex);
1011
0
    MyReplicationSlot->data.two_phase = *two_phase;
1012
0
    SpinLockRelease(&MyReplicationSlot->mutex);
1013
1014
0
    update_slot = true;
1015
0
  }
1016
1017
0
  if (update_slot)
1018
0
  {
1019
0
    ReplicationSlotMarkDirty();
1020
0
    ReplicationSlotSave();
1021
0
  }
1022
1023
0
  ReplicationSlotRelease();
1024
0
}
1025
1026
/*
1027
 * Permanently drop the currently acquired replication slot.
1028
 *
1029
 * If caller requests it, have checkpointer attempt to disable logical
1030
 * decoding.  Obviously, this should only be done if the slot is logical.
1031
 */
1032
void
1033
ReplicationSlotDropAcquired(bool try_disable)
1034
0
{
1035
0
  ReplicationSlot *slot;
1036
1037
0
  Assert(MyReplicationSlot != NULL);
1038
0
  slot = MyReplicationSlot;
1039
1040
  /* Can only disable logical decoding if slot is logical */
1041
0
  Assert(!try_disable || SlotIsLogical(slot));
1042
1043
  /* slot isn't acquired anymore */
1044
0
  MyReplicationSlot = NULL;
1045
1046
0
  ReplicationSlotDropPtr(slot);
1047
1048
0
  if (try_disable)
1049
0
    RequestDisableLogicalDecoding();
1050
0
}
1051
1052
/*
1053
 * Permanently drop the replication slot which will be released by the point
1054
 * this function returns.
1055
 */
1056
static void
1057
ReplicationSlotDropPtr(ReplicationSlot *slot)
1058
0
{
1059
0
  char    path[MAXPGPATH];
1060
0
  char    tmppath[MAXPGPATH];
1061
1062
  /*
1063
   * If some other backend ran this code concurrently with us, we might try
1064
   * to delete a slot with a certain name while someone else was trying to
1065
   * create a slot with the same name.
1066
   */
1067
0
  LWLockAcquire(ReplicationSlotAllocationLock, LW_EXCLUSIVE);
1068
1069
  /* Generate pathnames. */
1070
0
  sprintf(path, "%s/%s", PG_REPLSLOT_DIR, NameStr(slot->data.name));
1071
0
  sprintf(tmppath, "%s/%s.tmp", PG_REPLSLOT_DIR, NameStr(slot->data.name));
1072
1073
  /*
1074
   * Rename the slot directory on disk, so that we'll no longer recognize
1075
   * this as a valid slot.  Note that if this fails, we've got to mark the
1076
   * slot inactive before bailing out.  If we're dropping an ephemeral or a
1077
   * temporary slot, we better never fail hard as the caller won't expect
1078
   * the slot to survive and this might get called during error handling.
1079
   */
1080
0
  if (rename(path, tmppath) == 0)
1081
0
  {
1082
    /*
1083
     * We need to fsync() the directory we just renamed and its parent to
1084
     * make sure that our changes are on disk in a crash-safe fashion.  If
1085
     * fsync() fails, we can't be sure whether the changes are on disk or
1086
     * not.  For now, we handle that by panicking;
1087
     * StartupReplicationSlots() will try to straighten it out after
1088
     * restart.
1089
     */
1090
0
    START_CRIT_SECTION();
1091
0
    fsync_fname(tmppath, true);
1092
0
    fsync_fname(PG_REPLSLOT_DIR, true);
1093
0
    END_CRIT_SECTION();
1094
0
  }
1095
0
  else
1096
0
  {
1097
0
    bool    fail_softly = slot->data.persistency != RS_PERSISTENT;
1098
1099
0
    SpinLockAcquire(&slot->mutex);
1100
0
    slot->active_proc = INVALID_PROC_NUMBER;
1101
0
    SpinLockRelease(&slot->mutex);
1102
1103
    /* wake up anyone waiting on this slot */
1104
0
    ConditionVariableBroadcast(&slot->active_cv);
1105
1106
0
    ereport(fail_softly ? WARNING : ERROR,
1107
0
        (errcode_for_file_access(),
1108
0
         errmsg("could not rename file \"%s\" to \"%s\": %m",
1109
0
            path, tmppath)));
1110
0
  }
1111
1112
  /*
1113
   * The slot is definitely gone.  Lock out concurrent scans of the array
1114
   * long enough to kill it.  It's OK to clear the active PID here without
1115
   * grabbing the mutex because nobody else can be scanning the array here,
1116
   * and nobody can be attached to this slot and thus access it without
1117
   * scanning the array.
1118
   *
1119
   * Also wake up processes waiting for it.
1120
   */
1121
0
  LWLockAcquire(ReplicationSlotControlLock, LW_EXCLUSIVE);
1122
0
  slot->active_proc = INVALID_PROC_NUMBER;
1123
0
  slot->in_use = false;
1124
0
  LWLockRelease(ReplicationSlotControlLock);
1125
0
  ConditionVariableBroadcast(&slot->active_cv);
1126
1127
  /*
1128
   * Slot is dead and doesn't prevent resource removal anymore, recompute
1129
   * limits.
1130
   */
1131
0
  ReplicationSlotsComputeRequiredXmin(false);
1132
0
  ReplicationSlotsComputeRequiredLSN();
1133
1134
  /*
1135
   * If removing the directory fails, the worst thing that will happen is
1136
   * that the user won't be able to create a new slot with the same name
1137
   * until the next server restart.  We warn about it, but that's all.
1138
   */
1139
0
  if (!rmtree(tmppath, true))
1140
0
    ereport(WARNING,
1141
0
        (errmsg("could not remove directory \"%s\"", tmppath)));
1142
1143
  /*
1144
   * Drop the statistics entry for the replication slot.  Do this while
1145
   * holding ReplicationSlotAllocationLock so that we don't drop a
1146
   * statistics entry for another slot with the same name just created in
1147
   * another session.
1148
   */
1149
0
  if (SlotIsLogical(slot))
1150
0
    pgstat_drop_replslot(slot);
1151
1152
  /*
1153
   * We release this at the very end, so that nobody starts trying to create
1154
   * a slot while we're still cleaning up the detritus of the old one.
1155
   */
1156
0
  LWLockRelease(ReplicationSlotAllocationLock);
1157
0
}
1158
1159
/*
1160
 * Serialize the currently acquired slot's state from memory to disk, thereby
1161
 * guaranteeing the current state will survive a crash.
1162
 */
1163
void
1164
ReplicationSlotSave(void)
1165
0
{
1166
0
  char    path[MAXPGPATH];
1167
1168
0
  Assert(MyReplicationSlot != NULL);
1169
1170
0
  sprintf(path, "%s/%s", PG_REPLSLOT_DIR, NameStr(MyReplicationSlot->data.name));
1171
0
  SaveSlotToPath(MyReplicationSlot, path, ERROR);
1172
0
}
1173
1174
/*
1175
 * Signal that it would be useful if the currently acquired slot would be
1176
 * flushed out to disk.
1177
 *
1178
 * Note that the actual flush to disk can be delayed for a long time, if
1179
 * required for correctness explicitly do a ReplicationSlotSave().
1180
 */
1181
void
1182
ReplicationSlotMarkDirty(void)
1183
0
{
1184
0
  ReplicationSlot *slot = MyReplicationSlot;
1185
1186
0
  Assert(MyReplicationSlot != NULL);
1187
1188
0
  SpinLockAcquire(&slot->mutex);
1189
0
  MyReplicationSlot->just_dirtied = true;
1190
0
  MyReplicationSlot->dirty = true;
1191
0
  SpinLockRelease(&slot->mutex);
1192
0
}
1193
1194
/*
1195
 * Convert a slot that's marked as RS_EPHEMERAL or RS_TEMPORARY to a
1196
 * RS_PERSISTENT slot, guaranteeing it will be there after an eventual crash.
1197
 */
1198
void
1199
ReplicationSlotPersist(void)
1200
0
{
1201
0
  ReplicationSlot *slot = MyReplicationSlot;
1202
1203
0
  Assert(slot != NULL);
1204
0
  Assert(slot->data.persistency != RS_PERSISTENT);
1205
1206
0
  SpinLockAcquire(&slot->mutex);
1207
0
  slot->data.persistency = RS_PERSISTENT;
1208
0
  SpinLockRelease(&slot->mutex);
1209
1210
0
  ReplicationSlotMarkDirty();
1211
0
  ReplicationSlotSave();
1212
0
}
1213
1214
/*
1215
 * Compute the oldest xmin across all slots and store it in the ProcArray.
1216
 *
1217
 * If already_locked is true, both the ReplicationSlotControlLock and the
1218
 * ProcArrayLock have already been acquired exclusively. It is crucial that the
1219
 * caller first acquires the ReplicationSlotControlLock, followed by the
1220
 * ProcArrayLock, to prevent any undetectable deadlocks since this function
1221
 * acquires them in that order.
1222
 */
1223
void
1224
ReplicationSlotsComputeRequiredXmin(bool already_locked)
1225
0
{
1226
0
  int     i;
1227
0
  TransactionId agg_xmin = InvalidTransactionId;
1228
0
  TransactionId agg_catalog_xmin = InvalidTransactionId;
1229
1230
0
  Assert(ReplicationSlotCtl != NULL);
1231
0
  Assert(!already_locked ||
1232
0
       (LWLockHeldByMeInMode(ReplicationSlotControlLock, LW_EXCLUSIVE) &&
1233
0
      LWLockHeldByMeInMode(ProcArrayLock, LW_EXCLUSIVE)));
1234
1235
  /*
1236
   * Hold the ReplicationSlotControlLock until after updating the slot xmin
1237
   * values, so no backend updates the initial xmin for newly created slot
1238
   * concurrently. A shared lock is used here to minimize lock contention,
1239
   * especially when many slots exist and advancements occur frequently.
1240
   * This is safe since an exclusive lock is taken during initial slot xmin
1241
   * update in slot creation.
1242
   *
1243
   * One might think that we can hold the ProcArrayLock exclusively and
1244
   * update the slot xmin values, but it could increase lock contention on
1245
   * the ProcArrayLock, which is not great since this function can be called
1246
   * at non-negligible frequency.
1247
   *
1248
   * Concurrent invocation of this function may cause the computed slot xmin
1249
   * to regress. However, this is harmless because tuples prior to the most
1250
   * recent xmin are no longer useful once advancement occurs (see
1251
   * LogicalConfirmReceivedLocation where the slot's xmin value is flushed
1252
   * before updating the effective_xmin). Thus, such regression merely
1253
   * prevents VACUUM from prematurely removing tuples without causing the
1254
   * early deletion of required data.
1255
   */
1256
0
  if (!already_locked)
1257
0
    LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
1258
1259
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
1260
0
  {
1261
0
    ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
1262
0
    TransactionId effective_xmin;
1263
0
    TransactionId effective_catalog_xmin;
1264
0
    bool    invalidated;
1265
1266
0
    if (!s->in_use)
1267
0
      continue;
1268
1269
0
    SpinLockAcquire(&s->mutex);
1270
0
    effective_xmin = s->effective_xmin;
1271
0
    effective_catalog_xmin = s->effective_catalog_xmin;
1272
0
    invalidated = s->data.invalidated != RS_INVAL_NONE;
1273
0
    SpinLockRelease(&s->mutex);
1274
1275
    /* invalidated slots need not apply */
1276
0
    if (invalidated)
1277
0
      continue;
1278
1279
    /* check the data xmin */
1280
0
    if (TransactionIdIsValid(effective_xmin) &&
1281
0
      (!TransactionIdIsValid(agg_xmin) ||
1282
0
       TransactionIdPrecedes(effective_xmin, agg_xmin)))
1283
0
      agg_xmin = effective_xmin;
1284
1285
    /* check the catalog xmin */
1286
0
    if (TransactionIdIsValid(effective_catalog_xmin) &&
1287
0
      (!TransactionIdIsValid(agg_catalog_xmin) ||
1288
0
       TransactionIdPrecedes(effective_catalog_xmin, agg_catalog_xmin)))
1289
0
      agg_catalog_xmin = effective_catalog_xmin;
1290
0
  }
1291
1292
0
  ProcArraySetReplicationSlotXmin(agg_xmin, agg_catalog_xmin, already_locked);
1293
1294
0
  if (!already_locked)
1295
0
    LWLockRelease(ReplicationSlotControlLock);
1296
0
}
1297
1298
/*
1299
 * Compute the oldest restart LSN across all slots and inform xlog module.
1300
 *
1301
 * Note: while max_slot_wal_keep_size is theoretically relevant for this
1302
 * purpose, we don't try to account for that, because this module doesn't
1303
 * know what to compare against.
1304
 */
1305
void
1306
ReplicationSlotsComputeRequiredLSN(void)
1307
0
{
1308
0
  int     i;
1309
0
  XLogRecPtr  min_required = InvalidXLogRecPtr;
1310
1311
0
  Assert(ReplicationSlotCtl != NULL);
1312
1313
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
1314
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
1315
0
  {
1316
0
    ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
1317
0
    XLogRecPtr  restart_lsn;
1318
0
    XLogRecPtr  last_saved_restart_lsn;
1319
0
    bool    invalidated;
1320
0
    ReplicationSlotPersistency persistency;
1321
1322
0
    if (!s->in_use)
1323
0
      continue;
1324
1325
0
    SpinLockAcquire(&s->mutex);
1326
0
    persistency = s->data.persistency;
1327
0
    restart_lsn = s->data.restart_lsn;
1328
0
    invalidated = s->data.invalidated != RS_INVAL_NONE;
1329
0
    last_saved_restart_lsn = s->last_saved_restart_lsn;
1330
0
    SpinLockRelease(&s->mutex);
1331
1332
    /* invalidated slots need not apply */
1333
0
    if (invalidated)
1334
0
      continue;
1335
1336
    /*
1337
     * For persistent slot use last_saved_restart_lsn to compute the
1338
     * oldest LSN for removal of WAL segments.  The segments between
1339
     * last_saved_restart_lsn and restart_lsn might be needed by a
1340
     * persistent slot in the case of database crash.  Non-persistent
1341
     * slots can't survive the database crash, so we don't care about
1342
     * last_saved_restart_lsn for them.
1343
     */
1344
0
    if (persistency == RS_PERSISTENT)
1345
0
    {
1346
0
      if (XLogRecPtrIsValid(last_saved_restart_lsn) &&
1347
0
        restart_lsn > last_saved_restart_lsn)
1348
0
      {
1349
0
        restart_lsn = last_saved_restart_lsn;
1350
0
      }
1351
0
    }
1352
1353
0
    if (XLogRecPtrIsValid(restart_lsn) &&
1354
0
      (!XLogRecPtrIsValid(min_required) ||
1355
0
       restart_lsn < min_required))
1356
0
      min_required = restart_lsn;
1357
0
  }
1358
0
  LWLockRelease(ReplicationSlotControlLock);
1359
1360
0
  XLogSetReplicationSlotMinimumLSN(min_required);
1361
0
}
1362
1363
/*
1364
 * Compute the oldest WAL LSN required by *logical* decoding slots..
1365
 *
1366
 * Returns InvalidXLogRecPtr if logical decoding is disabled or no logical
1367
 * slots exist.
1368
 *
1369
 * NB: this returns a value >= ReplicationSlotsComputeRequiredLSN(), since it
1370
 * ignores physical replication slots.
1371
 *
1372
 * The results aren't required frequently, so we don't maintain a precomputed
1373
 * value like we do for ComputeRequiredLSN() and ComputeRequiredXmin().
1374
 */
1375
XLogRecPtr
1376
ReplicationSlotsComputeLogicalRestartLSN(void)
1377
0
{
1378
0
  XLogRecPtr  result = InvalidXLogRecPtr;
1379
0
  int     i;
1380
1381
0
  if (max_replication_slots + max_repack_replication_slots <= 0)
1382
0
    return InvalidXLogRecPtr;
1383
1384
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
1385
1386
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
1387
0
  {
1388
0
    ReplicationSlot *s;
1389
0
    XLogRecPtr  restart_lsn;
1390
0
    XLogRecPtr  last_saved_restart_lsn;
1391
0
    bool    invalidated;
1392
0
    ReplicationSlotPersistency persistency;
1393
1394
0
    s = &ReplicationSlotCtl->replication_slots[i];
1395
1396
    /* cannot change while ReplicationSlotCtlLock is held */
1397
0
    if (!s->in_use)
1398
0
      continue;
1399
1400
    /* we're only interested in logical slots */
1401
0
    if (!SlotIsLogical(s))
1402
0
      continue;
1403
1404
    /* read once, it's ok if it increases while we're checking */
1405
0
    SpinLockAcquire(&s->mutex);
1406
0
    persistency = s->data.persistency;
1407
0
    restart_lsn = s->data.restart_lsn;
1408
0
    invalidated = s->data.invalidated != RS_INVAL_NONE;
1409
0
    last_saved_restart_lsn = s->last_saved_restart_lsn;
1410
0
    SpinLockRelease(&s->mutex);
1411
1412
    /* invalidated slots need not apply */
1413
0
    if (invalidated)
1414
0
      continue;
1415
1416
    /*
1417
     * For persistent slot use last_saved_restart_lsn to compute the
1418
     * oldest LSN for removal of WAL segments.  The segments between
1419
     * last_saved_restart_lsn and restart_lsn might be needed by a
1420
     * persistent slot in the case of database crash.  Non-persistent
1421
     * slots can't survive the database crash, so we don't care about
1422
     * last_saved_restart_lsn for them.
1423
     */
1424
0
    if (persistency == RS_PERSISTENT)
1425
0
    {
1426
0
      if (XLogRecPtrIsValid(last_saved_restart_lsn) &&
1427
0
        restart_lsn > last_saved_restart_lsn)
1428
0
      {
1429
0
        restart_lsn = last_saved_restart_lsn;
1430
0
      }
1431
0
    }
1432
1433
0
    if (!XLogRecPtrIsValid(restart_lsn))
1434
0
      continue;
1435
1436
0
    if (!XLogRecPtrIsValid(result) ||
1437
0
      restart_lsn < result)
1438
0
      result = restart_lsn;
1439
0
  }
1440
1441
0
  LWLockRelease(ReplicationSlotControlLock);
1442
1443
0
  return result;
1444
0
}
1445
1446
/*
1447
 * ReplicationSlotsCountDBSlots -- count the number of slots that refer to the
1448
 * passed database oid.
1449
 *
1450
 * Returns true if there are any slots referencing the database. *nslots will
1451
 * be set to the absolute number of slots in the database, *nactive to ones
1452
 * currently active.
1453
 */
1454
bool
1455
ReplicationSlotsCountDBSlots(Oid dboid, int *nslots, int *nactive)
1456
0
{
1457
0
  int     i;
1458
1459
0
  *nslots = *nactive = 0;
1460
1461
0
  if (max_replication_slots + max_repack_replication_slots <= 0)
1462
0
    return false;
1463
1464
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
1465
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
1466
0
  {
1467
0
    ReplicationSlot *s;
1468
1469
0
    s = &ReplicationSlotCtl->replication_slots[i];
1470
1471
    /* cannot change while ReplicationSlotCtlLock is held */
1472
0
    if (!s->in_use)
1473
0
      continue;
1474
1475
    /* only logical slots are database specific, skip */
1476
0
    if (!SlotIsLogical(s))
1477
0
      continue;
1478
1479
    /* not our database, skip */
1480
0
    if (s->data.database != dboid)
1481
0
      continue;
1482
1483
    /* NB: intentionally counting invalidated slots */
1484
1485
    /* count slots with spinlock held */
1486
0
    SpinLockAcquire(&s->mutex);
1487
0
    (*nslots)++;
1488
0
    if (s->active_proc != INVALID_PROC_NUMBER)
1489
0
      (*nactive)++;
1490
0
    SpinLockRelease(&s->mutex);
1491
0
  }
1492
0
  LWLockRelease(ReplicationSlotControlLock);
1493
1494
0
  if (*nslots > 0)
1495
0
    return true;
1496
0
  return false;
1497
0
}
1498
1499
/*
1500
 * ReplicationSlotsDropDBSlots -- Drop all db-specific slots relating to the
1501
 * passed database oid. The caller should hold an exclusive lock on the
1502
 * pg_database oid for the database to prevent creation of new slots on the db
1503
 * or replay from existing slots.
1504
 *
1505
 * Another session that concurrently acquires an existing slot on the target DB
1506
 * (most likely to drop it) may cause this function to ERROR. If that happens
1507
 * it may have dropped some but not all slots.
1508
 *
1509
 * This routine isn't as efficient as it could be - but we don't drop
1510
 * databases often, especially databases with lots of slots.
1511
 *
1512
 * If the last logical slot in the cluster is dropped, request to disable
1513
 * logical decoding.
1514
 */
1515
void
1516
ReplicationSlotsDropDBSlots(Oid dboid)
1517
0
{
1518
0
  int     i;
1519
0
  bool    found_valid_logicalslot;
1520
0
  bool    dropped = false;
1521
1522
0
  if (max_replication_slots + max_repack_replication_slots <= 0)
1523
0
    return;
1524
1525
0
restart:
1526
0
  found_valid_logicalslot = false;
1527
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
1528
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
1529
0
  {
1530
0
    ReplicationSlot *s;
1531
0
    char     *slotname;
1532
0
    ProcNumber  active_proc;
1533
1534
0
    s = &ReplicationSlotCtl->replication_slots[i];
1535
1536
    /* cannot change while ReplicationSlotCtlLock is held */
1537
0
    if (!s->in_use)
1538
0
      continue;
1539
1540
    /* only logical slots are database specific, skip */
1541
0
    if (!SlotIsLogical(s))
1542
0
      continue;
1543
1544
    /*
1545
     * Check logical slots on other databases too so we can disable
1546
     * logical decoding only if no slots in the cluster.
1547
     */
1548
0
    SpinLockAcquire(&s->mutex);
1549
0
    found_valid_logicalslot |= (s->data.invalidated == RS_INVAL_NONE);
1550
0
    SpinLockRelease(&s->mutex);
1551
1552
    /* not our database, skip */
1553
0
    if (s->data.database != dboid)
1554
0
      continue;
1555
1556
    /* NB: intentionally including invalidated slots to drop */
1557
1558
    /* acquire slot, so ReplicationSlotDropAcquired can be reused  */
1559
0
    SpinLockAcquire(&s->mutex);
1560
    /* can't change while ReplicationSlotControlLock is held */
1561
0
    slotname = NameStr(s->data.name);
1562
0
    active_proc = s->active_proc;
1563
0
    if (active_proc == INVALID_PROC_NUMBER)
1564
0
    {
1565
0
      MyReplicationSlot = s;
1566
0
      s->active_proc = MyProcNumber;
1567
0
    }
1568
0
    SpinLockRelease(&s->mutex);
1569
1570
    /*
1571
     * Even though we hold an exclusive lock on the database object a
1572
     * logical slot for that DB can still be active, e.g. if it's
1573
     * concurrently being dropped by a backend connected to another DB.
1574
     *
1575
     * That's fairly unlikely in practice, so we'll just bail out.
1576
     *
1577
     * The slot sync worker holds a shared lock on the database before
1578
     * operating on synced logical slots to avoid conflict with the drop
1579
     * happening here. The persistent synced slots are thus safe but there
1580
     * is a possibility that the slot sync worker has created a temporary
1581
     * slot (which stays active even on release) and we are trying to drop
1582
     * that here. In practice, the chances of hitting this scenario are
1583
     * less as during slot synchronization, the temporary slot is
1584
     * immediately converted to persistent and thus is safe due to the
1585
     * shared lock taken on the database. So, we'll just bail out in such
1586
     * a case.
1587
     *
1588
     * XXX: We can consider shutting down the slot sync worker before
1589
     * trying to drop synced temporary slots here.
1590
     */
1591
0
    if (active_proc != INVALID_PROC_NUMBER)
1592
0
      ereport(ERROR,
1593
0
          (errcode(ERRCODE_OBJECT_IN_USE),
1594
0
           errmsg("replication slot \"%s\" is active for PID %d",
1595
0
              slotname, GetPGProcByNumber(active_proc)->pid)));
1596
1597
    /*
1598
     * To avoid duplicating ReplicationSlotDropAcquired() and to avoid
1599
     * holding ReplicationSlotControlLock over filesystem operations,
1600
     * release ReplicationSlotControlLock and use
1601
     * ReplicationSlotDropAcquired.
1602
     *
1603
     * As that means the set of slots could change, restart scan from the
1604
     * beginning each time we release the lock.
1605
     */
1606
0
    LWLockRelease(ReplicationSlotControlLock);
1607
0
    ReplicationSlotDropAcquired(false);
1608
0
    dropped = true;
1609
0
    goto restart;
1610
0
  }
1611
0
  LWLockRelease(ReplicationSlotControlLock);
1612
1613
0
  if (dropped && !found_valid_logicalslot)
1614
0
    RequestDisableLogicalDecoding();
1615
0
}
1616
1617
/*
1618
 * Returns true if there is at least one in-use valid logical replication slot.
1619
 */
1620
bool
1621
CheckLogicalSlotExists(void)
1622
0
{
1623
0
  bool    found = false;
1624
1625
0
  if (max_replication_slots + max_repack_replication_slots <= 0)
1626
0
    return false;
1627
1628
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
1629
0
  for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
1630
0
  {
1631
0
    ReplicationSlot *s;
1632
0
    bool    invalidated;
1633
1634
0
    s = &ReplicationSlotCtl->replication_slots[i];
1635
1636
    /* cannot change while ReplicationSlotCtlLock is held */
1637
0
    if (!s->in_use)
1638
0
      continue;
1639
1640
0
    if (SlotIsPhysical(s))
1641
0
      continue;
1642
1643
0
    SpinLockAcquire(&s->mutex);
1644
0
    invalidated = s->data.invalidated != RS_INVAL_NONE;
1645
0
    SpinLockRelease(&s->mutex);
1646
1647
0
    if (invalidated)
1648
0
      continue;
1649
1650
0
    found = true;
1651
0
    break;
1652
0
  }
1653
0
  LWLockRelease(ReplicationSlotControlLock);
1654
1655
0
  return found;
1656
0
}
1657
1658
/*
1659
 * Check whether the server's configuration supports using replication
1660
 * slots.
1661
 */
1662
void
1663
CheckSlotRequirements(bool repack)
1664
0
{
1665
  /*
1666
   * NB: Adding a new requirement likely means that RestoreSlotFromDisk()
1667
   * needs the same check.
1668
   */
1669
1670
0
  if (!repack && max_replication_slots == 0)
1671
0
    ereport(ERROR,
1672
0
        errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1673
0
        errmsg("replication slots can only be used if \"%s\" > 0",
1674
0
             "max_replication_slots"));
1675
1676
0
  if (repack && max_repack_replication_slots == 0)
1677
0
    ereport(ERROR,
1678
0
        errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1679
0
        errmsg("REPACK can only be used if \"%s\" > 0",
1680
0
             "max_repack_replication_slots"));
1681
1682
0
  if (wal_level < WAL_LEVEL_REPLICA)
1683
0
    ereport(ERROR,
1684
0
        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1685
0
         errmsg("replication slots can only be used if \"wal_level\" >= \"replica\"")));
1686
0
}
1687
1688
/*
1689
 * Check whether the user has privilege to use replication slots.
1690
 */
1691
void
1692
CheckSlotPermissions(void)
1693
0
{
1694
0
  if (!has_rolreplication(GetUserId()))
1695
0
    ereport(ERROR,
1696
0
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1697
0
         errmsg("permission denied to use replication slots"),
1698
0
         errdetail("Only roles with the %s attribute may use replication slots.",
1699
0
               "REPLICATION")));
1700
0
}
1701
1702
/*
1703
 * Reserve WAL for the currently active slot.
1704
 *
1705
 * Compute and set restart_lsn in a manner that's appropriate for the type of
1706
 * the slot and concurrency safe.
1707
 */
1708
void
1709
ReplicationSlotReserveWal(void)
1710
0
{
1711
0
  ReplicationSlot *slot = MyReplicationSlot;
1712
0
  XLogSegNo segno;
1713
0
  XLogRecPtr  restart_lsn;
1714
1715
0
  Assert(slot != NULL);
1716
0
  Assert(!XLogRecPtrIsValid(slot->data.restart_lsn));
1717
0
  Assert(!XLogRecPtrIsValid(slot->last_saved_restart_lsn));
1718
1719
  /*
1720
   * The replication slot mechanism is used to prevent the removal of
1721
   * required WAL.
1722
   *
1723
   * Acquire an exclusive lock to prevent the checkpoint process from
1724
   * concurrently computing the minimum slot LSN (see
1725
   * CheckPointReplicationSlots). This ensures that the WAL reserved for
1726
   * replication cannot be removed during a checkpoint.
1727
   *
1728
   * The mechanism is reliable because if WAL reservation occurs first, the
1729
   * checkpoint must wait for the restart_lsn update before determining the
1730
   * minimum non-removable LSN. On the other hand, if the checkpoint happens
1731
   * first, subsequent WAL reservations will select positions at or beyond
1732
   * the redo pointer of that checkpoint.
1733
   */
1734
0
  LWLockAcquire(ReplicationSlotAllocationLock, LW_EXCLUSIVE);
1735
1736
  /*
1737
   * For logical slots log a standby snapshot and start logical decoding at
1738
   * exactly that position. That allows the slot to start up more quickly.
1739
   * But on a standby we cannot do WAL writes, so just use the replay
1740
   * pointer; effectively, an attempt to create a logical slot on standby
1741
   * will cause it to wait for an xl_running_xact record to be logged
1742
   * independently on the primary, so that a snapshot can be built using the
1743
   * record.
1744
   *
1745
   * None of this is needed (or indeed helpful) for physical slots as
1746
   * they'll start replay at the last logged checkpoint anyway. Instead,
1747
   * return the location of the last redo LSN, where a base backup has to
1748
   * start replay at.
1749
   */
1750
0
  if (SlotIsPhysical(slot))
1751
0
    restart_lsn = GetRedoRecPtr();
1752
0
  else if (RecoveryInProgress())
1753
0
    restart_lsn = GetXLogReplayRecPtr(NULL);
1754
0
  else
1755
0
    restart_lsn = GetXLogInsertRecPtr();
1756
1757
0
  SpinLockAcquire(&slot->mutex);
1758
0
  slot->data.restart_lsn = restart_lsn;
1759
0
  SpinLockRelease(&slot->mutex);
1760
1761
  /* prevent WAL removal as fast as possible */
1762
0
  ReplicationSlotsComputeRequiredLSN();
1763
1764
  /* Checkpoint shouldn't remove the required WAL. */
1765
0
  XLByteToSeg(slot->data.restart_lsn, segno, wal_segment_size);
1766
0
  if (XLogGetLastRemovedSegno() >= segno)
1767
0
    elog(ERROR, "WAL required by replication slot %s has been removed concurrently",
1768
0
       NameStr(slot->data.name));
1769
1770
0
  LWLockRelease(ReplicationSlotAllocationLock);
1771
1772
0
  if (!RecoveryInProgress() && SlotIsLogical(slot))
1773
0
  {
1774
0
    XLogRecPtr  flushptr;
1775
1776
    /* make sure we have enough information to start */
1777
0
    flushptr = LogStandbySnapshot();
1778
1779
    /* and make sure it's fsynced to disk */
1780
0
    XLogFlush(flushptr);
1781
0
  }
1782
0
}
1783
1784
/*
1785
 * Report that replication slot needs to be invalidated
1786
 */
1787
static void
1788
ReportSlotInvalidation(ReplicationSlotInvalidationCause cause,
1789
             bool terminating,
1790
             int pid,
1791
             NameData slotname,
1792
             XLogRecPtr restart_lsn,
1793
             XLogRecPtr oldestLSN,
1794
             TransactionId snapshotConflictHorizon,
1795
             long slot_idle_seconds)
1796
{
1797
  StringInfoData err_detail;
1798
  StringInfoData err_hint;
1799
1800
  initStringInfo(&err_detail);
1801
  initStringInfo(&err_hint);
1802
1803
  switch (cause)
1804
  {
1805
    case RS_INVAL_WAL_REMOVED:
1806
      {
1807
        uint64    ex = oldestLSN - restart_lsn;
1808
1809
        appendStringInfo(&err_detail,
1810
                 ngettext("The slot's restart_lsn %X/%08X exceeds the limit by %" PRIu64 " byte.",
1811
                      "The slot's restart_lsn %X/%08X exceeds the limit by %" PRIu64 " bytes.",
1812
                      ex),
1813
                 LSN_FORMAT_ARGS(restart_lsn),
1814
                 ex);
1815
        /* translator: %s is a GUC variable name */
1816
        appendStringInfo(&err_hint, _("You might need to increase \"%s\"."),
1817
                 "max_slot_wal_keep_size");
1818
        break;
1819
      }
1820
    case RS_INVAL_HORIZON:
1821
      appendStringInfo(&err_detail, _("The slot conflicted with xid horizon %u."),
1822
               snapshotConflictHorizon);
1823
      break;
1824
1825
    case RS_INVAL_WAL_LEVEL:
1826
      appendStringInfoString(&err_detail, _("Logical decoding on standby requires the primary server to either set \"wal_level\" >= \"logical\" or have at least one logical slot when \"wal_level\" = \"replica\"."));
1827
      break;
1828
1829
    case RS_INVAL_IDLE_TIMEOUT:
1830
      {
1831
        /* translator: %s is a GUC variable name */
1832
        appendStringInfo(&err_detail, _("The slot's idle time of %lds exceeds the configured \"%s\" duration of %ds."),
1833
                 slot_idle_seconds, "idle_replication_slot_timeout",
1834
                 idle_replication_slot_timeout_secs);
1835
        /* translator: %s is a GUC variable name */
1836
        appendStringInfo(&err_hint, _("You might need to increase \"%s\"."),
1837
                 "idle_replication_slot_timeout");
1838
        break;
1839
      }
1840
    case RS_INVAL_NONE:
1841
      pg_unreachable();
1842
  }
1843
1844
  ereport(LOG,
1845
      terminating ?
1846
      errmsg("terminating process %d to release replication slot \"%s\"",
1847
           pid, NameStr(slotname)) :
1848
      errmsg("invalidating obsolete replication slot \"%s\"",
1849
           NameStr(slotname)),
1850
      errdetail_internal("%s", err_detail.data),
1851
      err_hint.len ? errhint("%s", err_hint.data) : 0);
1852
1853
  pfree(err_detail.data);
1854
  pfree(err_hint.data);
1855
}
1856
1857
/*
1858
 * Can we invalidate an idle replication slot?
1859
 *
1860
 * Idle timeout invalidation is allowed only when:
1861
 *
1862
 * 1. Idle timeout is set
1863
 * 2. Slot has reserved WAL
1864
 * 3. Slot is inactive
1865
 * 4. The slot is not being synced from the primary while the server is in
1866
 *    recovery. This is because synced slots are always considered to be
1867
 *    inactive because they don't perform logical decoding to produce changes.
1868
 */
1869
static inline bool
1870
CanInvalidateIdleSlot(ReplicationSlot *s)
1871
0
{
1872
0
  return (idle_replication_slot_timeout_secs != 0 &&
1873
0
      XLogRecPtrIsValid(s->data.restart_lsn) &&
1874
0
      s->inactive_since > 0 &&
1875
0
      !(RecoveryInProgress() && s->data.synced));
1876
0
}
1877
1878
/*
1879
 * DetermineSlotInvalidationCause - Determine the cause for which a slot
1880
 * becomes invalid among the given possible causes.
1881
 *
1882
 * This function sequentially checks all possible invalidation causes and
1883
 * returns the first one for which the slot is eligible for invalidation.
1884
 */
1885
static ReplicationSlotInvalidationCause
1886
DetermineSlotInvalidationCause(uint32 possible_causes, ReplicationSlot *s,
1887
                 XLogRecPtr oldestLSN, Oid dboid,
1888
                 TransactionId snapshotConflictHorizon,
1889
                 TimestampTz *inactive_since, TimestampTz now)
1890
0
{
1891
0
  Assert(possible_causes != RS_INVAL_NONE);
1892
1893
0
  if (possible_causes & RS_INVAL_WAL_REMOVED)
1894
0
  {
1895
0
    XLogRecPtr  restart_lsn = s->data.restart_lsn;
1896
1897
0
    if (XLogRecPtrIsValid(restart_lsn) &&
1898
0
      restart_lsn < oldestLSN)
1899
0
      return RS_INVAL_WAL_REMOVED;
1900
0
  }
1901
1902
0
  if (possible_causes & RS_INVAL_HORIZON)
1903
0
  {
1904
    /* invalid DB oid signals a shared relation */
1905
0
    if (SlotIsLogical(s) &&
1906
0
      (dboid == InvalidOid || dboid == s->data.database))
1907
0
    {
1908
0
      TransactionId effective_xmin = s->effective_xmin;
1909
0
      TransactionId catalog_effective_xmin = s->effective_catalog_xmin;
1910
1911
0
      if (TransactionIdIsValid(effective_xmin) &&
1912
0
        TransactionIdPrecedesOrEquals(effective_xmin,
1913
0
                        snapshotConflictHorizon))
1914
0
        return RS_INVAL_HORIZON;
1915
0
      else if (TransactionIdIsValid(catalog_effective_xmin) &&
1916
0
           TransactionIdPrecedesOrEquals(catalog_effective_xmin,
1917
0
                           snapshotConflictHorizon))
1918
0
        return RS_INVAL_HORIZON;
1919
0
    }
1920
0
  }
1921
1922
0
  if (possible_causes & RS_INVAL_WAL_LEVEL)
1923
0
  {
1924
0
    if (SlotIsLogical(s))
1925
0
      return RS_INVAL_WAL_LEVEL;
1926
0
  }
1927
1928
0
  if (possible_causes & RS_INVAL_IDLE_TIMEOUT)
1929
0
  {
1930
0
    Assert(now > 0);
1931
1932
0
    if (CanInvalidateIdleSlot(s))
1933
0
    {
1934
      /*
1935
       * Simulate the invalidation due to idle_timeout to test the
1936
       * timeout behavior promptly, without waiting for it to trigger
1937
       * naturally.
1938
       */
1939
#ifdef USE_INJECTION_POINTS
1940
      if (IS_INJECTION_POINT_ATTACHED("slot-timeout-inval"))
1941
      {
1942
        *inactive_since = 0;  /* since the beginning of time */
1943
        return RS_INVAL_IDLE_TIMEOUT;
1944
      }
1945
#endif
1946
1947
      /*
1948
       * Check if the slot needs to be invalidated due to
1949
       * idle_replication_slot_timeout GUC.
1950
       */
1951
0
      if (TimestampDifferenceExceedsSeconds(s->inactive_since, now,
1952
0
                          idle_replication_slot_timeout_secs))
1953
0
      {
1954
0
        *inactive_since = s->inactive_since;
1955
0
        return RS_INVAL_IDLE_TIMEOUT;
1956
0
      }
1957
0
    }
1958
0
  }
1959
1960
0
  return RS_INVAL_NONE;
1961
0
}
1962
1963
/*
1964
 * Helper for InvalidateObsoleteReplicationSlots
1965
 *
1966
 * Acquires the given slot and mark it invalid, if necessary and possible.
1967
 *
1968
 * Returns true if the slot was invalidated.
1969
 *
1970
 * Set *released_lock_out if ReplicationSlotControlLock was released in the
1971
 * interim (and in that case we're not holding the lock at return, otherwise
1972
 * we are).
1973
 *
1974
 * This is inherently racy, because we release the LWLock
1975
 * for syscalls, so caller must restart if we return true.
1976
 */
1977
static bool
1978
InvalidatePossiblyObsoleteSlot(uint32 possible_causes,
1979
                 ReplicationSlot *s,
1980
                 XLogRecPtr oldestLSN,
1981
                 Oid dboid, TransactionId snapshotConflictHorizon,
1982
                 bool *released_lock_out)
1983
0
{
1984
0
  int     last_signaled_pid = 0;
1985
0
  bool    released_lock = false;
1986
0
  bool    invalidated = false;
1987
0
  TimestampTz inactive_since = 0;
1988
1989
0
  for (;;)
1990
0
  {
1991
0
    XLogRecPtr  restart_lsn;
1992
0
    NameData  slotname;
1993
0
    ProcNumber  active_proc;
1994
0
    int     active_pid = 0;
1995
0
    ReplicationSlotInvalidationCause invalidation_cause = RS_INVAL_NONE;
1996
0
    TimestampTz now = 0;
1997
0
    long    slot_idle_secs = 0;
1998
1999
0
    Assert(LWLockHeldByMeInMode(ReplicationSlotControlLock, LW_SHARED));
2000
2001
0
    if (!s->in_use)
2002
0
    {
2003
0
      if (released_lock)
2004
0
        LWLockRelease(ReplicationSlotControlLock);
2005
0
      break;
2006
0
    }
2007
2008
0
    if (possible_causes & RS_INVAL_IDLE_TIMEOUT)
2009
0
    {
2010
      /*
2011
       * Assign the current time here to avoid system call overhead
2012
       * while holding the spinlock in subsequent code.
2013
       */
2014
0
      now = GetCurrentTimestamp();
2015
0
    }
2016
2017
    /*
2018
     * Check if the slot needs to be invalidated. If it needs to be
2019
     * invalidated, and is not currently acquired, acquire it and mark it
2020
     * as having been invalidated.  We do this with the spinlock held to
2021
     * avoid race conditions -- for example the restart_lsn could move
2022
     * forward, or the slot could be dropped.
2023
     */
2024
0
    SpinLockAcquire(&s->mutex);
2025
2026
0
    restart_lsn = s->data.restart_lsn;
2027
2028
    /* we do nothing if the slot is already invalid */
2029
0
    if (s->data.invalidated == RS_INVAL_NONE)
2030
0
      invalidation_cause = DetermineSlotInvalidationCause(possible_causes,
2031
0
                                s, oldestLSN,
2032
0
                                dboid,
2033
0
                                snapshotConflictHorizon,
2034
0
                                &inactive_since,
2035
0
                                now);
2036
2037
    /* if there's no invalidation, we're done */
2038
0
    if (invalidation_cause == RS_INVAL_NONE)
2039
0
    {
2040
0
      SpinLockRelease(&s->mutex);
2041
0
      if (released_lock)
2042
0
        LWLockRelease(ReplicationSlotControlLock);
2043
0
      break;
2044
0
    }
2045
2046
0
    slotname = s->data.name;
2047
0
    active_proc = s->active_proc;
2048
2049
    /*
2050
     * If the slot can be acquired, do so and mark it invalidated
2051
     * immediately.  Otherwise we'll signal the owning process, below, and
2052
     * retry.
2053
     *
2054
     * Note: Unlike other slot attributes, slot's inactive_since can't be
2055
     * changed until the acquired slot is released or the owning process
2056
     * is terminated. So, the inactive slot can only be invalidated
2057
     * immediately without being terminated.
2058
     */
2059
0
    if (active_proc == INVALID_PROC_NUMBER)
2060
0
    {
2061
0
      MyReplicationSlot = s;
2062
0
      s->active_proc = MyProcNumber;
2063
0
      s->data.invalidated = invalidation_cause;
2064
2065
      /*
2066
       * XXX: We should consider not overwriting restart_lsn and instead
2067
       * just rely on .invalidated.
2068
       */
2069
0
      if (invalidation_cause == RS_INVAL_WAL_REMOVED)
2070
0
      {
2071
0
        s->data.restart_lsn = InvalidXLogRecPtr;
2072
0
        s->last_saved_restart_lsn = InvalidXLogRecPtr;
2073
0
      }
2074
2075
      /* Let caller know */
2076
0
      invalidated = true;
2077
0
    }
2078
0
    else
2079
0
    {
2080
0
      active_pid = GetPGProcByNumber(active_proc)->pid;
2081
0
      Assert(active_pid != 0);
2082
0
    }
2083
2084
0
    SpinLockRelease(&s->mutex);
2085
2086
    /*
2087
     * Calculate the idle time duration of the slot if slot is marked
2088
     * invalidated with RS_INVAL_IDLE_TIMEOUT.
2089
     */
2090
0
    if (invalidation_cause == RS_INVAL_IDLE_TIMEOUT)
2091
0
    {
2092
0
      int     slot_idle_usecs;
2093
2094
0
      TimestampDifference(inactive_since, now, &slot_idle_secs,
2095
0
                &slot_idle_usecs);
2096
0
    }
2097
2098
0
    if (active_proc != INVALID_PROC_NUMBER)
2099
0
    {
2100
      /*
2101
       * Prepare the sleep on the slot's condition variable before
2102
       * releasing the lock, to close a possible race condition if the
2103
       * slot is released before the sleep below.
2104
       */
2105
0
      ConditionVariablePrepareToSleep(&s->active_cv);
2106
2107
0
      LWLockRelease(ReplicationSlotControlLock);
2108
0
      released_lock = true;
2109
2110
      /*
2111
       * Signal to terminate the process that owns the slot, if we
2112
       * haven't already signalled it.  (Avoidance of repeated
2113
       * signalling is the only reason for there to be a loop in this
2114
       * routine; otherwise we could rely on caller's restart loop.)
2115
       *
2116
       * There is the race condition that other process may own the slot
2117
       * after its current owner process is terminated and before this
2118
       * process owns it. To handle that, we signal only if the PID of
2119
       * the owning process has changed from the previous time. (This
2120
       * logic assumes that the same PID is not reused very quickly.)
2121
       */
2122
0
      if (last_signaled_pid != active_pid)
2123
0
      {
2124
0
        ReportSlotInvalidation(invalidation_cause, true, active_pid,
2125
0
                     slotname, restart_lsn,
2126
0
                     oldestLSN, snapshotConflictHorizon,
2127
0
                     slot_idle_secs);
2128
2129
0
        if (MyBackendType == B_STARTUP)
2130
0
          (void) SignalRecoveryConflict(GetPGProcByNumber(active_proc),
2131
0
                          active_pid,
2132
0
                          RECOVERY_CONFLICT_LOGICALSLOT);
2133
0
        else
2134
0
          (void) kill(active_pid, SIGTERM);
2135
2136
0
        last_signaled_pid = active_pid;
2137
0
      }
2138
2139
      /* Wait until the slot is released. */
2140
0
      ConditionVariableSleep(&s->active_cv,
2141
0
                   WAIT_EVENT_REPLICATION_SLOT_DROP);
2142
2143
      /*
2144
       * Re-acquire lock and start over; we expect to invalidate the
2145
       * slot next time (unless another process acquires the slot in the
2146
       * meantime).
2147
       *
2148
       * Note: It is possible for a slot to advance its restart_lsn or
2149
       * xmin values sufficiently between when we release the mutex and
2150
       * when we recheck, moving from a conflicting state to a non
2151
       * conflicting state.  This is intentional and safe: if the slot
2152
       * has caught up while we're busy here, the resources we were
2153
       * concerned about (WAL segments or tuples) have not yet been
2154
       * removed, and there's no reason to invalidate the slot.
2155
       */
2156
0
      LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
2157
0
      continue;
2158
0
    }
2159
0
    else
2160
0
    {
2161
      /*
2162
       * We hold the slot now and have already invalidated it; flush it
2163
       * to ensure that state persists.
2164
       *
2165
       * Don't want to hold ReplicationSlotControlLock across file
2166
       * system operations, so release it now but be sure to tell caller
2167
       * to restart from scratch.
2168
       */
2169
0
      LWLockRelease(ReplicationSlotControlLock);
2170
0
      released_lock = true;
2171
2172
      /* Make sure the invalidated state persists across server restart */
2173
0
      ReplicationSlotMarkDirty();
2174
0
      ReplicationSlotSave();
2175
0
      ReplicationSlotRelease();
2176
2177
0
      ReportSlotInvalidation(invalidation_cause, false, active_pid,
2178
0
                   slotname, restart_lsn,
2179
0
                   oldestLSN, snapshotConflictHorizon,
2180
0
                   slot_idle_secs);
2181
2182
      /* done with this slot for now */
2183
0
      break;
2184
0
    }
2185
0
  }
2186
2187
0
  Assert(released_lock == !LWLockHeldByMe(ReplicationSlotControlLock));
2188
2189
0
  *released_lock_out = released_lock;
2190
0
  return invalidated;
2191
0
}
2192
2193
/*
2194
 * Invalidate slots that require resources about to be removed.
2195
 *
2196
 * Returns true when any slot have got invalidated.
2197
 *
2198
 * Whether a slot needs to be invalidated depends on the invalidation cause.
2199
 * A slot is invalidated if it:
2200
 * - RS_INVAL_WAL_REMOVED: requires a LSN older than the given segment
2201
 * - RS_INVAL_HORIZON: requires a snapshot <= the given horizon in the given
2202
 *   db; dboid may be InvalidOid for shared relations
2203
 * - RS_INVAL_WAL_LEVEL: is a logical slot and effective_wal_level is not
2204
 *   logical.
2205
 * - RS_INVAL_IDLE_TIMEOUT: has been idle longer than the configured
2206
 *   "idle_replication_slot_timeout" duration.
2207
 *
2208
 * Note: This function attempts to invalidate the slot for multiple possible
2209
 * causes in a single pass, minimizing redundant iterations. The "cause"
2210
 * parameter can be a MASK representing one or more of the defined causes.
2211
 *
2212
 * If it invalidates the last logical slot in the cluster, it requests to
2213
 * disable logical decoding.
2214
 *
2215
 * NB - this runs as part of checkpoint, so avoid raising errors if possible.
2216
 */
2217
bool
2218
InvalidateObsoleteReplicationSlots(uint32 possible_causes,
2219
                   XLogSegNo oldestSegno, Oid dboid,
2220
                   TransactionId snapshotConflictHorizon)
2221
0
{
2222
0
  XLogRecPtr  oldestLSN;
2223
0
  bool    invalidated = false;
2224
0
  bool    invalidated_logical = false;
2225
0
  bool    found_valid_logicalslot;
2226
2227
0
  Assert(!(possible_causes & RS_INVAL_HORIZON) || TransactionIdIsValid(snapshotConflictHorizon));
2228
0
  Assert(!(possible_causes & RS_INVAL_WAL_REMOVED) || oldestSegno > 0);
2229
0
  Assert(possible_causes != RS_INVAL_NONE);
2230
2231
0
  if (max_replication_slots == 0 && max_repack_replication_slots == 0)
2232
0
    return invalidated;
2233
2234
0
  XLogSegNoOffsetToRecPtr(oldestSegno, 0, wal_segment_size, oldestLSN);
2235
2236
0
restart:
2237
0
  found_valid_logicalslot = false;
2238
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
2239
0
  for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
2240
0
  {
2241
0
    ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
2242
0
    bool    released_lock = false;
2243
2244
0
    if (!s->in_use)
2245
0
      continue;
2246
2247
    /* Prevent invalidation of logical slots during binary upgrade */
2248
0
    if (SlotIsLogical(s) && IsBinaryUpgrade)
2249
0
    {
2250
0
      SpinLockAcquire(&s->mutex);
2251
0
      found_valid_logicalslot |= (s->data.invalidated == RS_INVAL_NONE);
2252
0
      SpinLockRelease(&s->mutex);
2253
2254
0
      continue;
2255
0
    }
2256
2257
0
    if (InvalidatePossiblyObsoleteSlot(possible_causes, s, oldestLSN,
2258
0
                       dboid, snapshotConflictHorizon,
2259
0
                       &released_lock))
2260
0
    {
2261
0
      Assert(released_lock);
2262
2263
      /* Remember we have invalidated a physical or logical slot */
2264
0
      invalidated = true;
2265
2266
      /*
2267
       * Additionally, remember we have invalidated a logical slot as we
2268
       * can request disabling logical decoding later.
2269
       */
2270
0
      if (SlotIsLogical(s))
2271
0
        invalidated_logical = true;
2272
0
    }
2273
0
    else
2274
0
    {
2275
      /*
2276
       * We need to check if the slot is invalidated here since
2277
       * InvalidatePossiblyObsoleteSlot() returns false also if the slot
2278
       * is already invalidated.
2279
       */
2280
0
      SpinLockAcquire(&s->mutex);
2281
0
      found_valid_logicalslot |=
2282
0
        (SlotIsLogical(s) && (s->data.invalidated == RS_INVAL_NONE));
2283
0
      SpinLockRelease(&s->mutex);
2284
0
    }
2285
2286
    /* if the lock was released, start from scratch */
2287
0
    if (released_lock)
2288
0
      goto restart;
2289
0
  }
2290
0
  LWLockRelease(ReplicationSlotControlLock);
2291
2292
  /*
2293
   * If any slots have been invalidated, recalculate the resource limits.
2294
   */
2295
0
  if (invalidated)
2296
0
  {
2297
0
    ReplicationSlotsComputeRequiredXmin(false);
2298
0
    ReplicationSlotsComputeRequiredLSN();
2299
0
  }
2300
2301
  /*
2302
   * Request the checkpointer to disable logical decoding if no valid
2303
   * logical slots remain. If called by the checkpointer during a
2304
   * checkpoint, only the request is initiated; actual deactivation is
2305
   * deferred until after the checkpoint completes.
2306
   */
2307
0
  if (invalidated_logical && !found_valid_logicalslot)
2308
0
    RequestDisableLogicalDecoding();
2309
2310
0
  return invalidated;
2311
0
}
2312
2313
/*
2314
 * Flush all replication slots to disk.
2315
 *
2316
 * It is convenient to flush dirty replication slots at the time of checkpoint.
2317
 * Additionally, in case of a shutdown checkpoint, we also identify the slots
2318
 * for which the confirmed_flush LSN has been updated since the last time it
2319
 * was saved and flush them.
2320
 */
2321
void
2322
CheckPointReplicationSlots(bool is_shutdown)
2323
0
{
2324
0
  int     i;
2325
0
  bool    last_saved_restart_lsn_updated = false;
2326
2327
0
  elog(DEBUG1, "performing replication slot checkpoint");
2328
2329
  /*
2330
   * Prevent any slot from being created/dropped while we're active. As we
2331
   * explicitly do *not* want to block iterating over replication_slots or
2332
   * acquiring a slot we cannot take the control lock - but that's OK,
2333
   * because holding ReplicationSlotAllocationLock is strictly stronger, and
2334
   * enough to guarantee that nobody can change the in_use bits on us.
2335
   *
2336
   * Additionally, acquiring the Allocation lock is necessary to serialize
2337
   * the slot flush process with concurrent slot WAL reservation. This
2338
   * ensures that the WAL position being reserved is either flushed to disk
2339
   * or is beyond or equal to the redo pointer of the current checkpoint
2340
   * (See ReplicationSlotReserveWal for details).
2341
   */
2342
0
  LWLockAcquire(ReplicationSlotAllocationLock, LW_SHARED);
2343
2344
0
  for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++)
2345
0
  {
2346
0
    ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
2347
0
    char    path[MAXPGPATH];
2348
2349
0
    if (!s->in_use)
2350
0
      continue;
2351
2352
    /* save the slot to disk, locking is handled in SaveSlotToPath() */
2353
0
    sprintf(path, "%s/%s", PG_REPLSLOT_DIR, NameStr(s->data.name));
2354
2355
    /*
2356
     * Slot's data is not flushed each time the confirmed_flush LSN is
2357
     * updated as that could lead to frequent writes.  However, we decide
2358
     * to force a flush of all logical slot's data at the time of shutdown
2359
     * if the confirmed_flush LSN is changed since we last flushed it to
2360
     * disk.  This helps in avoiding an unnecessary retreat of the
2361
     * confirmed_flush LSN after restart.
2362
     */
2363
0
    if (is_shutdown && SlotIsLogical(s))
2364
0
    {
2365
0
      SpinLockAcquire(&s->mutex);
2366
2367
0
      if (s->data.invalidated == RS_INVAL_NONE &&
2368
0
        s->data.confirmed_flush > s->last_saved_confirmed_flush)
2369
0
      {
2370
0
        s->just_dirtied = true;
2371
0
        s->dirty = true;
2372
0
      }
2373
0
      SpinLockRelease(&s->mutex);
2374
0
    }
2375
2376
    /*
2377
     * Track if we're going to update slot's last_saved_restart_lsn. We
2378
     * need this to know if we need to recompute the required LSN.
2379
     */
2380
0
    if (s->last_saved_restart_lsn != s->data.restart_lsn)
2381
0
      last_saved_restart_lsn_updated = true;
2382
2383
0
    SaveSlotToPath(s, path, LOG);
2384
0
  }
2385
0
  LWLockRelease(ReplicationSlotAllocationLock);
2386
2387
  /*
2388
   * Recompute the required LSN if SaveSlotToPath() updated
2389
   * last_saved_restart_lsn for any slot.
2390
   */
2391
0
  if (last_saved_restart_lsn_updated)
2392
0
    ReplicationSlotsComputeRequiredLSN();
2393
0
}
2394
2395
/*
2396
 * Load all replication slots from disk into memory at server startup. This
2397
 * needs to be run before we start crash recovery.
2398
 */
2399
void
2400
StartupReplicationSlots(void)
2401
0
{
2402
0
  DIR      *replication_dir;
2403
0
  struct dirent *replication_de;
2404
2405
0
  elog(DEBUG1, "starting up replication slots");
2406
2407
  /* restore all slots by iterating over all on-disk entries */
2408
0
  replication_dir = AllocateDir(PG_REPLSLOT_DIR);
2409
0
  while ((replication_de = ReadDir(replication_dir, PG_REPLSLOT_DIR)) != NULL)
2410
0
  {
2411
0
    char    path[MAXPGPATH + sizeof(PG_REPLSLOT_DIR)];
2412
0
    PGFileType  de_type;
2413
2414
0
    if (strcmp(replication_de->d_name, ".") == 0 ||
2415
0
      strcmp(replication_de->d_name, "..") == 0)
2416
0
      continue;
2417
2418
0
    snprintf(path, sizeof(path), "%s/%s", PG_REPLSLOT_DIR, replication_de->d_name);
2419
0
    de_type = get_dirent_type(path, replication_de, false, DEBUG1);
2420
2421
    /* we're only creating directories here, skip if it's not our's */
2422
0
    if (de_type != PGFILETYPE_ERROR && de_type != PGFILETYPE_DIR)
2423
0
      continue;
2424
2425
    /* we crashed while a slot was being setup or deleted, clean up */
2426
0
    if (pg_str_endswith(replication_de->d_name, ".tmp"))
2427
0
    {
2428
0
      if (!rmtree(path, true))
2429
0
      {
2430
0
        ereport(WARNING,
2431
0
            (errmsg("could not remove directory \"%s\"",
2432
0
                path)));
2433
0
        continue;
2434
0
      }
2435
0
      fsync_fname(PG_REPLSLOT_DIR, true);
2436
0
      continue;
2437
0
    }
2438
2439
    /* looks like a slot in a normal state, restore */
2440
0
    RestoreSlotFromDisk(replication_de->d_name);
2441
0
  }
2442
0
  FreeDir(replication_dir);
2443
2444
  /* currently no slots exist, we're done. */
2445
0
  if (max_replication_slots + max_repack_replication_slots <= 0)
2446
0
    return;
2447
2448
  /* Now that we have recovered all the data, compute replication xmin */
2449
0
  ReplicationSlotsComputeRequiredXmin(false);
2450
0
  ReplicationSlotsComputeRequiredLSN();
2451
0
}
2452
2453
/* ----
2454
 * Manipulation of on-disk state of replication slots
2455
 *
2456
 * NB: none of the routines below should take any notice whether a slot is the
2457
 * current one or not, that's all handled a layer above.
2458
 * ----
2459
 */
2460
static void
2461
CreateSlotOnDisk(ReplicationSlot *slot)
2462
0
{
2463
0
  char    tmppath[MAXPGPATH];
2464
0
  char    path[MAXPGPATH];
2465
0
  struct stat st;
2466
2467
  /*
2468
   * No need to take out the io_in_progress_lock, nobody else can see this
2469
   * slot yet, so nobody else will write. We're reusing SaveSlotToPath which
2470
   * takes out the lock, if we'd take the lock here, we'd deadlock.
2471
   */
2472
2473
0
  sprintf(path, "%s/%s", PG_REPLSLOT_DIR, NameStr(slot->data.name));
2474
0
  sprintf(tmppath, "%s/%s.tmp", PG_REPLSLOT_DIR, NameStr(slot->data.name));
2475
2476
  /*
2477
   * It's just barely possible that some previous effort to create or drop a
2478
   * slot with this name left a temp directory lying around. If that seems
2479
   * to be the case, try to remove it.  If the rmtree() fails, we'll error
2480
   * out at the MakePGDirectory() below, so we don't bother checking
2481
   * success.
2482
   */
2483
0
  if (stat(tmppath, &st) == 0 && S_ISDIR(st.st_mode))
2484
0
    rmtree(tmppath, true);
2485
2486
  /* Create and fsync the temporary slot directory. */
2487
0
  if (MakePGDirectory(tmppath) < 0)
2488
0
    ereport(ERROR,
2489
0
        (errcode_for_file_access(),
2490
0
         errmsg("could not create directory \"%s\": %m",
2491
0
            tmppath)));
2492
0
  fsync_fname(tmppath, true);
2493
2494
  /* Write the actual state file. */
2495
0
  slot->dirty = true;     /* signal that we really need to write */
2496
0
  SaveSlotToPath(slot, tmppath, ERROR);
2497
2498
  /* Rename the directory into place. */
2499
0
  if (rename(tmppath, path) != 0)
2500
0
    ereport(ERROR,
2501
0
        (errcode_for_file_access(),
2502
0
         errmsg("could not rename file \"%s\" to \"%s\": %m",
2503
0
            tmppath, path)));
2504
2505
  /*
2506
   * If we'd now fail - really unlikely - we wouldn't know whether this slot
2507
   * would persist after an OS crash or not - so, force a restart. The
2508
   * restart would try to fsync this again till it works.
2509
   */
2510
0
  START_CRIT_SECTION();
2511
2512
0
  fsync_fname(path, true);
2513
0
  fsync_fname(PG_REPLSLOT_DIR, true);
2514
2515
0
  END_CRIT_SECTION();
2516
0
}
2517
2518
/*
2519
 * Shared functionality between saving and creating a replication slot.
2520
 */
2521
static void
2522
SaveSlotToPath(ReplicationSlot *slot, const char *dir, int elevel)
2523
0
{
2524
0
  char    tmppath[MAXPGPATH];
2525
0
  char    path[MAXPGPATH];
2526
0
  int     fd;
2527
0
  ReplicationSlotOnDisk cp;
2528
0
  bool    was_dirty;
2529
2530
  /* first check whether there's something to write out */
2531
0
  SpinLockAcquire(&slot->mutex);
2532
0
  was_dirty = slot->dirty;
2533
0
  slot->just_dirtied = false;
2534
0
  SpinLockRelease(&slot->mutex);
2535
2536
  /* and don't do anything if there's nothing to write */
2537
0
  if (!was_dirty)
2538
0
    return;
2539
2540
0
  LWLockAcquire(&slot->io_in_progress_lock, LW_EXCLUSIVE);
2541
2542
  /* silence valgrind :( */
2543
0
  memset(&cp, 0, sizeof(ReplicationSlotOnDisk));
2544
2545
0
  sprintf(tmppath, "%s/state.tmp", dir);
2546
0
  sprintf(path, "%s/state", dir);
2547
2548
0
  fd = OpenTransientFile(tmppath, O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
2549
0
  if (fd < 0)
2550
0
  {
2551
    /*
2552
     * If not an ERROR, then release the lock before returning.  In case
2553
     * of an ERROR, the error recovery path automatically releases the
2554
     * lock, but no harm in explicitly releasing even in that case.  Note
2555
     * that LWLockRelease() could affect errno.
2556
     */
2557
0
    int     save_errno = errno;
2558
2559
0
    LWLockRelease(&slot->io_in_progress_lock);
2560
0
    errno = save_errno;
2561
0
    ereport(elevel,
2562
0
        (errcode_for_file_access(),
2563
0
         errmsg("could not create file \"%s\": %m",
2564
0
            tmppath)));
2565
0
    return;
2566
0
  }
2567
2568
0
  cp.magic = SLOT_MAGIC;
2569
0
  INIT_CRC32C(cp.checksum);
2570
0
  cp.version = SLOT_VERSION;
2571
0
  cp.length = ReplicationSlotOnDiskV2Size;
2572
2573
0
  SpinLockAcquire(&slot->mutex);
2574
2575
0
  memcpy(&cp.slotdata, &slot->data, sizeof(ReplicationSlotPersistentData));
2576
2577
0
  SpinLockRelease(&slot->mutex);
2578
2579
0
  COMP_CRC32C(cp.checksum,
2580
0
        (char *) (&cp) + ReplicationSlotOnDiskNotChecksummedSize,
2581
0
        ReplicationSlotOnDiskChecksummedSize);
2582
0
  FIN_CRC32C(cp.checksum);
2583
2584
0
  errno = 0;
2585
0
  pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_WRITE);
2586
0
  if ((write(fd, &cp, sizeof(cp))) != sizeof(cp))
2587
0
  {
2588
0
    int     save_errno = errno;
2589
2590
0
    pgstat_report_wait_end();
2591
0
    CloseTransientFile(fd);
2592
0
    unlink(tmppath);
2593
0
    LWLockRelease(&slot->io_in_progress_lock);
2594
2595
    /* if write didn't set errno, assume problem is no disk space */
2596
0
    errno = save_errno ? save_errno : ENOSPC;
2597
0
    ereport(elevel,
2598
0
        (errcode_for_file_access(),
2599
0
         errmsg("could not write to file \"%s\": %m",
2600
0
            tmppath)));
2601
0
    return;
2602
0
  }
2603
0
  pgstat_report_wait_end();
2604
2605
  /* fsync the temporary file */
2606
0
  pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_SYNC);
2607
0
  if (pg_fsync(fd) != 0)
2608
0
  {
2609
0
    int     save_errno = errno;
2610
2611
0
    pgstat_report_wait_end();
2612
0
    CloseTransientFile(fd);
2613
0
    unlink(tmppath);
2614
0
    LWLockRelease(&slot->io_in_progress_lock);
2615
2616
0
    errno = save_errno;
2617
0
    ereport(elevel,
2618
0
        (errcode_for_file_access(),
2619
0
         errmsg("could not fsync file \"%s\": %m",
2620
0
            tmppath)));
2621
0
    return;
2622
0
  }
2623
0
  pgstat_report_wait_end();
2624
2625
0
  if (CloseTransientFile(fd) != 0)
2626
0
  {
2627
0
    int     save_errno = errno;
2628
2629
0
    unlink(tmppath);
2630
0
    LWLockRelease(&slot->io_in_progress_lock);
2631
2632
0
    errno = save_errno;
2633
0
    ereport(elevel,
2634
0
        (errcode_for_file_access(),
2635
0
         errmsg("could not close file \"%s\": %m",
2636
0
            tmppath)));
2637
0
    return;
2638
0
  }
2639
2640
  /* rename to permanent file, fsync file and directory */
2641
0
  if (rename(tmppath, path) != 0)
2642
0
  {
2643
0
    int     save_errno = errno;
2644
2645
0
    unlink(tmppath);
2646
0
    LWLockRelease(&slot->io_in_progress_lock);
2647
2648
0
    errno = save_errno;
2649
0
    ereport(elevel,
2650
0
        (errcode_for_file_access(),
2651
0
         errmsg("could not rename file \"%s\" to \"%s\": %m",
2652
0
            tmppath, path)));
2653
0
    return;
2654
0
  }
2655
2656
  /*
2657
   * Check CreateSlotOnDisk() for the reasoning of using a critical section.
2658
   */
2659
0
  START_CRIT_SECTION();
2660
2661
0
  fsync_fname(path, false);
2662
0
  fsync_fname(dir, true);
2663
0
  fsync_fname(PG_REPLSLOT_DIR, true);
2664
2665
0
  END_CRIT_SECTION();
2666
2667
  /*
2668
   * Successfully wrote, unset dirty bit, unless somebody dirtied again
2669
   * already and remember the confirmed_flush LSN value.
2670
   */
2671
0
  SpinLockAcquire(&slot->mutex);
2672
0
  if (!slot->just_dirtied)
2673
0
    slot->dirty = false;
2674
0
  slot->last_saved_confirmed_flush = cp.slotdata.confirmed_flush;
2675
0
  slot->last_saved_restart_lsn = cp.slotdata.restart_lsn;
2676
0
  SpinLockRelease(&slot->mutex);
2677
2678
0
  LWLockRelease(&slot->io_in_progress_lock);
2679
0
}
2680
2681
/*
2682
 * Load a single slot from disk into memory.
2683
 */
2684
static void
2685
RestoreSlotFromDisk(const char *name)
2686
0
{
2687
0
  ReplicationSlotOnDisk cp;
2688
0
  int     i;
2689
0
  char    slotdir[MAXPGPATH + sizeof(PG_REPLSLOT_DIR)];
2690
0
  char    path[MAXPGPATH + sizeof(PG_REPLSLOT_DIR) + 10];
2691
0
  int     fd;
2692
0
  bool    restored = false;
2693
0
  ssize_t   readBytes;
2694
0
  pg_crc32c checksum;
2695
0
  TimestampTz now = 0;
2696
2697
  /* no need to lock here, no concurrent access allowed yet */
2698
2699
  /* delete temp file if it exists */
2700
0
  sprintf(slotdir, "%s/%s", PG_REPLSLOT_DIR, name);
2701
0
  sprintf(path, "%s/state.tmp", slotdir);
2702
0
  if (unlink(path) < 0 && errno != ENOENT)
2703
0
    ereport(PANIC,
2704
0
        (errcode_for_file_access(),
2705
0
         errmsg("could not remove file \"%s\": %m", path)));
2706
2707
0
  sprintf(path, "%s/state", slotdir);
2708
2709
0
  elog(DEBUG1, "restoring replication slot from \"%s\"", path);
2710
2711
  /* on some operating systems fsyncing a file requires O_RDWR */
2712
0
  fd = OpenTransientFile(path, O_RDWR | PG_BINARY);
2713
2714
  /*
2715
   * We do not need to handle this as we are rename()ing the directory into
2716
   * place only after we fsync()ed the state file.
2717
   */
2718
0
  if (fd < 0)
2719
0
    ereport(PANIC,
2720
0
        (errcode_for_file_access(),
2721
0
         errmsg("could not open file \"%s\": %m", path)));
2722
2723
  /*
2724
   * Sync state file before we're reading from it. We might have crashed
2725
   * while it wasn't synced yet and we shouldn't continue on that basis.
2726
   */
2727
0
  pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_RESTORE_SYNC);
2728
0
  if (pg_fsync(fd) != 0)
2729
0
    ereport(PANIC,
2730
0
        (errcode_for_file_access(),
2731
0
         errmsg("could not fsync file \"%s\": %m",
2732
0
            path)));
2733
0
  pgstat_report_wait_end();
2734
2735
  /* Also sync the parent directory */
2736
0
  START_CRIT_SECTION();
2737
0
  fsync_fname(slotdir, true);
2738
0
  END_CRIT_SECTION();
2739
2740
  /* read part of statefile that's guaranteed to be version independent */
2741
0
  pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_READ);
2742
0
  readBytes = read(fd, &cp, ReplicationSlotOnDiskConstantSize);
2743
0
  pgstat_report_wait_end();
2744
0
  if (readBytes != ReplicationSlotOnDiskConstantSize)
2745
0
  {
2746
0
    if (readBytes < 0)
2747
0
      ereport(PANIC,
2748
0
          (errcode_for_file_access(),
2749
0
           errmsg("could not read file \"%s\": %m", path)));
2750
0
    else
2751
0
      ereport(PANIC,
2752
0
          (errcode(ERRCODE_DATA_CORRUPTED),
2753
0
           errmsg("could not read file \"%s\": read %zd of %zu",
2754
0
              path, readBytes,
2755
0
              ReplicationSlotOnDiskConstantSize)));
2756
0
  }
2757
2758
  /* verify magic */
2759
0
  if (cp.magic != SLOT_MAGIC)
2760
0
    ereport(PANIC,
2761
0
        (errcode(ERRCODE_DATA_CORRUPTED),
2762
0
         errmsg("replication slot file \"%s\" has wrong magic number: %u instead of %u",
2763
0
            path, cp.magic, SLOT_MAGIC)));
2764
2765
  /* verify version */
2766
0
  if (cp.version != SLOT_VERSION)
2767
0
    ereport(PANIC,
2768
0
        (errcode(ERRCODE_DATA_CORRUPTED),
2769
0
         errmsg("replication slot file \"%s\" has unsupported version %u",
2770
0
            path, cp.version)));
2771
2772
  /* boundary check on length */
2773
0
  if (cp.length != ReplicationSlotOnDiskV2Size)
2774
0
    ereport(PANIC,
2775
0
        (errcode(ERRCODE_DATA_CORRUPTED),
2776
0
         errmsg("replication slot file \"%s\" has corrupted length %u",
2777
0
            path, cp.length)));
2778
2779
  /* Now that we know the size, read the entire file */
2780
0
  pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_READ);
2781
0
  readBytes = read(fd,
2782
0
           (char *) &cp + ReplicationSlotOnDiskConstantSize,
2783
0
           cp.length);
2784
0
  pgstat_report_wait_end();
2785
0
  if (readBytes != cp.length)
2786
0
  {
2787
0
    if (readBytes < 0)
2788
0
      ereport(PANIC,
2789
0
          (errcode_for_file_access(),
2790
0
           errmsg("could not read file \"%s\": %m", path)));
2791
0
    else
2792
0
      ereport(PANIC,
2793
0
          (errcode(ERRCODE_DATA_CORRUPTED),
2794
0
           errmsg("could not read file \"%s\": read %zd of %zu",
2795
0
              path, readBytes, (Size) cp.length)));
2796
0
  }
2797
2798
0
  if (CloseTransientFile(fd) != 0)
2799
0
    ereport(PANIC,
2800
0
        (errcode_for_file_access(),
2801
0
         errmsg("could not close file \"%s\": %m", path)));
2802
2803
  /* now verify the CRC */
2804
0
  INIT_CRC32C(checksum);
2805
0
  COMP_CRC32C(checksum,
2806
0
        (char *) &cp + ReplicationSlotOnDiskNotChecksummedSize,
2807
0
        ReplicationSlotOnDiskChecksummedSize);
2808
0
  FIN_CRC32C(checksum);
2809
2810
0
  if (!EQ_CRC32C(checksum, cp.checksum))
2811
0
    ereport(PANIC,
2812
0
        (errmsg("checksum mismatch for replication slot file \"%s\": is %u, should be %u",
2813
0
            path, checksum, cp.checksum)));
2814
2815
  /*
2816
   * If we crashed with an ephemeral slot active, don't restore but delete
2817
   * it.
2818
   */
2819
0
  if (cp.slotdata.persistency != RS_PERSISTENT)
2820
0
  {
2821
0
    if (!rmtree(slotdir, true))
2822
0
    {
2823
0
      ereport(WARNING,
2824
0
          (errmsg("could not remove directory \"%s\"",
2825
0
              slotdir)));
2826
0
    }
2827
0
    fsync_fname(PG_REPLSLOT_DIR, true);
2828
0
    return;
2829
0
  }
2830
2831
  /*
2832
   * Verify that requirements for the specific slot type are met. That's
2833
   * important because if these aren't met we're not guaranteed to retain
2834
   * all the necessary resources for the slot.
2835
   *
2836
   * NB: We have to do so *after* the above checks for ephemeral slots,
2837
   * because otherwise a slot that shouldn't exist anymore could prevent
2838
   * restarts.
2839
   *
2840
   * NB: Changing the requirements here also requires adapting
2841
   * CheckSlotRequirements() and CheckLogicalDecodingRequirements().
2842
   */
2843
0
  if (cp.slotdata.database != InvalidOid)
2844
0
  {
2845
0
    if (wal_level < WAL_LEVEL_REPLICA)
2846
0
      ereport(FATAL,
2847
0
          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2848
0
           errmsg("logical replication slot \"%s\" exists, but \"wal_level\" < \"replica\"",
2849
0
              NameStr(cp.slotdata.name)),
2850
0
           errhint("Change \"wal_level\" to be \"replica\" or higher.")));
2851
2852
    /*
2853
     * In standby mode, the hot standby must be enabled. This check is
2854
     * necessary to ensure logical slots are invalidated when they become
2855
     * incompatible due to insufficient wal_level. Otherwise, if the
2856
     * primary reduces effective_wal_level < logical while hot standby is
2857
     * disabled, primary disable logical decoding while hot standby is
2858
     * disabled, logical slots would remain valid even after promotion.
2859
     */
2860
0
    if (StandbyMode && !EnableHotStandby)
2861
0
      ereport(FATAL,
2862
0
          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2863
0
           errmsg("logical replication slot \"%s\" exists on the standby, but \"hot_standby\" = \"off\"",
2864
0
              NameStr(cp.slotdata.name)),
2865
0
           errhint("Change \"hot_standby\" to be \"on\".")));
2866
0
  }
2867
0
  else if (wal_level < WAL_LEVEL_REPLICA)
2868
0
    ereport(FATAL,
2869
0
        (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2870
0
         errmsg("physical replication slot \"%s\" exists, but \"wal_level\" < \"replica\"",
2871
0
            NameStr(cp.slotdata.name)),
2872
0
         errhint("Change \"wal_level\" to be \"replica\" or higher.")));
2873
2874
  /*
2875
   * Nothing can be active yet, don't lock anything.  Note we iterate up to
2876
   * max_replication_slots instead of adding max_repack_replication_slots as
2877
   * in all other places, because we must enforce the GUC value in case
2878
   * there were more slots before the shutdown than what it is set up to
2879
   * now.
2880
   */
2881
0
  for (i = 0; i < max_replication_slots; i++)
2882
0
  {
2883
0
    ReplicationSlot *slot;
2884
2885
0
    slot = &ReplicationSlotCtl->replication_slots[i];
2886
2887
0
    if (slot->in_use)
2888
0
      continue;
2889
2890
    /* restore the entire set of persistent data */
2891
0
    memcpy(&slot->data, &cp.slotdata,
2892
0
         sizeof(ReplicationSlotPersistentData));
2893
2894
    /* initialize in memory state */
2895
0
    slot->effective_xmin = cp.slotdata.xmin;
2896
0
    slot->effective_catalog_xmin = cp.slotdata.catalog_xmin;
2897
0
    slot->last_saved_confirmed_flush = cp.slotdata.confirmed_flush;
2898
0
    slot->last_saved_restart_lsn = cp.slotdata.restart_lsn;
2899
2900
0
    slot->candidate_catalog_xmin = InvalidTransactionId;
2901
0
    slot->candidate_xmin_lsn = InvalidXLogRecPtr;
2902
0
    slot->candidate_restart_lsn = InvalidXLogRecPtr;
2903
0
    slot->candidate_restart_valid = InvalidXLogRecPtr;
2904
2905
0
    slot->in_use = true;
2906
0
    slot->active_proc = INVALID_PROC_NUMBER;
2907
2908
    /*
2909
     * Set the time since the slot has become inactive after loading the
2910
     * slot from the disk into memory. Whoever acquires the slot i.e.
2911
     * makes the slot active will reset it. Use the same inactive_since
2912
     * time for all the slots.
2913
     */
2914
0
    if (now == 0)
2915
0
      now = GetCurrentTimestamp();
2916
2917
0
    ReplicationSlotSetInactiveSince(slot, now, false);
2918
2919
0
    restored = true;
2920
0
    break;
2921
0
  }
2922
2923
0
  if (!restored)
2924
0
    ereport(FATAL,
2925
0
        (errmsg("too many replication slots active before shutdown"),
2926
0
         errhint("Increase \"max_replication_slots\" and try again.")));
2927
0
}
2928
2929
/*
2930
 * Maps an invalidation reason for a replication slot to
2931
 * ReplicationSlotInvalidationCause.
2932
 */
2933
ReplicationSlotInvalidationCause
2934
GetSlotInvalidationCause(const char *cause_name)
2935
0
{
2936
0
  Assert(cause_name);
2937
2938
  /* Search lookup table for the cause having this name */
2939
0
  for (int i = 0; i <= RS_INVAL_MAX_CAUSES; i++)
2940
0
  {
2941
0
    if (strcmp(SlotInvalidationCauses[i].cause_name, cause_name) == 0)
2942
0
      return SlotInvalidationCauses[i].cause;
2943
0
  }
2944
2945
0
  Assert(false);
2946
0
  return RS_INVAL_NONE;   /* to keep compiler quiet */
2947
0
}
2948
2949
/*
2950
 * Maps a ReplicationSlotInvalidationCause to the invalidation
2951
 * reason for a replication slot.
2952
 */
2953
const char *
2954
GetSlotInvalidationCauseName(ReplicationSlotInvalidationCause cause)
2955
0
{
2956
  /* Search lookup table for the name of this cause */
2957
0
  for (int i = 0; i <= RS_INVAL_MAX_CAUSES; i++)
2958
0
  {
2959
0
    if (SlotInvalidationCauses[i].cause == cause)
2960
0
      return SlotInvalidationCauses[i].cause_name;
2961
0
  }
2962
2963
0
  Assert(false);
2964
0
  return "none";       /* to keep compiler quiet */
2965
0
}
2966
2967
/*
2968
 * A helper function to validate slots specified in GUC synchronized_standby_slots.
2969
 *
2970
 * The rawname will be parsed, and the result will be saved into *elemlist.
2971
 */
2972
static bool
2973
validate_sync_standby_slots(char *rawname, List **elemlist)
2974
0
{
2975
  /* Verify syntax and parse string into a list of identifiers */
2976
0
  if (!SplitIdentifierString(rawname, ',', elemlist))
2977
0
  {
2978
0
    GUC_check_errdetail("List syntax is invalid.");
2979
0
    return false;
2980
0
  }
2981
2982
  /* Iterate the list to validate each slot name */
2983
0
  foreach_ptr(char, name, *elemlist)
2984
0
  {
2985
0
    int     err_code;
2986
0
    char     *err_msg = NULL;
2987
0
    char     *err_hint = NULL;
2988
2989
0
    if (!ReplicationSlotValidateNameInternal(name, false, &err_code,
2990
0
                         &err_msg, &err_hint))
2991
0
    {
2992
0
      GUC_check_errcode(err_code);
2993
0
      GUC_check_errdetail("%s", err_msg);
2994
0
      if (err_hint != NULL)
2995
0
        GUC_check_errhint("%s", err_hint);
2996
0
      return false;
2997
0
    }
2998
0
  }
2999
3000
0
  return true;
3001
0
}
3002
3003
/*
3004
 * GUC check_hook for synchronized_standby_slots
3005
 */
3006
bool
3007
check_synchronized_standby_slots(char **newval, void **extra, GucSource source)
3008
2
{
3009
2
  char     *rawname;
3010
2
  char     *ptr;
3011
2
  List     *elemlist;
3012
2
  int     size;
3013
2
  bool    ok;
3014
2
  SyncStandbySlotsConfigData *config;
3015
3016
2
  if ((*newval)[0] == '\0')
3017
2
    return true;
3018
3019
  /* Need a modifiable copy of the GUC string */
3020
0
  rawname = pstrdup(*newval);
3021
3022
  /* Now verify if the specified slots exist and have correct type */
3023
0
  ok = validate_sync_standby_slots(rawname, &elemlist);
3024
3025
0
  if (!ok || elemlist == NIL)
3026
0
  {
3027
0
    pfree(rawname);
3028
0
    list_free(elemlist);
3029
0
    return ok;
3030
0
  }
3031
3032
  /* Compute the size required for the SyncStandbySlotsConfigData struct */
3033
0
  size = offsetof(SyncStandbySlotsConfigData, slot_names);
3034
0
  foreach_ptr(char, slot_name, elemlist)
3035
0
    size += strlen(slot_name) + 1;
3036
3037
  /* GUC extra value must be guc_malloc'd, not palloc'd */
3038
0
  config = (SyncStandbySlotsConfigData *) guc_malloc(LOG, size);
3039
0
  if (!config)
3040
0
    return false;
3041
3042
  /* Transform the data into SyncStandbySlotsConfigData */
3043
0
  config->nslotnames = list_length(elemlist);
3044
3045
0
  ptr = config->slot_names;
3046
0
  foreach_ptr(char, slot_name, elemlist)
3047
0
  {
3048
0
    strcpy(ptr, slot_name);
3049
0
    ptr += strlen(slot_name) + 1;
3050
0
  }
3051
3052
0
  *extra = config;
3053
3054
0
  pfree(rawname);
3055
0
  list_free(elemlist);
3056
0
  return true;
3057
0
}
3058
3059
/*
3060
 * GUC assign_hook for synchronized_standby_slots
3061
 */
3062
void
3063
assign_synchronized_standby_slots(const char *newval, void *extra)
3064
2
{
3065
  /*
3066
   * The standby slots may have changed, so we must recompute the oldest
3067
   * LSN.
3068
   */
3069
2
  ss_oldest_flush_lsn = InvalidXLogRecPtr;
3070
3071
2
  synchronized_standby_slots_config = (SyncStandbySlotsConfigData *) extra;
3072
2
}
3073
3074
/*
3075
 * Check if the passed slot_name is specified in the synchronized_standby_slots GUC.
3076
 */
3077
bool
3078
SlotExistsInSyncStandbySlots(const char *slot_name)
3079
0
{
3080
0
  const char *standby_slot_name;
3081
3082
  /* Return false if there is no value in synchronized_standby_slots */
3083
0
  if (synchronized_standby_slots_config == NULL)
3084
0
    return false;
3085
3086
  /*
3087
   * XXX: We are not expecting this list to be long so a linear search
3088
   * shouldn't hurt but if that turns out not to be true then we can cache
3089
   * this information for each WalSender as well.
3090
   */
3091
0
  standby_slot_name = synchronized_standby_slots_config->slot_names;
3092
0
  for (int i = 0; i < synchronized_standby_slots_config->nslotnames; i++)
3093
0
  {
3094
0
    if (strcmp(standby_slot_name, slot_name) == 0)
3095
0
      return true;
3096
3097
0
    standby_slot_name += strlen(standby_slot_name) + 1;
3098
0
  }
3099
3100
0
  return false;
3101
0
}
3102
3103
/*
3104
 * Return true if the slots specified in synchronized_standby_slots have caught up to
3105
 * the given WAL location, false otherwise.
3106
 *
3107
 * The elevel parameter specifies the error level used for logging messages
3108
 * related to slots that do not exist, are invalidated, or are inactive.
3109
 */
3110
bool
3111
StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel)
3112
0
{
3113
0
  const char *name;
3114
0
  int     caught_up_slot_num = 0;
3115
0
  XLogRecPtr  min_restart_lsn = InvalidXLogRecPtr;
3116
3117
  /*
3118
   * Don't need to wait for the standbys to catch up if there is no value in
3119
   * synchronized_standby_slots.
3120
   */
3121
0
  if (synchronized_standby_slots_config == NULL)
3122
0
    return true;
3123
3124
  /*
3125
   * Don't need to wait for the standbys to catch up if we are on a standby
3126
   * server, since we do not support syncing slots to cascading standbys.
3127
   */
3128
0
  if (RecoveryInProgress())
3129
0
    return true;
3130
3131
  /*
3132
   * Don't need to wait for the standbys to catch up if they are already
3133
   * beyond the specified WAL location.
3134
   */
3135
0
  if (XLogRecPtrIsValid(ss_oldest_flush_lsn) &&
3136
0
    ss_oldest_flush_lsn >= wait_for_lsn)
3137
0
    return true;
3138
3139
  /*
3140
   * To prevent concurrent slot dropping and creation while filtering the
3141
   * slots, take the ReplicationSlotControlLock outside of the loop.
3142
   */
3143
0
  LWLockAcquire(ReplicationSlotControlLock, LW_SHARED);
3144
3145
0
  name = synchronized_standby_slots_config->slot_names;
3146
0
  for (int i = 0; i < synchronized_standby_slots_config->nslotnames; i++)
3147
0
  {
3148
0
    XLogRecPtr  restart_lsn;
3149
0
    bool    invalidated;
3150
0
    bool    inactive;
3151
0
    ReplicationSlot *slot;
3152
3153
0
    slot = SearchNamedReplicationSlot(name, false);
3154
3155
    /*
3156
     * If a slot name provided in synchronized_standby_slots does not
3157
     * exist, report a message and exit the loop.
3158
     */
3159
0
    if (!slot)
3160
0
    {
3161
0
      ereport(elevel,
3162
0
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3163
0
          errmsg("replication slot \"%s\" specified in parameter \"%s\" does not exist",
3164
0
               name, "synchronized_standby_slots"),
3165
0
          errdetail("Logical replication is waiting on the standby associated with replication slot \"%s\".",
3166
0
                name),
3167
0
          errhint("Create the replication slot \"%s\" or amend parameter \"%s\".",
3168
0
              name, "synchronized_standby_slots"));
3169
0
      break;
3170
0
    }
3171
3172
    /* Same as above: if a slot is not physical, exit the loop. */
3173
0
    if (SlotIsLogical(slot))
3174
0
    {
3175
0
      ereport(elevel,
3176
0
          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3177
0
          errmsg("cannot specify logical replication slot \"%s\" in parameter \"%s\"",
3178
0
               name, "synchronized_standby_slots"),
3179
0
          errdetail("Logical replication is waiting for correction on replication slot \"%s\".",
3180
0
                name),
3181
0
          errhint("Remove the logical replication slot \"%s\" from parameter \"%s\".",
3182
0
              name, "synchronized_standby_slots"));
3183
0
      break;
3184
0
    }
3185
3186
0
    SpinLockAcquire(&slot->mutex);
3187
0
    restart_lsn = slot->data.restart_lsn;
3188
0
    invalidated = slot->data.invalidated != RS_INVAL_NONE;
3189
0
    inactive = slot->active_proc == INVALID_PROC_NUMBER;
3190
0
    SpinLockRelease(&slot->mutex);
3191
3192
0
    if (invalidated)
3193
0
    {
3194
      /* Specified physical slot has been invalidated */
3195
0
      ereport(elevel,
3196
0
          errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3197
0
          errmsg("physical replication slot \"%s\" specified in parameter \"%s\" has been invalidated",
3198
0
               name, "synchronized_standby_slots"),
3199
0
          errdetail("Logical replication is waiting on the standby associated with replication slot \"%s\".",
3200
0
                name),
3201
0
          errhint("Drop and recreate the replication slot \"%s\", or amend parameter \"%s\".",
3202
0
              name, "synchronized_standby_slots"));
3203
0
      break;
3204
0
    }
3205
3206
0
    if (!XLogRecPtrIsValid(restart_lsn) || restart_lsn < wait_for_lsn)
3207
0
    {
3208
      /* Log a message if no active_pid for this physical slot */
3209
0
      if (inactive)
3210
0
        ereport(elevel,
3211
0
            errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3212
0
            errmsg("replication slot \"%s\" specified in parameter \"%s\" does not have active_pid",
3213
0
                 name, "synchronized_standby_slots"),
3214
0
            errdetail("Logical replication is waiting on the standby associated with replication slot \"%s\".",
3215
0
                  name),
3216
0
            errhint("Start the standby associated with the replication slot \"%s\", or amend parameter \"%s\".",
3217
0
                name, "synchronized_standby_slots"));
3218
3219
      /* Continue if the current slot hasn't caught up. */
3220
0
      break;
3221
0
    }
3222
3223
0
    Assert(restart_lsn >= wait_for_lsn);
3224
3225
0
    if (!XLogRecPtrIsValid(min_restart_lsn) ||
3226
0
      min_restart_lsn > restart_lsn)
3227
0
      min_restart_lsn = restart_lsn;
3228
3229
0
    caught_up_slot_num++;
3230
3231
0
    name += strlen(name) + 1;
3232
0
  }
3233
3234
0
  LWLockRelease(ReplicationSlotControlLock);
3235
3236
  /*
3237
   * Return false if not all the standbys have caught up to the specified
3238
   * WAL location.
3239
   */
3240
0
  if (caught_up_slot_num != synchronized_standby_slots_config->nslotnames)
3241
0
    return false;
3242
3243
  /* The ss_oldest_flush_lsn must not retreat. */
3244
0
  Assert(!XLogRecPtrIsValid(ss_oldest_flush_lsn) ||
3245
0
       min_restart_lsn >= ss_oldest_flush_lsn);
3246
3247
0
  ss_oldest_flush_lsn = min_restart_lsn;
3248
3249
0
  return true;
3250
0
}
3251
3252
/*
3253
 * Wait for physical standbys to confirm receiving the given lsn.
3254
 *
3255
 * Used by logical decoding SQL functions. It waits for physical standbys
3256
 * corresponding to the physical slots specified in the synchronized_standby_slots GUC.
3257
 */
3258
void
3259
WaitForStandbyConfirmation(XLogRecPtr wait_for_lsn)
3260
0
{
3261
  /*
3262
   * Don't need to wait for the standby to catch up if the current acquired
3263
   * slot is not a logical failover slot, or there is no value in
3264
   * synchronized_standby_slots.
3265
   */
3266
0
  if (!MyReplicationSlot->data.failover || !synchronized_standby_slots_config)
3267
0
    return;
3268
3269
0
  ConditionVariablePrepareToSleep(&WalSndCtl->wal_confirm_rcv_cv);
3270
3271
0
  for (;;)
3272
0
  {
3273
0
    CHECK_FOR_INTERRUPTS();
3274
3275
0
    if (ConfigReloadPending)
3276
0
    {
3277
0
      ConfigReloadPending = false;
3278
0
      ProcessConfigFile(PGC_SIGHUP);
3279
0
    }
3280
3281
    /* Exit if done waiting for every slot. */
3282
0
    if (StandbySlotsHaveCaughtup(wait_for_lsn, WARNING))
3283
0
      break;
3284
3285
    /*
3286
     * Wait for the slots in the synchronized_standby_slots to catch up,
3287
     * but use a timeout (1s) so we can also check if the
3288
     * synchronized_standby_slots has been changed.
3289
     */
3290
0
    ConditionVariableTimedSleep(&WalSndCtl->wal_confirm_rcv_cv, 1000,
3291
0
                  WAIT_EVENT_WAIT_FOR_STANDBY_CONFIRMATION);
3292
0
  }
3293
3294
0
  ConditionVariableCancelSleep();
3295
0
}