Coverage Report

Created: 2026-11-12 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/commands/async.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * async.c
4
 *    Asynchronous notification: NOTIFY, LISTEN, UNLISTEN
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 * IDENTIFICATION
10
 *    src/backend/commands/async.c
11
 *
12
 *-------------------------------------------------------------------------
13
 */
14
15
/*-------------------------------------------------------------------------
16
 * Async Notification Model as of v19:
17
 *
18
 * 1. Multiple backends on same machine.  Multiple backends may be listening
19
 *    on each of several channels.
20
 *
21
 * 2. There is one central queue in disk-based storage (directory pg_notify/),
22
 *    with actively-used pages mapped into shared memory by the slru.c module.
23
 *    All notification messages are placed in the queue and later read out
24
 *    by listening backends.  The single queue allows us to guarantee that
25
 *    notifications are received in commit order.
26
 *
27
 *    Although there is only one queue, notifications are treated as being
28
 *    database-local; this is done by including the sender's database OID
29
 *    in each notification message.  Listening backends ignore messages
30
 *    that don't match their database OID.  This is important because it
31
 *    ensures senders and receivers have the same database encoding and won't
32
 *    misinterpret non-ASCII text in the channel name or payload string.
33
 *
34
 *    Since notifications are not expected to survive database crashes,
35
 *    we can simply clean out the pg_notify data at any reboot, and there
36
 *    is no need for WAL support or fsync'ing.
37
 *
38
 * 3. Every backend that is listening on at least one channel registers by
39
 *    entering its PID into the array in AsyncQueueControl. It then scans all
40
 *    incoming notifications in the central queue and first compares the
41
 *    database OID of the notification with its own database OID and then
42
 *    compares the notified channel with the list of channels that it listens
43
 *    to. In case there is a match it delivers the notification event to its
44
 *    frontend.  Non-matching events are simply skipped.
45
 *
46
 * 4. The NOTIFY statement (routine Async_Notify) stores the notification in
47
 *    a backend-local list which will not be processed until transaction end.
48
 *
49
 *    Duplicate notifications from the same transaction are sent out as one
50
 *    notification only. This is done to save work when for example a trigger
51
 *    on a 2 million row table fires a notification for each row that has been
52
 *    changed. If the application needs to receive every single notification
53
 *    that has been sent, it can easily add some unique string into the extra
54
 *    payload parameter.
55
 *
56
 *    When the transaction is ready to commit, PreCommit_Notify() adds the
57
 *    pending notifications to the head of the queue. The head pointer of the
58
 *    queue always points to the next free position and a position is just a
59
 *    page number and the offset in that page. This is done before marking the
60
 *    transaction as committed in clog. If we run into problems writing the
61
 *    notifications, we can still call elog(ERROR, ...) and the transaction
62
 *    will roll back safely.
63
 *
64
 *    Once we have put all of the notifications into the queue, we return to
65
 *    CommitTransaction() which will then do the actual transaction commit.
66
 *
67
 *    After commit we are called another time (AtCommit_Notify()). Here we
68
 *    make any required updates to the effective listen state (see below).
69
 *    Then we signal any backends that may be interested in our messages
70
 *    (including our own backend, if listening).  This is done by
71
 *    SignalBackends(), which sends a PROCSIG_NOTIFY_INTERRUPT signal to
72
 *    each relevant backend, as described below.
73
 *
74
 *    Finally, after we are out of the transaction altogether and about to go
75
 *    idle, we scan the queue for messages that need to be sent to our
76
 *    frontend (which might be notifies from other backends, or self-notifies
77
 *    from our own).  This step is not part of the CommitTransaction sequence
78
 *    for two important reasons.  First, we could get errors while sending
79
 *    data to our frontend, and it's really bad for errors to happen in
80
 *    post-commit cleanup.  Second, in cases where a procedure issues commits
81
 *    within a single frontend command, we don't want to send notifies to our
82
 *    frontend until the command is done; but notifies to other backends
83
 *    should go out immediately after each commit.
84
 *
85
 * 5. Upon receipt of a PROCSIG_NOTIFY_INTERRUPT signal, the signal handler
86
 *    sets the process's latch, which triggers the event to be processed
87
 *    immediately if this backend is idle (i.e., it is waiting for a frontend
88
 *    command and is not within a transaction block. C.f.
89
 *    ProcessClientReadInterrupt()).  Otherwise the handler may only set a
90
 *    flag, which will cause the processing to occur just before we next go
91
 *    idle.
92
 *
93
 *    Inbound-notify processing consists of reading all of the notifications
94
 *    that have arrived since scanning last time. We read every notification
95
 *    until we reach either a notification from an uncommitted transaction or
96
 *    the head pointer's position.
97
 *
98
 * 6. To limit disk space consumption, the tail pointer needs to be advanced
99
 *    so that old pages can be truncated. This is relatively expensive
100
 *    (notably, it requires an exclusive lock), so we don't want to do it
101
 *    often. We make sending backends do this work if they advanced the queue
102
 *    head into a new page, but only once every QUEUE_CLEANUP_DELAY pages.
103
 *
104
 * 7. So far we have not discussed how backends change their listening state,
105
 *    nor how notification senders know which backends to awaken.  To handle
106
 *    the latter, we maintain a global channel table (implemented as a dynamic
107
 *    shared hash table, or dshash) that maps channel names to the set of
108
 *    backends listening on each channel.  This table is created lazily on the
109
 *    first LISTEN command and grows dynamically as needed.  There is also a
110
 *    local channel table (a plain dynahash table) in each listening backend,
111
 *    tracking which channels that backend is listening to.  The local table
112
 *    serves to reduce the number of accesses needed to the shared table.
113
 *
114
 *    If the current transaction has executed any LISTEN/UNLISTEN actions,
115
 *    PreCommit_Notify() prepares to commit those.  For LISTEN, it
116
 *    pre-allocates entries in both the per-backend localChannelTable and the
117
 *    shared globalChannelTable, marking new shared entries removeOnAbort.
118
 *    It also records the final per-channel intent in pendingListenActions,
119
 *    so post-commit/abort processing can apply that in a single step.
120
 *    Since all these allocations happen before committing to clog, we can
121
 *    safely abort the transaction on failure.
122
 *
123
 *    After commit, AtCommit_Notify() runs through pendingListenActions and
124
 *    applies the final per-channel listen/unlisten state.  This happens
125
 *    before sending signals.
126
 *
127
 *    SignalBackends() consults the shared global channel table to identify
128
 *    listeners for the channels that the current transaction sent
129
 *    notification(s) to.  Each selected backend is marked as having a wakeup
130
 *    pending to avoid duplicate signals, and a PROCSIG_NOTIFY_INTERRUPT
131
 *    signal is sent to it.
132
 *
133
 * 8. While writing notifications, PreCommit_Notify() records the queue head
134
 *    position both before and after the write.  Because all writers serialize
135
 *    on a cluster-wide heavyweight lock, no other backend can insert entries
136
 *    between these two points.  SignalBackends() uses this fact to directly
137
 *    advance the queue pointer for any backend that is still positioned at
138
 *    the old head, or within the range written, but is not interested in any
139
 *    of our notifications.  This avoids unnecessary wakeups for idle
140
 *    listeners that have nothing to read.  Backends that are not interested
141
 *    in our notifications, but cannot be directly advanced, are signaled only
142
 *    if they are far behind the current queue head; that is to ensure that
143
 *    we can advance the queue tail without undue delay.
144
 *
145
 * An application that listens on the same channel it notifies will get
146
 * NOTIFY messages for its own NOTIFYs.  These can be ignored, if not useful,
147
 * by comparing be_pid in the NOTIFY message to the application's own backend's
148
 * PID.  (As of FE/BE protocol 2.0, the backend's PID is provided to the
149
 * frontend during startup.)  The above design guarantees that notifies from
150
 * other backends will never be missed by ignoring self-notifies.
151
 *
152
 * The amount of shared memory used for notify management (notify_buffers)
153
 * can be varied without affecting anything but performance.  The maximum
154
 * amount of notification data that can be queued at one time is determined
155
 * by the max_notify_queue_pages GUC.
156
 *-------------------------------------------------------------------------
157
 */
158
159
#include "postgres.h"
160
161
#include <limits.h>
162
#include <unistd.h>
163
#include <signal.h>
164
165
#include "access/parallel.h"
166
#include "access/slru.h"
167
#include "access/transam.h"
168
#include "access/xact.h"
169
#include "catalog/pg_database.h"
170
#include "commands/async.h"
171
#include "common/hashfn.h"
172
#include "funcapi.h"
173
#include "lib/dshash.h"
174
#include "libpq/libpq.h"
175
#include "libpq/pqformat.h"
176
#include "miscadmin.h"
177
#include "storage/dsm_registry.h"
178
#include "storage/ipc.h"
179
#include "storage/latch.h"
180
#include "storage/lmgr.h"
181
#include "storage/procsignal.h"
182
#include "storage/subsystems.h"
183
#include "tcop/tcopprot.h"
184
#include "utils/builtins.h"
185
#include "utils/dsa.h"
186
#include "utils/guc_hooks.h"
187
#include "utils/memutils.h"
188
#include "utils/ps_status.h"
189
#include "utils/snapmgr.h"
190
#include "utils/timestamp.h"
191
#include "utils/tuplestore.h"
192
193
194
/*
195
 * Maximum size of a NOTIFY payload, including terminating NULL.  This
196
 * must be kept small enough so that a notification message fits on one
197
 * SLRU page.  The magic fudge factor here is noncritical as long as it's
198
 * more than AsyncQueueEntryEmptySize --- we make it significantly bigger
199
 * than that, so changes in that data structure won't affect user-visible
200
 * restrictions.
201
 */
202
0
#define NOTIFY_PAYLOAD_MAX_LENGTH (BLCKSZ - NAMEDATALEN - 128)
203
204
/*
205
 * Struct representing an entry in the global notify queue
206
 *
207
 * This struct declaration has the maximal length, but in a real queue entry
208
 * the data area is only big enough for the actual channel and payload strings
209
 * (each null-terminated).  AsyncQueueEntryEmptySize is the minimum possible
210
 * entry size, if both channel and payload strings are empty (but note it
211
 * doesn't include alignment padding).
212
 *
213
 * The "length" field should always be rounded up to the next QUEUEALIGN
214
 * multiple so that all fields are properly aligned.
215
 */
216
typedef struct AsyncQueueEntry
217
{
218
  int     length;     /* total allocated length of entry */
219
  Oid     dboid;      /* sender's database OID */
220
  TransactionId xid;      /* sender's XID */
221
  int32   srcPid;     /* sender's PID */
222
  char    data[NAMEDATALEN + NOTIFY_PAYLOAD_MAX_LENGTH];
223
} AsyncQueueEntry;
224
225
/* Currently, no field of AsyncQueueEntry requires more than int alignment */
226
0
#define QUEUEALIGN(len)   INTALIGN(len)
227
228
0
#define AsyncQueueEntryEmptySize  (offsetof(AsyncQueueEntry, data) + 2)
229
230
/*
231
 * Struct describing a queue position, and assorted macros for working with it
232
 */
233
typedef struct QueuePosition
234
{
235
  int64   page;     /* SLRU page number */
236
  int     offset;     /* byte offset within page */
237
} QueuePosition;
238
239
0
#define QUEUE_POS_PAGE(x)   ((x).page)
240
0
#define QUEUE_POS_OFFSET(x)   ((x).offset)
241
242
#define SET_QUEUE_POS(x,y,z) \
243
0
  do { \
244
0
    (x).page = (y); \
245
0
    (x).offset = (z); \
246
0
  } while (0)
247
248
#define QUEUE_POS_EQUAL(x,y) \
249
0
  ((x).page == (y).page && (x).offset == (y).offset)
250
251
#define QUEUE_POS_IS_ZERO(x) \
252
0
  ((x).page == 0 && (x).offset == 0)
253
254
/* choose logically smaller QueuePosition */
255
#define QUEUE_POS_MIN(x,y) \
256
0
  (asyncQueuePagePrecedes((x).page, (y).page) ? (x) : \
257
0
   (x).page != (y).page ? (y) : \
258
0
   (x).offset < (y).offset ? (x) : (y))
259
260
/* choose logically larger QueuePosition */
261
#define QUEUE_POS_MAX(x,y) \
262
0
  (asyncQueuePagePrecedes((x).page, (y).page) ? (y) : \
263
0
   (x).page != (y).page ? (x) : \
264
0
   (x).offset > (y).offset ? (x) : (y))
265
266
/* returns true if x comes before y in queue order */
267
#define QUEUE_POS_PRECEDES(x,y) \
268
  (asyncQueuePagePrecedes((x).page, (y).page) || \
269
   ((x).page == (y).page && (x).offset < (y).offset))
270
271
/*
272
 * Parameter determining how often we try to advance the tail pointer:
273
 * we do that after every QUEUE_CLEANUP_DELAY pages of NOTIFY data.  This is
274
 * also the distance by which a backend that's not interested in our
275
 * notifications needs to be behind before we'll decide we need to wake it
276
 * up so it can advance its pointer.
277
 *
278
 * Resist the temptation to make this really large.  While that would save
279
 * work in some places, it would add cost in others.  In particular, this
280
 * should likely be less than notify_buffers, to ensure that backends
281
 * catch up before the pages they'll need to read fall out of SLRU cache.
282
 */
283
0
#define QUEUE_CLEANUP_DELAY 4
284
285
/*
286
 * Struct describing a listening backend's status
287
 */
288
typedef struct QueueBackendStatus
289
{
290
  int32   pid;      /* either a PID or InvalidPid */
291
  Oid     dboid;      /* backend's database OID, or InvalidOid */
292
  ProcNumber  nextListener; /* id of next listener, or INVALID_PROC_NUMBER */
293
  QueuePosition pos;      /* backend has read queue up to here */
294
  bool    wakeupPending;  /* signal sent to backend, not yet processed */
295
  bool    isAdvancing;  /* backend is advancing its position */
296
} QueueBackendStatus;
297
298
/*
299
 * Shared memory state for LISTEN/NOTIFY (excluding its SLRU stuff)
300
 *
301
 * The AsyncQueueControl structure is protected by the NotifyQueueLock and
302
 * NotifyQueueTailLock.
303
 *
304
 * When holding NotifyQueueLock in SHARED mode, backends may only inspect
305
 * their own entries as well as the head and tail pointers. Consequently we
306
 * can allow a backend to update its own record while holding only SHARED lock
307
 * (since no other backend will inspect it).
308
 *
309
 * When holding NotifyQueueLock in EXCLUSIVE mode, backends can inspect the
310
 * entries of other backends and also change the head pointer. They can
311
 * also advance other backends' queue positions, unless the other backend
312
 * has isAdvancing set (i.e., is in process of doing that itself).
313
 *
314
 * When holding both NotifyQueueLock and NotifyQueueTailLock in EXCLUSIVE
315
 * mode, backends can change the tail pointers.
316
 *
317
 * SLRU buffer pool is divided in banks and bank wise SLRU lock is used as
318
 * the control lock for the pg_notify SLRU buffers.
319
 * In order to avoid deadlocks, whenever we need multiple locks, we first get
320
 * NotifyQueueTailLock, then NotifyQueueLock, then SLRU bank lock, and lastly
321
 * globalChannelTable partition locks.
322
 *
323
 * Each backend uses the backend[] array entry with index equal to its
324
 * ProcNumber.  We rely on this to make SendProcSignal fast.
325
 *
326
 * The backend[] array entries for actively-listening backends are threaded
327
 * together using firstListener and the nextListener links, so that we can
328
 * scan them without having to iterate over inactive entries.  We keep this
329
 * list in order by ProcNumber so that the scan is cache-friendly when there
330
 * are many active entries.
331
 */
332
typedef struct AsyncQueueControl
333
{
334
  QueuePosition head;     /* head points to the next free location */
335
  QueuePosition tail;     /* tail must be <= the queue position of every
336
                 * listening backend */
337
  int64   stopPage;   /* oldest unrecycled page; must be <=
338
                 * tail.page */
339
  ProcNumber  firstListener;  /* id of first listener, or
340
                 * INVALID_PROC_NUMBER */
341
  TimestampTz lastQueueFillWarn;  /* time of last queue-full msg */
342
  dsa_handle  globalChannelTableDSA;  /* global channel table's DSA handle */
343
  dshash_table_handle globalChannelTableDSH;  /* and its dshash handle */
344
  /* Array with room for MaxBackends entries: */
345
  QueueBackendStatus backend[FLEXIBLE_ARRAY_MEMBER];
346
} AsyncQueueControl;
347
348
static AsyncQueueControl *asyncQueueControl;
349
350
static void AsyncShmemRequest(void *arg);
351
static void AsyncShmemInit(void *arg);
352
353
const ShmemCallbacks AsyncShmemCallbacks = {
354
  .request_fn = AsyncShmemRequest,
355
  .init_fn = AsyncShmemInit,
356
};
357
358
359
0
#define QUEUE_HEAD          (asyncQueueControl->head)
360
0
#define QUEUE_TAIL          (asyncQueueControl->tail)
361
0
#define QUEUE_STOP_PAGE       (asyncQueueControl->stopPage)
362
0
#define QUEUE_FIRST_LISTENER    (asyncQueueControl->firstListener)
363
0
#define QUEUE_BACKEND_PID(i)    (asyncQueueControl->backend[i].pid)
364
0
#define QUEUE_BACKEND_DBOID(i)    (asyncQueueControl->backend[i].dboid)
365
0
#define QUEUE_NEXT_LISTENER(i)    (asyncQueueControl->backend[i].nextListener)
366
0
#define QUEUE_BACKEND_POS(i)    (asyncQueueControl->backend[i].pos)
367
0
#define QUEUE_BACKEND_WAKEUP_PENDING(i) (asyncQueueControl->backend[i].wakeupPending)
368
0
#define QUEUE_BACKEND_IS_ADVANCING(i) (asyncQueueControl->backend[i].isAdvancing)
369
370
/*
371
 * The SLRU buffer area through which we access the notification queue
372
 */
373
static inline bool asyncQueuePagePrecedes(int64 p, int64 q);
374
static int  asyncQueueErrdetailForIoError(const void *opaque_data);
375
376
static SlruDesc NotifySlruDesc;
377
378
379
0
#define NotifyCtl         (&NotifySlruDesc)
380
0
#define QUEUE_PAGESIZE        BLCKSZ
381
382
#define QUEUE_FULL_WARN_INTERVAL  5000  /* warn at most once every 5s */
383
384
/*
385
 * Global channel table definitions
386
 *
387
 * This hash table maps (database OID, channel name) keys to arrays of
388
 * ProcNumbers representing the backends listening or about to listen
389
 * on each channel.  The removeOnAbort flags allow us to create hash table
390
 * entries pre-commit and not have to assume that creating them post-commit
391
 * will succeed.
392
 */
393
0
#define INITIAL_LISTENERS_ARRAY_SIZE 4
394
395
typedef struct GlobalChannelKey
396
{
397
  Oid     dboid;
398
  char    channel[NAMEDATALEN];
399
} GlobalChannelKey;
400
401
typedef struct ListenerEntry
402
{
403
  ProcNumber  procNo;     /* listener's ProcNumber */
404
  bool    removeOnAbort;  /* remove entry if current xact aborts */
405
} ListenerEntry;
406
407
typedef struct GlobalChannelEntry
408
{
409
  GlobalChannelKey key;   /* hash key */
410
  dsa_pointer listenersArray; /* DSA pointer to ListenerEntry array */
411
  int     numListeners; /* Number of listeners currently stored */
412
  int     allocatedListeners; /* Allocated size of array */
413
} GlobalChannelEntry;
414
415
static dshash_table *globalChannelTable = NULL;
416
static dsa_area *globalChannelDSA = NULL;
417
418
/*
419
 * localChannelTable caches the channel names this backend is listening on
420
 * (including those we have staged to be listened on, but not yet committed).
421
 * Used by IsListeningOn() for fast lookups when reading notifications.
422
 */
423
static HTAB *localChannelTable = NULL;
424
425
/* We test this condition to detect that we're not listening at all */
426
#define LocalChannelTableIsEmpty() \
427
0
  (localChannelTable == NULL || hash_get_num_entries(localChannelTable) == 0)
428
429
/*
430
 * State for pending LISTEN/UNLISTEN actions consists of an ordered list of
431
 * all actions requested in the current transaction.  As explained above,
432
 * we don't actually change listen state until we reach transaction commit.
433
 *
434
 * The list is kept in CurTransactionContext.  In subtransactions, each
435
 * subtransaction has its own list in its own CurTransactionContext, but
436
 * successful subtransactions attach their lists to their parent's list.
437
 * Failed subtransactions simply discard their lists.
438
 */
439
typedef enum
440
{
441
  LISTEN_LISTEN,
442
  LISTEN_UNLISTEN,
443
  LISTEN_UNLISTEN_ALL,
444
} ListenActionKind;
445
446
typedef struct
447
{
448
  ListenActionKind action;
449
  char    channel[FLEXIBLE_ARRAY_MEMBER]; /* nul-terminated string */
450
} ListenAction;
451
452
typedef struct ActionList
453
{
454
  int     nestingLevel; /* current transaction nesting depth */
455
  List     *actions;    /* list of ListenAction structs */
456
  struct ActionList *upper; /* details for upper transaction levels */
457
} ActionList;
458
459
static ActionList *pendingActions = NULL;
460
461
/*
462
 * Hash table recording the final listen/unlisten intent per channel for
463
 * the current transaction.  Key is channel name, value is PENDING_LISTEN or
464
 * PENDING_UNLISTEN.  This keeps critical commit/abort processing to one step
465
 * per channel instead of replaying every action.  This is built from the
466
 * pendingActions list by PreCommit_Notify, then used by AtCommit_Notify or
467
 * AtAbort_Notify.
468
 */
469
typedef enum
470
{
471
  PENDING_LISTEN,
472
  PENDING_UNLISTEN,
473
} PendingListenAction;
474
475
typedef struct PendingListenEntry
476
{
477
  char    channel[NAMEDATALEN]; /* hash key */
478
  PendingListenAction action; /* which action should we perform? */
479
} PendingListenEntry;
480
481
static HTAB *pendingListenActions = NULL;
482
483
/*
484
 * State for outbound notifies consists of a list of all channels+payloads
485
 * NOTIFYed in the current transaction.  We do not actually perform a NOTIFY
486
 * until and unless the transaction commits.  pendingNotifies is NULL if no
487
 * NOTIFYs have been done in the current (sub) transaction.
488
 *
489
 * We discard duplicate notify events issued in the same transaction.
490
 * Hence, in addition to the list proper (which we need to track the order
491
 * of the events, since we guarantee to deliver them in order), we build a
492
 * hash table which we can probe to detect duplicates.  Since building the
493
 * hash table is somewhat expensive, we do so only once we have at least
494
 * MIN_HASHABLE_NOTIFIES events queued in the current (sub) transaction;
495
 * before that we just scan the events linearly.
496
 *
497
 * The list is kept in CurTransactionContext.  In subtransactions, each
498
 * subtransaction has its own list in its own CurTransactionContext, but
499
 * successful subtransactions add their entries to their parent's list.
500
 * Failed subtransactions simply discard their lists.  Since these lists
501
 * are independent, there may be notify events in a subtransaction's list
502
 * that duplicate events in some ancestor (sub) transaction; we get rid of
503
 * the dups when merging the subtransaction's list into its parent's.
504
 *
505
 * Note: the action and notify lists do not interact within a transaction.
506
 * In particular, if a transaction does NOTIFY and then LISTEN on the same
507
 * condition name, it will get a self-notify at commit.  This is a bit odd
508
 * but is consistent with our historical behavior.
509
 */
510
typedef struct Notification
511
{
512
  uint16    channel_len;  /* length of channel-name string */
513
  uint16    payload_len;  /* length of payload string */
514
  /* null-terminated channel name, then null-terminated payload follow */
515
  char    data[FLEXIBLE_ARRAY_MEMBER];
516
} Notification;
517
518
typedef struct NotificationList
519
{
520
  int     nestingLevel; /* current transaction nesting depth */
521
  List     *events;     /* list of Notification structs */
522
  HTAB     *hashtab;    /* hash of NotificationHash structs, or NULL */
523
  List     *uniqueChannelNames; /* unique channel names being notified */
524
  HTAB     *uniqueChannelHash;  /* hash of unique channel names, or NULL */
525
  struct NotificationList *upper; /* details for upper transaction levels */
526
} NotificationList;
527
528
0
#define MIN_HASHABLE_NOTIFIES 16  /* threshold to build hashtab */
529
530
struct NotificationHash
531
{
532
  Notification *event;    /* => the actual Notification struct */
533
};
534
535
static NotificationList *pendingNotifies = NULL;
536
537
/*
538
 * Hash entry in NotificationList.uniqueChannelHash or localChannelTable
539
 * (both just carry the channel name, with no payload).
540
 */
541
typedef struct ChannelName
542
{
543
  char    channel[NAMEDATALEN]; /* hash key */
544
} ChannelName;
545
546
/*
547
 * Inbound notifications are initially processed by HandleNotifyInterrupt(),
548
 * called from inside a signal handler. That just sets the
549
 * notifyInterruptPending flag and sets the process
550
 * latch. ProcessNotifyInterrupt() will then be called whenever it's safe to
551
 * actually deal with the interrupt.
552
 */
553
volatile sig_atomic_t notifyInterruptPending = false;
554
555
/* True if we've registered an on_shmem_exit cleanup */
556
static bool unlistenExitRegistered = false;
557
558
/* True if we're currently registered as a listener in asyncQueueControl */
559
static bool amRegisteredListener = false;
560
561
/*
562
 * Queue head positions for direct advancement.
563
 * These are captured during PreCommit_Notify while holding the heavyweight
564
 * lock on database 0, ensuring no other backend can insert notifications
565
 * between them.  SignalBackends uses these to advance idle backends.
566
 */
567
static QueuePosition queueHeadBeforeWrite;
568
static QueuePosition queueHeadAfterWrite;
569
570
/*
571
 * Workspace arrays for SignalBackends.  These are preallocated in
572
 * PreCommit_Notify to avoid needing memory allocation after committing to
573
 * clog.
574
 */
575
static int32 *signalPids = NULL;
576
static ProcNumber *signalProcnos = NULL;
577
578
/* have we advanced to a page that's a multiple of QUEUE_CLEANUP_DELAY? */
579
static bool tryAdvanceTail = false;
580
581
/* GUC parameters */
582
bool    Trace_notify = false;
583
584
/* For 8 KB pages this gives 8 GB of disk space */
585
int     max_notify_queue_pages = 1048576;
586
587
/* local function prototypes */
588
static inline int64 asyncQueuePageDiff(int64 p, int64 q);
589
static inline void GlobalChannelKeyInit(GlobalChannelKey *key, Oid dboid,
590
                    const char *channel);
591
static dshash_hash globalChannelTableHash(const void *key, size_t size,
592
                      void *arg);
593
static void initGlobalChannelTable(void);
594
static void initLocalChannelTable(void);
595
static void queue_listen(ListenActionKind action, const char *channel);
596
static void Async_UnlistenOnExit(int code, Datum arg);
597
static void BecomeRegisteredListener(void);
598
static void PrepareTableEntriesForListen(const char *channel);
599
static void PrepareTableEntriesForUnlisten(const char *channel);
600
static void PrepareTableEntriesForUnlistenAll(void);
601
static void RemoveListenerFromChannel(GlobalChannelEntry **entry_ptr,
602
                    ListenerEntry *listeners,
603
                    int idx);
604
static void ApplyPendingListenActions(bool isCommit);
605
static void CleanupListenersOnExit(void);
606
static bool IsListeningOn(const char *channel);
607
static void asyncQueueUnregister(void);
608
static bool asyncQueueIsFull(void);
609
static bool asyncQueueAdvance(QueuePosition *position, int entryLength);
610
static void asyncQueueNotificationToEntry(Notification *n, AsyncQueueEntry *qe);
611
static ListCell *asyncQueueAddEntries(ListCell *nextNotify);
612
static double asyncQueueUsage(void);
613
static void asyncQueueFillWarning(void);
614
static void SignalBackends(void);
615
static void asyncQueueReadAllNotifications(void);
616
static bool asyncQueueProcessPageEntries(QueuePosition *current,
617
                     QueuePosition stop,
618
                     Snapshot snapshot);
619
static void asyncQueueAdvanceTail(void);
620
static void ProcessIncomingNotify(bool flush);
621
static bool AsyncExistsPendingNotify(Notification *n);
622
static void AddEventToPendingNotifies(Notification *n);
623
static uint32 notification_hash(const void *key, Size keysize);
624
static int  notification_match(const void *key1, const void *key2, Size keysize);
625
static void ClearPendingActionsAndNotifies(void);
626
627
static int
628
asyncQueueErrdetailForIoError(const void *opaque_data)
629
0
{
630
0
  const QueuePosition *position = opaque_data;
631
632
0
  return errdetail("Could not access async queue at page %" PRId64 ", offset %d.",
633
0
           position->page, position->offset);
634
0
}
635
636
/*
637
 * Compute the difference between two queue page numbers.
638
 * Previously this function accounted for a wraparound.
639
 */
640
static inline int64
641
asyncQueuePageDiff(int64 p, int64 q)
642
0
{
643
0
  return p - q;
644
0
}
645
646
/*
647
 * Determines whether p precedes q.
648
 * Previously this function accounted for a wraparound.
649
 */
650
static inline bool
651
asyncQueuePagePrecedes(int64 p, int64 q)
652
0
{
653
0
  return p < q;
654
0
}
655
656
/*
657
 * GlobalChannelKeyInit
658
 *    Prepare a global channel table key for hashing.
659
 */
660
static inline void
661
GlobalChannelKeyInit(GlobalChannelKey *key, Oid dboid, const char *channel)
662
0
{
663
0
  memset(key, 0, sizeof(GlobalChannelKey));
664
0
  key->dboid = dboid;
665
0
  strlcpy(key->channel, channel, NAMEDATALEN);
666
0
}
667
668
/*
669
 * globalChannelTableHash
670
 *    Hash function for global channel table keys.
671
 */
672
static dshash_hash
673
globalChannelTableHash(const void *key, size_t size, void *arg)
674
0
{
675
0
  const GlobalChannelKey *k = (const GlobalChannelKey *) key;
676
0
  dshash_hash h;
677
678
0
  h = DatumGetUInt32(hash_uint32(k->dboid));
679
0
  h ^= hash_bytes((const unsigned char *) k->channel,
680
0
          strnlen(k->channel, NAMEDATALEN));
681
682
0
  return h;
683
0
}
684
685
/* parameters for the global channel table */
686
static const dshash_parameters globalChannelTableDSHParams = {
687
  sizeof(GlobalChannelKey),
688
  sizeof(GlobalChannelEntry),
689
  dshash_memcmp,
690
  globalChannelTableHash,
691
  dshash_memcpy,
692
  LWTRANCHE_NOTIFY_CHANNEL_HASH
693
};
694
695
/*
696
 * initGlobalChannelTable
697
 *    Lazy initialization of the global channel table.
698
 */
699
static void
700
initGlobalChannelTable(void)
701
0
{
702
0
  MemoryContext oldcontext;
703
704
  /* Quick exit if we already did this */
705
0
  if (asyncQueueControl->globalChannelTableDSH != DSHASH_HANDLE_INVALID &&
706
0
    globalChannelTable != NULL)
707
0
    return;
708
709
  /* Otherwise, use a lock to ensure only one process creates the table */
710
0
  LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
711
712
  /* Be sure any local memory allocated by DSA routines is persistent */
713
0
  oldcontext = MemoryContextSwitchTo(TopMemoryContext);
714
715
0
  if (asyncQueueControl->globalChannelTableDSH == DSHASH_HANDLE_INVALID)
716
0
  {
717
    /* Initialize dynamic shared hash table for global channels */
718
0
    globalChannelDSA = dsa_create(LWTRANCHE_NOTIFY_CHANNEL_HASH);
719
0
    dsa_pin(globalChannelDSA);
720
0
    dsa_pin_mapping(globalChannelDSA);
721
0
    globalChannelTable = dshash_create(globalChannelDSA,
722
0
                       &globalChannelTableDSHParams,
723
0
                       NULL);
724
725
    /* Store handles in shared memory for other backends to use */
726
0
    asyncQueueControl->globalChannelTableDSA = dsa_get_handle(globalChannelDSA);
727
0
    asyncQueueControl->globalChannelTableDSH =
728
0
      dshash_get_hash_table_handle(globalChannelTable);
729
0
  }
730
0
  else if (!globalChannelTable)
731
0
  {
732
    /* Attach to existing dynamic shared hash table */
733
0
    globalChannelDSA = dsa_attach(asyncQueueControl->globalChannelTableDSA);
734
0
    dsa_pin_mapping(globalChannelDSA);
735
0
    globalChannelTable = dshash_attach(globalChannelDSA,
736
0
                       &globalChannelTableDSHParams,
737
0
                       asyncQueueControl->globalChannelTableDSH,
738
0
                       NULL);
739
0
  }
740
741
0
  MemoryContextSwitchTo(oldcontext);
742
0
  LWLockRelease(NotifyQueueLock);
743
0
}
744
745
/*
746
 * initLocalChannelTable
747
 *    Lazy initialization of the local channel table.
748
 *    Once created, this table lasts for the life of the session.
749
 */
750
static void
751
initLocalChannelTable(void)
752
0
{
753
0
  HASHCTL   hash_ctl;
754
755
  /* Quick exit if we already did this */
756
0
  if (localChannelTable != NULL)
757
0
    return;
758
759
  /* Initialize local hash table for this backend's listened channels */
760
0
  hash_ctl.keysize = NAMEDATALEN;
761
0
  hash_ctl.entrysize = sizeof(ChannelName);
762
763
0
  localChannelTable =
764
0
    hash_create("Local Listen Channels",
765
0
          64,
766
0
          &hash_ctl,
767
0
          HASH_ELEM | HASH_STRINGS);
768
0
}
769
770
/*
771
 * initPendingListenActions
772
 *    Lazy initialization of the pending listen actions hash table.
773
 *    This is allocated in CurTransactionContext during PreCommit_Notify,
774
 *    and destroyed at transaction end.
775
 */
776
static void
777
initPendingListenActions(void)
778
0
{
779
0
  HASHCTL   hash_ctl;
780
781
0
  if (pendingListenActions != NULL)
782
0
    return;
783
784
0
  hash_ctl.keysize = NAMEDATALEN;
785
0
  hash_ctl.entrysize = sizeof(PendingListenEntry);
786
0
  hash_ctl.hcxt = CurTransactionContext;
787
788
0
  pendingListenActions =
789
0
    hash_create("Pending Listen Actions",
790
0
          list_length(pendingActions->actions),
791
0
          &hash_ctl,
792
0
          HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
793
0
}
794
795
/*
796
 * Register our shared memory needs
797
 */
798
static void
799
AsyncShmemRequest(void *arg)
800
0
{
801
0
  Size    size;
802
803
0
  size = mul_size(MaxBackends, sizeof(QueueBackendStatus));
804
0
  size = add_size(size, offsetof(AsyncQueueControl, backend));
805
806
0
  ShmemRequestStruct(.name = "Async Queue Control",
807
0
             .size = size,
808
0
             .ptr = (void **) &asyncQueueControl,
809
0
    );
810
811
0
  SimpleLruRequest(.desc = &NotifySlruDesc,
812
0
           .name = "notify",
813
0
           .Dir = "pg_notify",
814
815
  /* long segment names are used in order to avoid wraparound */
816
0
           .long_segment_names = true,
817
818
0
           .nslots = notify_buffers,
819
820
0
           .sync_handler = SYNC_HANDLER_NONE,
821
0
           .PagePrecedes = asyncQueuePagePrecedes,
822
0
           .errdetail_for_io_error = asyncQueueErrdetailForIoError,
823
824
0
           .buffer_tranche_id = LWTRANCHE_NOTIFY_BUFFER,
825
0
           .bank_tranche_id = LWTRANCHE_NOTIFY_SLRU,
826
0
    );
827
0
}
828
829
static void
830
AsyncShmemInit(void *arg)
831
0
{
832
0
  SET_QUEUE_POS(QUEUE_HEAD, 0, 0);
833
0
  SET_QUEUE_POS(QUEUE_TAIL, 0, 0);
834
0
  QUEUE_STOP_PAGE = 0;
835
0
  QUEUE_FIRST_LISTENER = INVALID_PROC_NUMBER;
836
0
  asyncQueueControl->lastQueueFillWarn = 0;
837
0
  asyncQueueControl->globalChannelTableDSA = DSA_HANDLE_INVALID;
838
0
  asyncQueueControl->globalChannelTableDSH = DSHASH_HANDLE_INVALID;
839
0
  for (int i = 0; i < MaxBackends; i++)
840
0
  {
841
0
    QUEUE_BACKEND_PID(i) = InvalidPid;
842
0
    QUEUE_BACKEND_DBOID(i) = InvalidOid;
843
0
    QUEUE_NEXT_LISTENER(i) = INVALID_PROC_NUMBER;
844
0
    SET_QUEUE_POS(QUEUE_BACKEND_POS(i), 0, 0);
845
0
    QUEUE_BACKEND_WAKEUP_PENDING(i) = false;
846
0
    QUEUE_BACKEND_IS_ADVANCING(i) = false;
847
0
  }
848
849
  /*
850
   * During start or reboot, clean out the pg_notify directory.
851
   */
852
0
  (void) SlruScanDirectory(NotifyCtl, SlruScanDirCbDeleteAll, NULL);
853
0
}
854
855
856
/*
857
 * pg_notify -
858
 *    SQL function to send a notification event
859
 */
860
Datum
861
pg_notify(PG_FUNCTION_ARGS)
862
0
{
863
0
  const char *channel;
864
0
  const char *payload;
865
866
0
  if (PG_ARGISNULL(0))
867
0
    channel = "";
868
0
  else
869
0
    channel = text_to_cstring(PG_GETARG_TEXT_PP(0));
870
871
0
  if (PG_ARGISNULL(1))
872
0
    payload = "";
873
0
  else
874
0
    payload = text_to_cstring(PG_GETARG_TEXT_PP(1));
875
876
  /* For NOTIFY as a statement, this is checked in ProcessUtility */
877
0
  PreventCommandDuringRecovery("NOTIFY");
878
879
0
  Async_Notify(channel, payload);
880
881
0
  PG_RETURN_VOID();
882
0
}
883
884
885
/*
886
 * Async_Notify
887
 *
888
 *    This is executed by the SQL notify command.
889
 *
890
 *    Adds the message to the list of pending notifies.
891
 *    Actual notification happens during transaction commit.
892
 *    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
893
 */
894
void
895
Async_Notify(const char *channel, const char *payload)
896
0
{
897
0
  int     my_level = GetCurrentTransactionNestLevel();
898
0
  size_t    channel_len;
899
0
  size_t    payload_len;
900
0
  Notification *n;
901
0
  MemoryContext oldcontext;
902
903
0
  if (IsParallelWorker())
904
0
    elog(ERROR, "cannot send notifications from a parallel worker");
905
906
0
  if (Trace_notify)
907
0
    elog(DEBUG1, "Async_Notify(%s)", channel);
908
909
0
  channel_len = channel ? strlen(channel) : 0;
910
0
  payload_len = payload ? strlen(payload) : 0;
911
912
  /* a channel name must be specified */
913
0
  if (channel_len == 0)
914
0
    ereport(ERROR,
915
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
916
0
         errmsg("channel name cannot be empty")));
917
918
  /* enforce length limits */
919
0
  if (channel_len >= NAMEDATALEN)
920
0
    ereport(ERROR,
921
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
922
0
         errmsg("channel name too long")));
923
924
0
  if (payload_len >= NOTIFY_PAYLOAD_MAX_LENGTH)
925
0
    ereport(ERROR,
926
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
927
0
         errmsg("payload string too long")));
928
929
  /*
930
   * We must construct the Notification entry, even if we end up not using
931
   * it, in order to compare it cheaply to existing list entries.
932
   *
933
   * The notification list needs to live until end of transaction, so store
934
   * it in the transaction context.
935
   */
936
0
  oldcontext = MemoryContextSwitchTo(CurTransactionContext);
937
938
0
  n = (Notification *) palloc(offsetof(Notification, data) +
939
0
                channel_len + payload_len + 2);
940
0
  n->channel_len = channel_len;
941
0
  n->payload_len = payload_len;
942
0
  strcpy(n->data, channel);
943
0
  if (payload)
944
0
    strcpy(n->data + channel_len + 1, payload);
945
0
  else
946
0
    n->data[channel_len + 1] = '\0';
947
948
0
  if (pendingNotifies == NULL || my_level > pendingNotifies->nestingLevel)
949
0
  {
950
0
    NotificationList *notifies;
951
952
    /*
953
     * First notify event in current (sub)xact. Note that we allocate the
954
     * NotificationList in TopTransactionContext; the nestingLevel might
955
     * get changed later by AtSubCommit_Notify.
956
     */
957
0
    notifies = (NotificationList *)
958
0
      MemoryContextAlloc(TopTransactionContext,
959
0
                 sizeof(NotificationList));
960
0
    notifies->nestingLevel = my_level;
961
0
    notifies->events = list_make1(n);
962
    /* We certainly don't need a hashtable yet */
963
0
    notifies->hashtab = NULL;
964
    /* We won't build uniqueChannelNames/Hash till later, either */
965
0
    notifies->uniqueChannelNames = NIL;
966
0
    notifies->uniqueChannelHash = NULL;
967
0
    notifies->upper = pendingNotifies;
968
0
    pendingNotifies = notifies;
969
0
  }
970
0
  else
971
0
  {
972
    /* Now check for duplicates */
973
0
    if (AsyncExistsPendingNotify(n))
974
0
    {
975
      /* It's a dup, so forget it */
976
0
      pfree(n);
977
0
      MemoryContextSwitchTo(oldcontext);
978
0
      return;
979
0
    }
980
981
    /* Append more events to existing list */
982
0
    AddEventToPendingNotifies(n);
983
0
  }
984
985
0
  MemoryContextSwitchTo(oldcontext);
986
0
}
987
988
/*
989
 * queue_listen
990
 *    Common code for listen, unlisten, unlisten all commands.
991
 *
992
 *    Adds the request to the list of pending actions.
993
 *    Actual update of localChannelTable and globalChannelTable happens during
994
 *    PreCommit_Notify, with staged changes committed in AtCommit_Notify.
995
 */
996
static void
997
queue_listen(ListenActionKind action, const char *channel)
998
0
{
999
0
  MemoryContext oldcontext;
1000
0
  ListenAction *actrec;
1001
0
  int     my_level = GetCurrentTransactionNestLevel();
1002
1003
  /*
1004
   * Unlike Async_Notify, we don't try to collapse out duplicates here. We
1005
   * keep the ordered list to preserve interactions like UNLISTEN ALL; the
1006
   * final per-channel intent is computed during PreCommit_Notify.
1007
   */
1008
0
  oldcontext = MemoryContextSwitchTo(CurTransactionContext);
1009
1010
  /* space for terminating null is included in sizeof(ListenAction) */
1011
0
  actrec = (ListenAction *) palloc(offsetof(ListenAction, channel) +
1012
0
                   strlen(channel) + 1);
1013
0
  actrec->action = action;
1014
0
  strcpy(actrec->channel, channel);
1015
1016
0
  if (pendingActions == NULL || my_level > pendingActions->nestingLevel)
1017
0
  {
1018
0
    ActionList *actions;
1019
1020
    /*
1021
     * First action in current sub(xact). Note that we allocate the
1022
     * ActionList in TopTransactionContext; the nestingLevel might get
1023
     * changed later by AtSubCommit_Notify.
1024
     */
1025
0
    actions = (ActionList *)
1026
0
      MemoryContextAlloc(TopTransactionContext, sizeof(ActionList));
1027
0
    actions->nestingLevel = my_level;
1028
0
    actions->actions = list_make1(actrec);
1029
0
    actions->upper = pendingActions;
1030
0
    pendingActions = actions;
1031
0
  }
1032
0
  else
1033
0
    pendingActions->actions = lappend(pendingActions->actions, actrec);
1034
1035
0
  MemoryContextSwitchTo(oldcontext);
1036
0
}
1037
1038
/*
1039
 * Async_Listen
1040
 *
1041
 *    This is executed by the SQL listen command.
1042
 */
1043
void
1044
Async_Listen(const char *channel)
1045
{
1046
  if (Trace_notify)
1047
    elog(DEBUG1, "Async_Listen(%s,%d)", channel, MyProcPid);
1048
1049
  queue_listen(LISTEN_LISTEN, channel);
1050
}
1051
1052
/*
1053
 * Async_Unlisten
1054
 *
1055
 *    This is executed by the SQL unlisten command.
1056
 */
1057
void
1058
Async_Unlisten(const char *channel)
1059
0
{
1060
0
  if (Trace_notify)
1061
0
    elog(DEBUG1, "Async_Unlisten(%s,%d)", channel, MyProcPid);
1062
1063
  /* If we couldn't possibly be listening, no need to queue anything */
1064
0
  if (pendingActions == NULL && !unlistenExitRegistered)
1065
0
    return;
1066
1067
0
  queue_listen(LISTEN_UNLISTEN, channel);
1068
0
}
1069
1070
/*
1071
 * Async_UnlistenAll
1072
 *
1073
 *    This is invoked by UNLISTEN * command, and also at backend exit.
1074
 */
1075
void
1076
Async_UnlistenAll(void)
1077
0
{
1078
0
  if (Trace_notify)
1079
0
    elog(DEBUG1, "Async_UnlistenAll(%d)", MyProcPid);
1080
1081
  /* If we couldn't possibly be listening, no need to queue anything */
1082
0
  if (pendingActions == NULL && !unlistenExitRegistered)
1083
0
    return;
1084
1085
0
  queue_listen(LISTEN_UNLISTEN_ALL, "");
1086
0
}
1087
1088
/*
1089
 * SQL function: return a set of the channel names this backend is actively
1090
 * listening to.
1091
 */
1092
Datum
1093
pg_listening_channels(PG_FUNCTION_ARGS)
1094
0
{
1095
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1096
1097
0
  InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
1098
1099
  /*
1100
   * Complete the hash scan in one call, since the caller might not fetch
1101
   * all the rows and would otherwise leave the scan registered until
1102
   * transaction end.
1103
   */
1104
0
  if (localChannelTable != NULL)
1105
0
  {
1106
0
    HASH_SEQ_STATUS status;
1107
0
    ChannelName *channelEntry;
1108
1109
0
    hash_seq_init(&status, localChannelTable);
1110
0
    while ((channelEntry = (ChannelName *) hash_seq_search(&status)) != NULL)
1111
0
    {
1112
0
      Datum   values[1];
1113
0
      bool    nulls[1];
1114
1115
0
      values[0] = CStringGetTextDatum(channelEntry->channel);
1116
0
      nulls[0] = false;
1117
0
      tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
1118
0
                 values, nulls);
1119
0
    }
1120
0
  }
1121
1122
0
  return (Datum) 0;
1123
0
}
1124
1125
/*
1126
 * Async_UnlistenOnExit
1127
 *
1128
 * This is executed at backend exit if we have done any LISTENs in this
1129
 * backend.  It might not be necessary anymore, if the user UNLISTENed
1130
 * everything, but we don't try to detect that case.
1131
 */
1132
static void
1133
Async_UnlistenOnExit(int code, Datum arg)
1134
0
{
1135
0
  CleanupListenersOnExit();
1136
0
  asyncQueueUnregister();
1137
0
}
1138
1139
/*
1140
 * AtPrepare_Notify
1141
 *
1142
 *    This is called at the prepare phase of a two-phase
1143
 *    transaction.  Save the state for possible commit later.
1144
 */
1145
void
1146
AtPrepare_Notify(void)
1147
0
{
1148
  /* It's not allowed to have any pending LISTEN/UNLISTEN/NOTIFY actions */
1149
0
  if (pendingActions || pendingNotifies)
1150
0
    ereport(ERROR,
1151
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1152
0
         errmsg("cannot PREPARE a transaction that has executed LISTEN, UNLISTEN, or NOTIFY")));
1153
0
}
1154
1155
/*
1156
 * PreCommit_Notify
1157
 *
1158
 *    This is called at transaction commit, before actually committing to
1159
 *    clog.
1160
 *
1161
 *    If there are pending LISTEN actions, make sure we are listed in the
1162
 *    shared-memory listener array.  This must happen before commit to
1163
 *    ensure we don't miss any notifies from transactions that commit
1164
 *    just after ours.
1165
 *
1166
 *    If there are outbound notify requests in the pendingNotifies list,
1167
 *    add them to the global queue.  We do that before commit so that
1168
 *    we can still throw error if we run out of queue space.
1169
 */
1170
void
1171
PreCommit_Notify(void)
1172
0
{
1173
0
  ListCell   *p;
1174
1175
0
  if (!pendingActions && !pendingNotifies)
1176
0
    return;         /* no relevant statements in this xact */
1177
1178
0
  if (Trace_notify)
1179
0
    elog(DEBUG1, "PreCommit_Notify");
1180
1181
  /* Preflight for any pending listen/unlisten actions */
1182
0
  initGlobalChannelTable();
1183
1184
0
  if (pendingActions != NULL)
1185
0
  {
1186
    /* Ensure we have a local channel table */
1187
0
    initLocalChannelTable();
1188
    /* Create pendingListenActions hash table for this transaction */
1189
0
    initPendingListenActions();
1190
1191
    /* Stage all the actions this transaction wants to perform */
1192
0
    foreach(p, pendingActions->actions)
1193
0
    {
1194
0
      ListenAction *actrec = (ListenAction *) lfirst(p);
1195
1196
0
      switch (actrec->action)
1197
0
      {
1198
0
        case LISTEN_LISTEN:
1199
0
          BecomeRegisteredListener();
1200
0
          PrepareTableEntriesForListen(actrec->channel);
1201
0
          break;
1202
0
        case LISTEN_UNLISTEN:
1203
0
          PrepareTableEntriesForUnlisten(actrec->channel);
1204
0
          break;
1205
0
        case LISTEN_UNLISTEN_ALL:
1206
0
          PrepareTableEntriesForUnlistenAll();
1207
0
          break;
1208
0
      }
1209
0
    }
1210
0
  }
1211
1212
  /* Queue any pending notifies (must happen after the above) */
1213
0
  if (pendingNotifies)
1214
0
  {
1215
0
    ListCell   *nextNotify;
1216
0
    bool    firstIteration = true;
1217
1218
    /*
1219
     * Build list of unique channel names being notified for use by
1220
     * SignalBackends().
1221
     *
1222
     * If uniqueChannelHash is available, use it to efficiently get the
1223
     * unique channels.  Otherwise, fall back to the O(N^2) approach.
1224
     */
1225
0
    pendingNotifies->uniqueChannelNames = NIL;
1226
0
    if (pendingNotifies->uniqueChannelHash != NULL)
1227
0
    {
1228
0
      HASH_SEQ_STATUS status;
1229
0
      ChannelName *channelEntry;
1230
1231
0
      hash_seq_init(&status, pendingNotifies->uniqueChannelHash);
1232
0
      while ((channelEntry = (ChannelName *) hash_seq_search(&status)) != NULL)
1233
0
        pendingNotifies->uniqueChannelNames =
1234
0
          lappend(pendingNotifies->uniqueChannelNames,
1235
0
              channelEntry->channel);
1236
0
    }
1237
0
    else
1238
0
    {
1239
      /* O(N^2) approach is better for small number of notifications */
1240
0
      foreach_ptr(Notification, n, pendingNotifies->events)
1241
0
      {
1242
0
        char     *channel = n->data;
1243
0
        bool    found = false;
1244
1245
        /* Name present in list? */
1246
0
        foreach_ptr(char, oldchan, pendingNotifies->uniqueChannelNames)
1247
0
        {
1248
0
          if (strcmp(oldchan, channel) == 0)
1249
0
          {
1250
0
            found = true;
1251
0
            break;
1252
0
          }
1253
0
        }
1254
        /* Add if not already in list */
1255
0
        if (!found)
1256
0
          pendingNotifies->uniqueChannelNames =
1257
0
            lappend(pendingNotifies->uniqueChannelNames,
1258
0
                channel);
1259
0
      }
1260
0
    }
1261
1262
    /* Preallocate workspace that will be needed by SignalBackends() */
1263
0
    if (signalPids == NULL)
1264
0
      signalPids = MemoryContextAlloc(TopMemoryContext,
1265
0
                      MaxBackends * sizeof(int32));
1266
1267
0
    if (signalProcnos == NULL)
1268
0
      signalProcnos = MemoryContextAlloc(TopMemoryContext,
1269
0
                         MaxBackends * sizeof(ProcNumber));
1270
1271
    /*
1272
     * Make sure that we have an XID assigned to the current transaction.
1273
     * GetCurrentTransactionId is cheap if we already have an XID, but not
1274
     * so cheap if we don't, and we'd prefer not to do that work while
1275
     * holding NotifyQueueLock.
1276
     */
1277
0
    (void) GetCurrentTransactionId();
1278
1279
    /*
1280
     * Serialize writers by acquiring a special lock that we hold till
1281
     * after commit.  This ensures that queue entries appear in commit
1282
     * order, and in particular that there are never uncommitted queue
1283
     * entries ahead of committed ones, so an uncommitted transaction
1284
     * can't block delivery of deliverable notifications.
1285
     *
1286
     * We use a heavyweight lock so that it'll automatically be released
1287
     * after either commit or abort.  This also allows deadlocks to be
1288
     * detected, though really a deadlock shouldn't be possible here.
1289
     *
1290
     * The lock is on "database 0", which is pretty ugly but it doesn't
1291
     * seem worth inventing a special locktag category just for this.
1292
     * (Historical note: before PG 9.0, a similar lock on "database 0" was
1293
     * used by the flatfiles mechanism.)
1294
     */
1295
0
    LockSharedObject(DatabaseRelationId, InvalidOid, 0,
1296
0
             AccessExclusiveLock);
1297
1298
    /*
1299
     * For the direct advancement optimization in SignalBackends(), we
1300
     * need to ensure that no other backend can insert queue entries
1301
     * between queueHeadBeforeWrite and queueHeadAfterWrite.  The
1302
     * heavyweight lock above provides this guarantee, since it serializes
1303
     * all writers.
1304
     *
1305
     * Note: if the heavyweight lock were ever removed for scalability
1306
     * reasons, we could achieve the same guarantee by holding
1307
     * NotifyQueueLock in EXCLUSIVE mode across all our insertions, rather
1308
     * than releasing and reacquiring it for each page as we do below.
1309
     */
1310
1311
    /* Initialize values to a safe default in case list is empty */
1312
0
    SET_QUEUE_POS(queueHeadBeforeWrite, 0, 0);
1313
0
    SET_QUEUE_POS(queueHeadAfterWrite, 0, 0);
1314
1315
    /* Now push the notifications into the queue */
1316
0
    nextNotify = list_head(pendingNotifies->events);
1317
0
    while (nextNotify != NULL)
1318
0
    {
1319
      /*
1320
       * Add the pending notifications to the queue.  We acquire and
1321
       * release NotifyQueueLock once per page, which might be overkill
1322
       * but it does allow readers to get in while we're doing this.
1323
       *
1324
       * A full queue is very uncommon and should really not happen,
1325
       * given that we have so much space available in the SLRU pages.
1326
       * Nevertheless we need to deal with this possibility. Note that
1327
       * when we get here we are in the process of committing our
1328
       * transaction, but we have not yet committed to clog, so at this
1329
       * point in time we can still roll the transaction back.
1330
       */
1331
0
      LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
1332
0
      if (firstIteration)
1333
0
      {
1334
0
        queueHeadBeforeWrite = QUEUE_HEAD;
1335
0
        firstIteration = false;
1336
0
      }
1337
0
      asyncQueueFillWarning();
1338
0
      if (asyncQueueIsFull())
1339
0
        ereport(ERROR,
1340
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1341
0
             errmsg("too many notifications in the NOTIFY queue")));
1342
0
      nextNotify = asyncQueueAddEntries(nextNotify);
1343
0
      queueHeadAfterWrite = QUEUE_HEAD;
1344
0
      LWLockRelease(NotifyQueueLock);
1345
0
    }
1346
1347
    /* Note that we don't clear pendingNotifies; AtCommit_Notify will. */
1348
0
  }
1349
0
}
1350
1351
/*
1352
 * AtCommit_Notify
1353
 *
1354
 *    This is called at transaction commit, after committing to clog.
1355
 *
1356
 *    Apply pending listen/unlisten changes and clear transaction-local state.
1357
 *
1358
 *    If we issued any notifications in the transaction, send signals to
1359
 *    listening backends (possibly including ourselves) to process them.
1360
 *    Also, if we filled enough queue pages with new notifies, try to
1361
 *    advance the queue tail pointer.
1362
 */
1363
void
1364
AtCommit_Notify(void)
1365
0
{
1366
  /*
1367
   * Allow transactions that have not executed LISTEN/UNLISTEN/NOTIFY to
1368
   * return as soon as possible
1369
   */
1370
0
  if (!pendingActions && !pendingNotifies)
1371
0
    return;
1372
1373
0
  if (Trace_notify)
1374
0
    elog(DEBUG1, "AtCommit_Notify");
1375
1376
  /* Apply staged listen/unlisten changes */
1377
0
  ApplyPendingListenActions(true);
1378
1379
  /* If no longer listening to anything, get out of listener array */
1380
0
  if (amRegisteredListener && LocalChannelTableIsEmpty())
1381
0
    asyncQueueUnregister();
1382
1383
  /*
1384
   * Send signals to listening backends.  We need do this only if there are
1385
   * pending notifies, which were previously added to the shared queue by
1386
   * PreCommit_Notify().
1387
   */
1388
0
  if (pendingNotifies != NULL)
1389
0
    SignalBackends();
1390
1391
  /*
1392
   * If it's time to try to advance the global tail pointer, do that.
1393
   *
1394
   * (It might seem odd to do this in the sender, when more than likely the
1395
   * listeners won't yet have read the messages we just sent.  However,
1396
   * there's less contention if only the sender does it, and there is little
1397
   * need for urgency in advancing the global tail.  So this typically will
1398
   * be clearing out messages that were sent some time ago.)
1399
   */
1400
0
  if (tryAdvanceTail)
1401
0
  {
1402
0
    tryAdvanceTail = false;
1403
0
    asyncQueueAdvanceTail();
1404
0
  }
1405
1406
  /* And clean up */
1407
0
  ClearPendingActionsAndNotifies();
1408
0
}
1409
1410
/*
1411
 * BecomeRegisteredListener --- subroutine for PreCommit_Notify
1412
 *
1413
 * This function must make sure we are ready to catch any incoming messages.
1414
 */
1415
static void
1416
BecomeRegisteredListener(void)
1417
0
{
1418
0
  QueuePosition head;
1419
0
  QueuePosition max;
1420
0
  ProcNumber  prevListener;
1421
1422
  /*
1423
   * Nothing to do if we are already listening to something, nor if we
1424
   * already ran this routine in this transaction.
1425
   */
1426
0
  if (amRegisteredListener)
1427
0
    return;
1428
1429
0
  if (Trace_notify)
1430
0
    elog(DEBUG1, "BecomeRegisteredListener(%d)", MyProcPid);
1431
1432
  /*
1433
   * Before registering, make sure we will unlisten before dying. (Note:
1434
   * this action does not get undone if we abort later.)
1435
   */
1436
0
  if (!unlistenExitRegistered)
1437
0
  {
1438
0
    before_shmem_exit(Async_UnlistenOnExit, 0);
1439
0
    unlistenExitRegistered = true;
1440
0
  }
1441
1442
  /*
1443
   * This is our first LISTEN, so establish our pointer.
1444
   *
1445
   * We set our pointer to the global tail pointer and then move it forward
1446
   * over already-committed notifications.  This ensures we cannot miss any
1447
   * not-yet-committed notifications.  We might get a few more but that
1448
   * doesn't hurt.
1449
   *
1450
   * In some scenarios there might be a lot of committed notifications that
1451
   * have not yet been pruned away (because some backend is being lazy about
1452
   * reading them).  To reduce our startup time, we can look at other
1453
   * backends and adopt the maximum "pos" pointer of any backend that's in
1454
   * our database; any notifications it's already advanced over are surely
1455
   * committed and need not be re-examined by us.  (We must consider only
1456
   * backends connected to our DB, because others will not have bothered to
1457
   * check committed-ness of notifications in our DB.)
1458
   *
1459
   * We need exclusive lock here so we can look at other backends' entries
1460
   * and manipulate the list links.
1461
   */
1462
0
  LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
1463
0
  head = QUEUE_HEAD;
1464
0
  max = QUEUE_TAIL;
1465
0
  prevListener = INVALID_PROC_NUMBER;
1466
0
  for (ProcNumber i = QUEUE_FIRST_LISTENER; i != INVALID_PROC_NUMBER; i = QUEUE_NEXT_LISTENER(i))
1467
0
  {
1468
0
    if (QUEUE_BACKEND_DBOID(i) == MyDatabaseId)
1469
0
      max = QUEUE_POS_MAX(max, QUEUE_BACKEND_POS(i));
1470
    /* Also find last listening backend before this one */
1471
0
    if (i < MyProcNumber)
1472
0
      prevListener = i;
1473
0
  }
1474
0
  QUEUE_BACKEND_POS(MyProcNumber) = max;
1475
0
  QUEUE_BACKEND_PID(MyProcNumber) = MyProcPid;
1476
0
  QUEUE_BACKEND_DBOID(MyProcNumber) = MyDatabaseId;
1477
0
  QUEUE_BACKEND_WAKEUP_PENDING(MyProcNumber) = false;
1478
0
  QUEUE_BACKEND_IS_ADVANCING(MyProcNumber) = false;
1479
  /* Insert backend into list of listeners at correct position */
1480
0
  if (prevListener != INVALID_PROC_NUMBER)
1481
0
  {
1482
0
    QUEUE_NEXT_LISTENER(MyProcNumber) = QUEUE_NEXT_LISTENER(prevListener);
1483
0
    QUEUE_NEXT_LISTENER(prevListener) = MyProcNumber;
1484
0
  }
1485
0
  else
1486
0
  {
1487
0
    QUEUE_NEXT_LISTENER(MyProcNumber) = QUEUE_FIRST_LISTENER;
1488
0
    QUEUE_FIRST_LISTENER = MyProcNumber;
1489
0
  }
1490
0
  LWLockRelease(NotifyQueueLock);
1491
1492
  /* Now we are listed in the global array, so remember we're listening */
1493
0
  amRegisteredListener = true;
1494
1495
  /*
1496
   * Try to move our pointer forward as far as possible.  This will skip
1497
   * over already-committed notifications, which we want to do because they
1498
   * might be quite stale.  Note that we are not yet listening on anything,
1499
   * so we won't deliver such notifications to our frontend.  Also, although
1500
   * our transaction might have executed NOTIFY, those message(s) aren't
1501
   * queued yet so we won't skip them here.
1502
   */
1503
0
  if (!QUEUE_POS_EQUAL(max, head))
1504
0
    asyncQueueReadAllNotifications();
1505
0
}
1506
1507
/*
1508
 * PrepareTableEntriesForListen --- subroutine for PreCommit_Notify
1509
 *
1510
 * Prepare a LISTEN by recording it in pendingListenActions, pre-allocating
1511
 * an entry in localChannelTable, and pre-allocating an entry in the shared
1512
 * globalChannelTable with removeOnAbort set.  AtCommit_Notify will clear
1513
 * removeOnAbort; abort processing will remove entries still marked so.
1514
 */
1515
static void
1516
PrepareTableEntriesForListen(const char *channel)
1517
0
{
1518
0
  GlobalChannelKey key;
1519
0
  GlobalChannelEntry *entry;
1520
0
  bool    found;
1521
0
  ListenerEntry *listeners;
1522
0
  PendingListenEntry *pending;
1523
1524
  /*
1525
   * Record in local pending hash that we want to LISTEN, overwriting any
1526
   * earlier attempt to UNLISTEN.
1527
   */
1528
0
  pending = (PendingListenEntry *)
1529
0
    hash_search(pendingListenActions, channel, HASH_ENTER, NULL);
1530
0
  pending->action = PENDING_LISTEN;
1531
1532
  /*
1533
   * Ensure that there is an entry for the channel in localChannelTable.
1534
   * (Should this fail, we can just roll back.)  If the transaction fails
1535
   * after this point, we will remove the entry if appropriate during
1536
   * ApplyPendingListenActions.  Note that this entry allows IsListeningOn()
1537
   * to return TRUE; we assume nothing is going to consult that before
1538
   * AtCommit_Notify/AtAbort_Notify.  However, if later actions attempt to
1539
   * UNLISTEN this channel or UNLISTEN *, we need to have the local entry
1540
   * present to ensure they do the right things; see
1541
   * PrepareTableEntriesForUnlisten and PrepareTableEntriesForUnlistenAll.
1542
   */
1543
0
  (void) hash_search(localChannelTable, channel, HASH_ENTER, NULL);
1544
1545
  /* Pre-allocate entry in shared globalChannelTable */
1546
0
  GlobalChannelKeyInit(&key, MyDatabaseId, channel);
1547
0
  entry = dshash_find_or_insert(globalChannelTable, &key, &found);
1548
1549
0
  if (!found)
1550
0
  {
1551
    /* New channel entry, so initialize it to a safe state */
1552
0
    entry->listenersArray = InvalidDsaPointer;
1553
0
    entry->numListeners = 0;
1554
0
    entry->allocatedListeners = 0;
1555
0
  }
1556
1557
  /*
1558
   * Create listenersArray if entry doesn't have one.  It's tempting to fold
1559
   * this into the !found case, but this coding allows us to cope in case
1560
   * dsa_allocate() failed in an earlier attempt.
1561
   */
1562
0
  if (!DsaPointerIsValid(entry->listenersArray))
1563
0
  {
1564
0
    entry->listenersArray = dsa_allocate(globalChannelDSA,
1565
0
                       sizeof(ListenerEntry) * INITIAL_LISTENERS_ARRAY_SIZE);
1566
0
    entry->allocatedListeners = INITIAL_LISTENERS_ARRAY_SIZE;
1567
0
  }
1568
1569
0
  listeners = (ListenerEntry *)
1570
0
    dsa_get_address(globalChannelDSA, entry->listenersArray);
1571
1572
  /*
1573
   * Check if we already have a ListenerEntry (possibly from earlier in this
1574
   * transaction)
1575
   */
1576
0
  for (int i = 0; i < entry->numListeners; i++)
1577
0
  {
1578
0
    if (listeners[i].procNo == MyProcNumber)
1579
0
    {
1580
      /* Already have an entry; leave removeOnAbort as-is */
1581
0
      dshash_release_lock(globalChannelTable, entry);
1582
0
      return;
1583
0
    }
1584
0
  }
1585
1586
  /* Need to add a new entry; grow array if necessary */
1587
0
  if (entry->numListeners >= entry->allocatedListeners)
1588
0
  {
1589
0
    int     new_size = entry->allocatedListeners * 2;
1590
0
    dsa_pointer old_array = entry->listenersArray;
1591
0
    dsa_pointer new_array = dsa_allocate(globalChannelDSA,
1592
0
                       sizeof(ListenerEntry) * new_size);
1593
0
    ListenerEntry *new_listeners = (ListenerEntry *) dsa_get_address(globalChannelDSA, new_array);
1594
1595
0
    memcpy(new_listeners, listeners, sizeof(ListenerEntry) * entry->numListeners);
1596
0
    entry->listenersArray = new_array;
1597
0
    entry->allocatedListeners = new_size;
1598
0
    dsa_free(globalChannelDSA, old_array);
1599
0
    listeners = new_listeners;
1600
0
  }
1601
1602
0
  listeners[entry->numListeners].procNo = MyProcNumber;
1603
0
  listeners[entry->numListeners].removeOnAbort = true;
1604
0
  entry->numListeners++;
1605
1606
0
  dshash_release_lock(globalChannelTable, entry);
1607
0
}
1608
1609
/*
1610
 * PrepareTableEntriesForUnlisten --- subroutine for PreCommit_Notify
1611
 *
1612
 * Prepare an UNLISTEN by recording it in pendingListenActions, but only if
1613
 * we're currently listening (committed or staged).  We don't touch
1614
 * globalChannelTable yet - the listener keeps receiving signals until
1615
 * commit, when the entry is removed.
1616
 */
1617
static void
1618
PrepareTableEntriesForUnlisten(const char *channel)
1619
0
{
1620
0
  PendingListenEntry *pending;
1621
1622
  /*
1623
   * If the channel name is not in localChannelTable, then we are neither
1624
   * listening on it nor preparing to listen on it, so we don't need to
1625
   * record an UNLISTEN action.
1626
   */
1627
0
  Assert(localChannelTable != NULL);
1628
0
  if (hash_search(localChannelTable, channel, HASH_FIND, NULL) == NULL)
1629
0
    return;
1630
1631
  /*
1632
   * Record in local pending hash that we want to UNLISTEN, overwriting any
1633
   * earlier attempt to LISTEN.  Don't touch localChannelTable or
1634
   * globalChannelTable yet - we keep receiving signals until commit.
1635
   */
1636
0
  pending = (PendingListenEntry *)
1637
0
    hash_search(pendingListenActions, channel, HASH_ENTER, NULL);
1638
0
  pending->action = PENDING_UNLISTEN;
1639
0
}
1640
1641
/*
1642
 * PrepareTableEntriesForUnlistenAll --- subroutine for PreCommit_Notify
1643
 *
1644
 * Prepare UNLISTEN * by recording an UNLISTEN for all listened or
1645
 * about-to-be-listened channels in pendingListenActions.
1646
 */
1647
static void
1648
PrepareTableEntriesForUnlistenAll(void)
1649
0
{
1650
0
  HASH_SEQ_STATUS seq;
1651
0
  ChannelName *channelEntry;
1652
0
  PendingListenEntry *pending;
1653
1654
  /*
1655
   * Scan localChannelTable, which will have the names of all channels that
1656
   * we are listening on or have prepared to listen on.  Record an UNLISTEN
1657
   * action for each one, overwriting any earlier attempt to LISTEN.
1658
   */
1659
0
  hash_seq_init(&seq, localChannelTable);
1660
0
  while ((channelEntry = (ChannelName *) hash_seq_search(&seq)) != NULL)
1661
0
  {
1662
0
    pending = (PendingListenEntry *)
1663
0
      hash_search(pendingListenActions, channelEntry->channel, HASH_ENTER, NULL);
1664
0
    pending->action = PENDING_UNLISTEN;
1665
0
  }
1666
0
}
1667
1668
/*
1669
 * RemoveListenerFromChannel --- remove idx'th listener from global channel entry
1670
 *
1671
 * Decrements numListeners, compacts the array, and frees the entry if empty.
1672
 * Sets *entry_ptr to NULL if the entry was deleted.
1673
 *
1674
 * We could get the listeners pointer from the entry, but all callers
1675
 * already have it at hand.
1676
 */
1677
static void
1678
RemoveListenerFromChannel(GlobalChannelEntry **entry_ptr,
1679
              ListenerEntry *listeners,
1680
              int idx)
1681
0
{
1682
0
  GlobalChannelEntry *entry = *entry_ptr;
1683
1684
0
  entry->numListeners--;
1685
0
  if (idx < entry->numListeners)
1686
0
    memmove(&listeners[idx], &listeners[idx + 1],
1687
0
        sizeof(ListenerEntry) * (entry->numListeners - idx));
1688
1689
0
  if (entry->numListeners == 0)
1690
0
  {
1691
0
    dsa_free(globalChannelDSA, entry->listenersArray);
1692
0
    dshash_delete_entry(globalChannelTable, entry);
1693
    /* tells caller not to release the entry's lock: */
1694
0
    *entry_ptr = NULL;
1695
0
  }
1696
0
}
1697
1698
/*
1699
 * ApplyPendingListenActions
1700
 *
1701
 * Apply, or revert, staged listen/unlisten changes to the local and global
1702
 * hash tables.
1703
 */
1704
static void
1705
ApplyPendingListenActions(bool isCommit)
1706
0
{
1707
0
  HASH_SEQ_STATUS seq;
1708
0
  PendingListenEntry *pending;
1709
1710
  /* Quick exit if nothing to do */
1711
0
  if (pendingListenActions == NULL)
1712
0
    return;
1713
1714
  /* We made a globalChannelTable before building pendingListenActions */
1715
0
  if (globalChannelTable == NULL)
1716
0
    elog(PANIC, "global channel table missing post-commit/abort");
1717
1718
  /* For each staged action ... */
1719
0
  hash_seq_init(&seq, pendingListenActions);
1720
0
  while ((pending = (PendingListenEntry *) hash_seq_search(&seq)) != NULL)
1721
0
  {
1722
0
    GlobalChannelKey key;
1723
0
    GlobalChannelEntry *entry;
1724
0
    bool    removeLocal = true;
1725
0
    bool    foundListener = false;
1726
1727
    /*
1728
     * Find the global entry for this channel.  If isCommit, it had better
1729
     * exist (it was created in PreCommit).  In an abort, it might not
1730
     * exist, in which case we are not listening and should discard any
1731
     * local entry that PreCommit may have managed to create.
1732
     */
1733
0
    GlobalChannelKeyInit(&key, MyDatabaseId, pending->channel);
1734
0
    entry = dshash_find(globalChannelTable, &key, true);
1735
0
    if (entry != NULL)
1736
0
    {
1737
      /* Scan entry to find the ListenerEntry for this backend */
1738
0
      ListenerEntry *listeners;
1739
1740
0
      listeners = (ListenerEntry *)
1741
0
        dsa_get_address(globalChannelDSA, entry->listenersArray);
1742
1743
0
      for (int i = 0; i < entry->numListeners; i++)
1744
0
      {
1745
0
        if (listeners[i].procNo != MyProcNumber)
1746
0
          continue;
1747
0
        foundListener = true;
1748
0
        if (isCommit)
1749
0
        {
1750
0
          if (pending->action == PENDING_LISTEN)
1751
0
          {
1752
            /*
1753
             * LISTEN being committed: entry is now permanent.
1754
             * localChannelTable entry was created during
1755
             * PreCommit and should be kept.
1756
             */
1757
0
            listeners[i].removeOnAbort = false;
1758
0
            removeLocal = false;
1759
0
          }
1760
0
          else
1761
0
          {
1762
            /*
1763
             * UNLISTEN being committed: remove pre-allocated
1764
             * entries from both tables.
1765
             */
1766
0
            RemoveListenerFromChannel(&entry, listeners, i);
1767
0
          }
1768
0
        }
1769
0
        else
1770
0
        {
1771
          /*
1772
           * Note: this part is reachable only if the transaction
1773
           * aborts after PreCommit_Notify() has made some
1774
           * pendingListenActions entries, so it's pretty hard to
1775
           * test.
1776
           */
1777
0
          if (listeners[i].removeOnAbort)
1778
0
          {
1779
            /*
1780
             * Staged LISTEN (or LISTEN+UNLISTEN) being aborted,
1781
             * so remove pre-allocated entries from both tables.
1782
             */
1783
0
            RemoveListenerFromChannel(&entry, listeners, i);
1784
0
          }
1785
0
          else
1786
0
          {
1787
            /*
1788
             * Entry predates this transaction, so keep the
1789
             * localChannelTable entry.
1790
             */
1791
0
            removeLocal = false;
1792
0
          }
1793
0
        }
1794
0
        break;      /* there shouldn't be another match */
1795
0
      }
1796
1797
      /* We might have already released the entry by removing it */
1798
0
      if (entry != NULL)
1799
0
        dshash_release_lock(globalChannelTable, entry);
1800
0
    }
1801
1802
    /*
1803
     * If we're committing a LISTEN action, we should have found a
1804
     * matching ListenerEntry, but otherwise it's okay if we didn't.
1805
     */
1806
0
    if (isCommit && pending->action == PENDING_LISTEN && !foundListener)
1807
0
      elog(PANIC, "could not find listener entry for channel \"%s\" backend %d",
1808
0
         pending->channel, MyProcNumber);
1809
1810
    /*
1811
     * If we did not find a globalChannelTable entry for our backend, or
1812
     * if we are unlistening, remove any localChannelTable entry that may
1813
     * exist.  (Note in particular that this cleans up if we created a
1814
     * localChannelTable entry and then failed while trying to create a
1815
     * globalChannelTable entry.)
1816
     */
1817
0
    if (removeLocal && localChannelTable != NULL)
1818
0
      (void) hash_search(localChannelTable, pending->channel,
1819
0
                 HASH_REMOVE, NULL);
1820
0
  }
1821
0
}
1822
1823
/*
1824
 * CleanupListenersOnExit --- called from Async_UnlistenOnExit
1825
 *
1826
 *    Remove this backend from all channels in the shared global table.
1827
 */
1828
static void
1829
CleanupListenersOnExit(void)
1830
0
{
1831
0
  dshash_seq_status status;
1832
0
  GlobalChannelEntry *entry;
1833
1834
0
  if (Trace_notify)
1835
0
    elog(DEBUG1, "CleanupListenersOnExit(%d)", MyProcPid);
1836
1837
  /* Clear our local cache (not really necessary, but be consistent) */
1838
0
  if (localChannelTable != NULL)
1839
0
  {
1840
0
    hash_destroy(localChannelTable);
1841
0
    localChannelTable = NULL;
1842
0
  }
1843
1844
  /* Now remove our entries from the shared globalChannelTable */
1845
0
  if (globalChannelTable == NULL)
1846
0
    return;
1847
1848
0
  dshash_seq_init(&status, globalChannelTable, true);
1849
0
  while ((entry = dshash_seq_next(&status)) != NULL)
1850
0
  {
1851
0
    ListenerEntry *listeners;
1852
1853
0
    if (entry->key.dboid != MyDatabaseId)
1854
0
      continue;     /* not relevant */
1855
1856
0
    listeners = (ListenerEntry *)
1857
0
      dsa_get_address(globalChannelDSA, entry->listenersArray);
1858
1859
0
    for (int i = 0; i < entry->numListeners; i++)
1860
0
    {
1861
0
      if (listeners[i].procNo == MyProcNumber)
1862
0
      {
1863
0
        entry->numListeners--;
1864
0
        if (i < entry->numListeners)
1865
0
          memmove(&listeners[i], &listeners[i + 1],
1866
0
              sizeof(ListenerEntry) * (entry->numListeners - i));
1867
1868
0
        if (entry->numListeners == 0)
1869
0
        {
1870
0
          dsa_free(globalChannelDSA, entry->listenersArray);
1871
0
          dshash_delete_current(&status);
1872
0
        }
1873
0
        break;
1874
0
      }
1875
0
    }
1876
0
  }
1877
0
  dshash_seq_term(&status);
1878
0
}
1879
1880
/*
1881
 * Test whether we are actively listening on the given channel name.
1882
 *
1883
 * Note: this function is executed for every notification found in the queue.
1884
 */
1885
static bool
1886
IsListeningOn(const char *channel)
1887
0
{
1888
0
  if (localChannelTable == NULL)
1889
0
    return false;
1890
1891
0
  return (hash_search(localChannelTable, channel, HASH_FIND, NULL) != NULL);
1892
0
}
1893
1894
/*
1895
 * Remove our entry from the listeners array when we are no longer listening
1896
 * on any channel.  NB: must not fail if we're already not listening.
1897
 */
1898
static void
1899
asyncQueueUnregister(void)
1900
0
{
1901
0
  Assert(LocalChannelTableIsEmpty()); /* else caller error */
1902
1903
0
  if (!amRegisteredListener) /* nothing to do */
1904
0
    return;
1905
1906
  /*
1907
   * Need exclusive lock here to manipulate list links.
1908
   */
1909
0
  LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
1910
  /* Mark our entry as invalid */
1911
0
  QUEUE_BACKEND_PID(MyProcNumber) = InvalidPid;
1912
0
  QUEUE_BACKEND_DBOID(MyProcNumber) = InvalidOid;
1913
0
  QUEUE_BACKEND_WAKEUP_PENDING(MyProcNumber) = false;
1914
0
  QUEUE_BACKEND_IS_ADVANCING(MyProcNumber) = false;
1915
  /* and remove it from the list */
1916
0
  if (QUEUE_FIRST_LISTENER == MyProcNumber)
1917
0
    QUEUE_FIRST_LISTENER = QUEUE_NEXT_LISTENER(MyProcNumber);
1918
0
  else
1919
0
  {
1920
0
    for (ProcNumber i = QUEUE_FIRST_LISTENER; i != INVALID_PROC_NUMBER; i = QUEUE_NEXT_LISTENER(i))
1921
0
    {
1922
0
      if (QUEUE_NEXT_LISTENER(i) == MyProcNumber)
1923
0
      {
1924
0
        QUEUE_NEXT_LISTENER(i) = QUEUE_NEXT_LISTENER(MyProcNumber);
1925
0
        break;
1926
0
      }
1927
0
    }
1928
0
  }
1929
0
  QUEUE_NEXT_LISTENER(MyProcNumber) = INVALID_PROC_NUMBER;
1930
0
  LWLockRelease(NotifyQueueLock);
1931
1932
  /* mark ourselves as no longer listed in the global array */
1933
0
  amRegisteredListener = false;
1934
0
}
1935
1936
/*
1937
 * Test whether there is room to insert more notification messages.
1938
 *
1939
 * Caller must hold at least shared NotifyQueueLock.
1940
 */
1941
static bool
1942
asyncQueueIsFull(void)
1943
0
{
1944
0
  int64   headPage = QUEUE_POS_PAGE(QUEUE_HEAD);
1945
0
  int64   tailPage = QUEUE_POS_PAGE(QUEUE_TAIL);
1946
0
  int64   occupied = headPage - tailPage;
1947
1948
0
  return occupied >= max_notify_queue_pages;
1949
0
}
1950
1951
/*
1952
 * Advance the QueuePosition to the next entry, assuming that the current
1953
 * entry is of length entryLength.  If we jump to a new page the function
1954
 * returns true, else false.
1955
 */
1956
static bool
1957
asyncQueueAdvance(QueuePosition *position, int entryLength)
1958
0
{
1959
0
  int64   pageno = QUEUE_POS_PAGE(*position);
1960
0
  int     offset = QUEUE_POS_OFFSET(*position);
1961
0
  bool    pageJump = false;
1962
1963
  /*
1964
   * Move to the next writing position: First jump over what we have just
1965
   * written or read.
1966
   */
1967
0
  offset += entryLength;
1968
0
  Assert(offset <= QUEUE_PAGESIZE);
1969
1970
  /*
1971
   * In a second step check if another entry can possibly be written to the
1972
   * page. If so, stay here, we have reached the next position. If not, then
1973
   * we need to move on to the next page.
1974
   */
1975
0
  if (offset + QUEUEALIGN(AsyncQueueEntryEmptySize) > QUEUE_PAGESIZE)
1976
0
  {
1977
0
    pageno++;
1978
0
    offset = 0;
1979
0
    pageJump = true;
1980
0
  }
1981
1982
0
  SET_QUEUE_POS(*position, pageno, offset);
1983
0
  return pageJump;
1984
0
}
1985
1986
/*
1987
 * Fill the AsyncQueueEntry at *qe with an outbound notification message.
1988
 */
1989
static void
1990
asyncQueueNotificationToEntry(Notification *n, AsyncQueueEntry *qe)
1991
0
{
1992
0
  size_t    channellen = n->channel_len;
1993
0
  size_t    payloadlen = n->payload_len;
1994
0
  int     entryLength;
1995
1996
0
  Assert(channellen < NAMEDATALEN);
1997
0
  Assert(payloadlen < NOTIFY_PAYLOAD_MAX_LENGTH);
1998
1999
  /* The terminators are already included in AsyncQueueEntryEmptySize */
2000
0
  entryLength = AsyncQueueEntryEmptySize + payloadlen + channellen;
2001
0
  entryLength = QUEUEALIGN(entryLength);
2002
0
  qe->length = entryLength;
2003
0
  qe->dboid = MyDatabaseId;
2004
0
  qe->xid = GetCurrentTransactionId();
2005
0
  qe->srcPid = MyProcPid;
2006
0
  memcpy(qe->data, n->data, channellen + payloadlen + 2);
2007
0
}
2008
2009
/*
2010
 * Add pending notifications to the queue.
2011
 *
2012
 * We go page by page here, i.e. we stop once we have to go to a new page but
2013
 * we will be called again and then fill that next page. If an entry does not
2014
 * fit into the current page, we write a dummy entry with an InvalidOid as the
2015
 * database OID in order to fill the page. So every page is always used up to
2016
 * the last byte which simplifies reading the page later.
2017
 *
2018
 * We are passed the list cell (in pendingNotifies->events) containing the next
2019
 * notification to write and return the first still-unwritten cell back.
2020
 * Eventually we will return NULL indicating all is done.
2021
 *
2022
 * We are holding NotifyQueueLock already from the caller and grab
2023
 * page specific SLRU bank lock locally in this function.
2024
 */
2025
static ListCell *
2026
asyncQueueAddEntries(ListCell *nextNotify)
2027
0
{
2028
0
  AsyncQueueEntry qe;
2029
0
  QueuePosition queue_head;
2030
0
  int64   pageno;
2031
0
  int     offset;
2032
0
  int     slotno;
2033
0
  LWLock     *prevlock;
2034
2035
  /*
2036
   * We work with a local copy of QUEUE_HEAD, which we write back to shared
2037
   * memory upon exiting.  The reason for this is that if we have to advance
2038
   * to a new page, SimpleLruZeroPage might fail (out of disk space, for
2039
   * instance), and we must not advance QUEUE_HEAD if it does.  (Otherwise,
2040
   * subsequent insertions would try to put entries into a page that slru.c
2041
   * thinks doesn't exist yet.)  So, use a local position variable.  Note
2042
   * that if we do fail, any already-inserted queue entries are forgotten;
2043
   * this is okay, since they'd be useless anyway after our transaction
2044
   * rolls back.
2045
   */
2046
0
  queue_head = QUEUE_HEAD;
2047
2048
  /*
2049
   * If this is the first write since the postmaster started, we need to
2050
   * initialize the first page of the async SLRU.  Otherwise, the current
2051
   * page should be initialized already, so just fetch it.
2052
   */
2053
0
  pageno = QUEUE_POS_PAGE(queue_head);
2054
0
  prevlock = SimpleLruGetBankLock(NotifyCtl, pageno);
2055
2056
  /* We hold both NotifyQueueLock and SLRU bank lock during this operation */
2057
0
  LWLockAcquire(prevlock, LW_EXCLUSIVE);
2058
2059
0
  if (QUEUE_POS_IS_ZERO(queue_head))
2060
0
    slotno = SimpleLruZeroPage(NotifyCtl, pageno);
2061
0
  else
2062
0
    slotno = SimpleLruReadPage(NotifyCtl, pageno, true, &queue_head);
2063
2064
  /* Note we mark the page dirty before writing in it */
2065
0
  NotifyCtl->shared->page_dirty[slotno] = true;
2066
2067
0
  while (nextNotify != NULL)
2068
0
  {
2069
0
    Notification *n = (Notification *) lfirst(nextNotify);
2070
2071
    /* Construct a valid queue entry in local variable qe */
2072
0
    asyncQueueNotificationToEntry(n, &qe);
2073
2074
0
    offset = QUEUE_POS_OFFSET(queue_head);
2075
2076
    /* Check whether the entry really fits on the current page */
2077
0
    if (offset + qe.length <= QUEUE_PAGESIZE)
2078
0
    {
2079
      /* OK, so advance nextNotify past this item */
2080
0
      nextNotify = lnext(pendingNotifies->events, nextNotify);
2081
0
    }
2082
0
    else
2083
0
    {
2084
      /*
2085
       * Write a dummy entry to fill up the page. Actually readers will
2086
       * only check dboid and since it won't match any reader's database
2087
       * OID, they will ignore this entry and move on.
2088
       */
2089
0
      qe.length = QUEUE_PAGESIZE - offset;
2090
0
      qe.dboid = InvalidOid;
2091
0
      qe.xid = InvalidTransactionId;
2092
0
      qe.data[0] = '\0';  /* empty channel */
2093
0
      qe.data[1] = '\0';  /* empty payload */
2094
0
    }
2095
2096
    /* Now copy qe into the shared buffer page */
2097
0
    memcpy(NotifyCtl->shared->page_buffer[slotno] + offset,
2098
0
         &qe,
2099
0
         qe.length);
2100
2101
    /* Advance queue_head appropriately, and detect if page is full */
2102
0
    if (asyncQueueAdvance(&(queue_head), qe.length))
2103
0
    {
2104
0
      LWLock     *lock;
2105
2106
0
      pageno = QUEUE_POS_PAGE(queue_head);
2107
0
      lock = SimpleLruGetBankLock(NotifyCtl, pageno);
2108
0
      if (lock != prevlock)
2109
0
      {
2110
0
        LWLockRelease(prevlock);
2111
0
        LWLockAcquire(lock, LW_EXCLUSIVE);
2112
0
        prevlock = lock;
2113
0
      }
2114
2115
      /*
2116
       * Page is full, so we're done here, but first fill the next page
2117
       * with zeroes.  The reason to do this is to ensure that slru.c's
2118
       * idea of the head page is always the same as ours, which avoids
2119
       * boundary problems in SimpleLruTruncate.  The test in
2120
       * asyncQueueIsFull() ensured that there is room to create this
2121
       * page without overrunning the queue.
2122
       */
2123
0
      slotno = SimpleLruZeroPage(NotifyCtl, QUEUE_POS_PAGE(queue_head));
2124
2125
      /*
2126
       * If the new page address is a multiple of QUEUE_CLEANUP_DELAY,
2127
       * set flag to remember that we should try to advance the tail
2128
       * pointer (we don't want to actually do that right here).
2129
       */
2130
0
      if (QUEUE_POS_PAGE(queue_head) % QUEUE_CLEANUP_DELAY == 0)
2131
0
        tryAdvanceTail = true;
2132
2133
      /* And exit the loop */
2134
0
      break;
2135
0
    }
2136
0
  }
2137
2138
  /* Success, so update the global QUEUE_HEAD */
2139
0
  QUEUE_HEAD = queue_head;
2140
2141
0
  LWLockRelease(prevlock);
2142
2143
0
  return nextNotify;
2144
0
}
2145
2146
/*
2147
 * SQL function to return the fraction of the notification queue currently
2148
 * occupied.
2149
 */
2150
Datum
2151
pg_notification_queue_usage(PG_FUNCTION_ARGS)
2152
0
{
2153
0
  double    usage;
2154
2155
  /* Advance the queue tail so we don't report a too-large result */
2156
0
  asyncQueueAdvanceTail();
2157
2158
0
  LWLockAcquire(NotifyQueueLock, LW_SHARED);
2159
0
  usage = asyncQueueUsage();
2160
0
  LWLockRelease(NotifyQueueLock);
2161
2162
0
  PG_RETURN_FLOAT8(usage);
2163
0
}
2164
2165
/*
2166
 * Return the fraction of the queue that is currently occupied.
2167
 *
2168
 * The caller must hold NotifyQueueLock in (at least) shared mode.
2169
 *
2170
 * Note: we measure the distance to the logical tail page, not the physical
2171
 * tail page.  In some sense that's wrong, but the relative position of the
2172
 * physical tail is affected by details such as SLRU segment boundaries,
2173
 * so that a result based on that is unpleasantly unstable.
2174
 */
2175
static double
2176
asyncQueueUsage(void)
2177
0
{
2178
0
  int64   headPage = QUEUE_POS_PAGE(QUEUE_HEAD);
2179
0
  int64   tailPage = QUEUE_POS_PAGE(QUEUE_TAIL);
2180
0
  int64   occupied = headPage - tailPage;
2181
2182
0
  if (occupied == 0)
2183
0
    return (double) 0;   /* fast exit for common case */
2184
2185
0
  return (double) occupied / (double) max_notify_queue_pages;
2186
0
}
2187
2188
/*
2189
 * Check whether the queue is at least half full, and emit a warning if so.
2190
 *
2191
 * This is unlikely given the size of the queue, but possible.
2192
 * The warnings show up at most once every QUEUE_FULL_WARN_INTERVAL.
2193
 *
2194
 * Caller must hold exclusive NotifyQueueLock.
2195
 */
2196
static void
2197
asyncQueueFillWarning(void)
2198
{
2199
  double    fillDegree;
2200
  TimestampTz t;
2201
2202
  fillDegree = asyncQueueUsage();
2203
  if (fillDegree < 0.5)
2204
    return;
2205
2206
  t = GetCurrentTimestamp();
2207
2208
  if (TimestampDifferenceExceeds(asyncQueueControl->lastQueueFillWarn,
2209
                   t, QUEUE_FULL_WARN_INTERVAL))
2210
  {
2211
    QueuePosition min = QUEUE_HEAD;
2212
    int32   minPid = InvalidPid;
2213
2214
    for (ProcNumber i = QUEUE_FIRST_LISTENER; i != INVALID_PROC_NUMBER; i = QUEUE_NEXT_LISTENER(i))
2215
    {
2216
      Assert(QUEUE_BACKEND_PID(i) != InvalidPid);
2217
      min = QUEUE_POS_MIN(min, QUEUE_BACKEND_POS(i));
2218
      if (QUEUE_POS_EQUAL(min, QUEUE_BACKEND_POS(i)))
2219
        minPid = QUEUE_BACKEND_PID(i);
2220
    }
2221
2222
    ereport(WARNING,
2223
        (errmsg("NOTIFY queue is %.0f%% full", fillDegree * 100),
2224
         (minPid != InvalidPid ?
2225
          errdetail("The server process with PID %d is among those with the oldest transactions.", minPid)
2226
          : 0),
2227
         (minPid != InvalidPid ?
2228
          errhint("The NOTIFY queue cannot be emptied until that process ends its current transaction.")
2229
          : 0)));
2230
2231
    asyncQueueControl->lastQueueFillWarn = t;
2232
  }
2233
}
2234
2235
/*
2236
 * Send signals to listening backends.
2237
 *
2238
 * Normally we signal only backends that are interested in the notifies that
2239
 * we just sent.  However, that will leave idle listeners falling further and
2240
 * further behind.  Waken them anyway if they're far enough behind, so they'll
2241
 * advance their queue position pointers, allowing the global tail to advance.
2242
 *
2243
 * Since we know the ProcNumber and the Pid the signaling is quite cheap.
2244
 *
2245
 * This is called during CommitTransaction(), so it's important for it
2246
 * to have very low probability of failure.
2247
 */
2248
static void
2249
SignalBackends(void)
2250
{
2251
  int     count;
2252
2253
  /* Can't get here without PreCommit_Notify having made the global table */
2254
  Assert(globalChannelTable != NULL);
2255
2256
  /* It should have set up these arrays, too */
2257
  Assert(signalPids != NULL && signalProcnos != NULL);
2258
2259
  /*
2260
   * Identify backends that we need to signal.  We don't want to send
2261
   * signals while holding the NotifyQueueLock, so this part just builds a
2262
   * list of target PIDs in signalPids[] and signalProcnos[].
2263
   */
2264
  count = 0;
2265
2266
  LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
2267
2268
  /* Scan each channel name that we notified in this transaction */
2269
  foreach_ptr(char, channel, pendingNotifies->uniqueChannelNames)
2270
  {
2271
    GlobalChannelKey key;
2272
    GlobalChannelEntry *entry;
2273
    ListenerEntry *listeners;
2274
2275
    GlobalChannelKeyInit(&key, MyDatabaseId, channel);
2276
    entry = dshash_find(globalChannelTable, &key, false);
2277
    if (entry == NULL)
2278
      continue;     /* nobody is listening */
2279
2280
    listeners = (ListenerEntry *) dsa_get_address(globalChannelDSA,
2281
                            entry->listenersArray);
2282
2283
    /*
2284
     * Identify listeners that now need waking, add them to arrays.
2285
     *
2286
     * Note that we signal listeners regardless of the state of their
2287
     * removeOnAbort flags.  Hence a new listener that reached PreCommit,
2288
     * but then failed before AtCommit_Notify, can receive a signal even
2289
     * though it was never really listening.  This is okay because it will
2290
     * not do anything in response to that signal.  If we did not do it
2291
     * like this then a new listener might miss some messages due to the
2292
     * direct-advance logic below.
2293
     */
2294
    for (int j = 0; j < entry->numListeners; j++)
2295
    {
2296
      ProcNumber  i = listeners[j].procNo;
2297
      int32   pid;
2298
      QueuePosition pos;
2299
2300
      if (QUEUE_BACKEND_WAKEUP_PENDING(i))
2301
        continue;   /* already signaled, no need to repeat */
2302
2303
      pid = QUEUE_BACKEND_PID(i);
2304
      pos = QUEUE_BACKEND_POS(i);
2305
2306
      if (QUEUE_POS_EQUAL(pos, QUEUE_HEAD))
2307
        continue;   /* it's fully caught up already */
2308
2309
      Assert(pid != InvalidPid);
2310
2311
      QUEUE_BACKEND_WAKEUP_PENDING(i) = true;
2312
      signalPids[count] = pid;
2313
      signalProcnos[count] = i;
2314
      count++;
2315
    }
2316
2317
    dshash_release_lock(globalChannelTable, entry);
2318
  }
2319
2320
  /*
2321
   * Scan all listeners.  Any that are not already pending wakeup must not
2322
   * be interested in our notifications (else we'd have set their wakeup
2323
   * flags above).  Check to see if we can directly advance their queue
2324
   * pointers to save a wakeup.  Otherwise, if they are far behind, wake
2325
   * them anyway so they will catch up.
2326
   */
2327
  for (ProcNumber i = QUEUE_FIRST_LISTENER; i != INVALID_PROC_NUMBER; i = QUEUE_NEXT_LISTENER(i))
2328
  {
2329
    int32   pid;
2330
    QueuePosition pos;
2331
2332
    if (QUEUE_BACKEND_WAKEUP_PENDING(i))
2333
      continue;
2334
2335
    /* If it's currently advancing, we should not touch it */
2336
    if (QUEUE_BACKEND_IS_ADVANCING(i))
2337
      continue;
2338
2339
    pid = QUEUE_BACKEND_PID(i);
2340
    pos = QUEUE_BACKEND_POS(i);
2341
2342
    /*
2343
     * We can directly advance the other backend's queue pointer if it's
2344
     * not currently advancing (else there are race conditions), and its
2345
     * current pointer is not behind queueHeadBeforeWrite (else we'd make
2346
     * it miss some older messages), and we'd not be moving the pointer
2347
     * backward.
2348
     */
2349
    if (!QUEUE_POS_PRECEDES(pos, queueHeadBeforeWrite) &&
2350
      QUEUE_POS_PRECEDES(pos, queueHeadAfterWrite))
2351
    {
2352
      /* We can directly advance its pointer past what we wrote */
2353
      QUEUE_BACKEND_POS(i) = queueHeadAfterWrite;
2354
    }
2355
    else if (asyncQueuePageDiff(QUEUE_POS_PAGE(QUEUE_HEAD),
2356
                  QUEUE_POS_PAGE(pos)) >= QUEUE_CLEANUP_DELAY)
2357
    {
2358
      /* It's idle and far behind, so wake it up */
2359
      Assert(pid != InvalidPid);
2360
2361
      QUEUE_BACKEND_WAKEUP_PENDING(i) = true;
2362
      signalPids[count] = pid;
2363
      signalProcnos[count] = i;
2364
      count++;
2365
    }
2366
  }
2367
2368
  LWLockRelease(NotifyQueueLock);
2369
2370
  /* Now send signals */
2371
  for (int i = 0; i < count; i++)
2372
  {
2373
    int32   pid = signalPids[i];
2374
2375
    /*
2376
     * If we are signaling our own process, no need to involve the kernel;
2377
     * just set the flag directly.
2378
     */
2379
    if (pid == MyProcPid)
2380
    {
2381
      notifyInterruptPending = true;
2382
      continue;
2383
    }
2384
2385
    /*
2386
     * Note: assuming things aren't broken, a signal failure here could
2387
     * only occur if the target backend exited since we released
2388
     * NotifyQueueLock; which is unlikely but certainly possible. So we
2389
     * just log a low-level debug message if it happens.
2390
     */
2391
    if (SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, signalProcnos[i]) < 0)
2392
      elog(DEBUG3, "could not signal backend with PID %d: %m", pid);
2393
  }
2394
}
2395
2396
/*
2397
 * AtAbort_Notify
2398
 *
2399
 *  This is called at transaction abort.
2400
 *
2401
 *  Revert any staged listen/unlisten changes and clean up transaction state.
2402
 */
2403
void
2404
AtAbort_Notify(void)
2405
0
{
2406
  /*
2407
   * If we failed during asyncQueueReadAllNotifications(), clear our
2408
   * isAdvancing flag, else SignalBackends() would never wake us again.
2409
   * Since only our own process changes this flag, it is safe to check it
2410
   * without the lock.
2411
   */
2412
0
  if (amRegisteredListener && QUEUE_BACKEND_IS_ADVANCING(MyProcNumber))
2413
0
  {
2414
0
    LWLockAcquire(NotifyQueueLock, LW_SHARED);
2415
0
    QUEUE_BACKEND_IS_ADVANCING(MyProcNumber) = false;
2416
0
    LWLockRelease(NotifyQueueLock);
2417
0
  }
2418
2419
  /* Revert any staged listen/unlisten changes */
2420
0
  ApplyPendingListenActions(false);
2421
2422
  /* If we're no longer listening on anything, unregister */
2423
0
  if (amRegisteredListener && LocalChannelTableIsEmpty())
2424
0
    asyncQueueUnregister();
2425
2426
  /* And clean up */
2427
0
  ClearPendingActionsAndNotifies();
2428
0
}
2429
2430
/*
2431
 * AtSubCommit_Notify() --- Take care of subtransaction commit.
2432
 *
2433
 * Reassign all items in the pending lists to the parent transaction.
2434
 */
2435
void
2436
AtSubCommit_Notify(void)
2437
0
{
2438
0
  int     my_level = GetCurrentTransactionNestLevel();
2439
2440
  /* If there are actions at our nesting level, we must reparent them. */
2441
0
  if (pendingActions != NULL &&
2442
0
    pendingActions->nestingLevel >= my_level)
2443
0
  {
2444
0
    if (pendingActions->upper == NULL ||
2445
0
      pendingActions->upper->nestingLevel < my_level - 1)
2446
0
    {
2447
      /* nothing to merge; give the whole thing to the parent */
2448
0
      --pendingActions->nestingLevel;
2449
0
    }
2450
0
    else
2451
0
    {
2452
0
      ActionList *childPendingActions = pendingActions;
2453
2454
0
      pendingActions = pendingActions->upper;
2455
2456
      /*
2457
       * Mustn't try to eliminate duplicates here --- see queue_listen()
2458
       */
2459
0
      pendingActions->actions =
2460
0
        list_concat(pendingActions->actions,
2461
0
              childPendingActions->actions);
2462
0
      pfree(childPendingActions);
2463
0
    }
2464
0
  }
2465
2466
  /* If there are notifies at our nesting level, we must reparent them. */
2467
0
  if (pendingNotifies != NULL &&
2468
0
    pendingNotifies->nestingLevel >= my_level)
2469
0
  {
2470
0
    Assert(pendingNotifies->nestingLevel == my_level);
2471
2472
0
    if (pendingNotifies->upper == NULL ||
2473
0
      pendingNotifies->upper->nestingLevel < my_level - 1)
2474
0
    {
2475
      /* nothing to merge; give the whole thing to the parent */
2476
0
      --pendingNotifies->nestingLevel;
2477
0
    }
2478
0
    else
2479
0
    {
2480
      /*
2481
       * Formerly, we didn't bother to eliminate duplicates here, but
2482
       * now we must, else we fall foul of "Assert(!found)", either here
2483
       * or during a later attempt to build the parent-level hashtable.
2484
       */
2485
0
      NotificationList *childPendingNotifies = pendingNotifies;
2486
0
      ListCell   *l;
2487
2488
0
      pendingNotifies = pendingNotifies->upper;
2489
      /* Insert all the subxact's events into parent, except for dups */
2490
0
      foreach(l, childPendingNotifies->events)
2491
0
      {
2492
0
        Notification *childn = (Notification *) lfirst(l);
2493
2494
0
        if (!AsyncExistsPendingNotify(childn))
2495
0
          AddEventToPendingNotifies(childn);
2496
0
      }
2497
0
      pfree(childPendingNotifies);
2498
0
    }
2499
0
  }
2500
0
}
2501
2502
/*
2503
 * AtSubAbort_Notify() --- Take care of subtransaction abort.
2504
 */
2505
void
2506
AtSubAbort_Notify(void)
2507
0
{
2508
0
  int     my_level = GetCurrentTransactionNestLevel();
2509
2510
  /*
2511
   * All we have to do is pop the stack --- the actions/notifies made in
2512
   * this subxact are no longer interesting, and the space will be freed
2513
   * when CurTransactionContext is recycled. We still have to free the
2514
   * ActionList and NotificationList objects themselves, though, because
2515
   * those are allocated in TopTransactionContext.
2516
   *
2517
   * Note that there might be no entries at all, or no entries for the
2518
   * current subtransaction level, either because none were ever created, or
2519
   * because we reentered this routine due to trouble during subxact abort.
2520
   */
2521
0
  while (pendingActions != NULL &&
2522
0
       pendingActions->nestingLevel >= my_level)
2523
0
  {
2524
0
    ActionList *childPendingActions = pendingActions;
2525
2526
0
    pendingActions = pendingActions->upper;
2527
0
    pfree(childPendingActions);
2528
0
  }
2529
2530
0
  while (pendingNotifies != NULL &&
2531
0
       pendingNotifies->nestingLevel >= my_level)
2532
0
  {
2533
0
    NotificationList *childPendingNotifies = pendingNotifies;
2534
2535
0
    pendingNotifies = pendingNotifies->upper;
2536
0
    pfree(childPendingNotifies);
2537
0
  }
2538
0
}
2539
2540
/*
2541
 * HandleNotifyInterrupt
2542
 *
2543
 *    Signal handler portion of interrupt handling. Let the backend know
2544
 *    that there's a pending notify interrupt. If we're currently reading
2545
 *    from the client, this will interrupt the read and
2546
 *    ProcessClientReadInterrupt() will call ProcessNotifyInterrupt().
2547
 */
2548
void
2549
HandleNotifyInterrupt(void)
2550
0
{
2551
  /*
2552
   * Note: this is called by a SIGNAL HANDLER. You must be very wary what
2553
   * you do here.
2554
   */
2555
2556
  /* signal that work needs to be done */
2557
0
  notifyInterruptPending = true;
2558
2559
  /* latch will be set by procsignal_sigusr1_handler */
2560
0
}
2561
2562
/*
2563
 * ProcessNotifyInterrupt
2564
 *
2565
 *    This is called if we see notifyInterruptPending set, just before
2566
 *    transmitting ReadyForQuery at the end of a frontend command, and
2567
 *    also if a notify signal occurs while reading from the frontend.
2568
 *    HandleNotifyInterrupt() will cause the read to be interrupted
2569
 *    via the process's latch, and this routine will get called.
2570
 *    If we are truly idle (ie, *not* inside a transaction block),
2571
 *    process the incoming notifies.
2572
 *
2573
 *    If "flush" is true, force any frontend messages out immediately.
2574
 *    This can be false when being called at the end of a frontend command,
2575
 *    since we'll flush after sending ReadyForQuery.
2576
 */
2577
void
2578
ProcessNotifyInterrupt(bool flush)
2579
0
{
2580
0
  if (IsTransactionOrTransactionBlock())
2581
0
    return;         /* not really idle */
2582
2583
  /* Loop in case another signal arrives while sending messages */
2584
0
  while (notifyInterruptPending)
2585
0
    ProcessIncomingNotify(flush);
2586
0
}
2587
2588
2589
/*
2590
 * Read all pending notifications from the queue, and deliver appropriate
2591
 * ones to my frontend.  Stop when we reach queue head or an uncommitted
2592
 * notification.
2593
 */
2594
static void
2595
asyncQueueReadAllNotifications(void)
2596
0
{
2597
0
  QueuePosition pos;
2598
0
  QueuePosition head;
2599
0
  Snapshot  snapshot;
2600
2601
  /*
2602
   * Fetch current state, indicate to others that we have woken up, and that
2603
   * we are in process of advancing our position.
2604
   */
2605
0
  LWLockAcquire(NotifyQueueLock, LW_SHARED);
2606
  /* Assert checks that we have a valid state entry */
2607
0
  Assert(MyProcPid == QUEUE_BACKEND_PID(MyProcNumber));
2608
0
  QUEUE_BACKEND_WAKEUP_PENDING(MyProcNumber) = false;
2609
0
  pos = QUEUE_BACKEND_POS(MyProcNumber);
2610
0
  head = QUEUE_HEAD;
2611
2612
0
  if (QUEUE_POS_EQUAL(pos, head))
2613
0
  {
2614
    /* Nothing to do, we have read all notifications already. */
2615
0
    LWLockRelease(NotifyQueueLock);
2616
0
    return;
2617
0
  }
2618
2619
0
  QUEUE_BACKEND_IS_ADVANCING(MyProcNumber) = true;
2620
0
  LWLockRelease(NotifyQueueLock);
2621
2622
  /*----------
2623
   * Get snapshot we'll use to decide which xacts are still in progress.
2624
   * This is trickier than it might seem, because of race conditions.
2625
   * Consider the following example:
2626
   *
2627
   * Backend 1:          Backend 2:
2628
   *
2629
   * transaction starts
2630
   * UPDATE foo SET ...;
2631
   * NOTIFY foo;
2632
   * commit starts
2633
   * queue the notify message
2634
   *                 transaction starts
2635
   *                 LISTEN foo;  -- first LISTEN in session
2636
   *                 SELECT * FROM foo WHERE ...;
2637
   * commit to clog
2638
   *                 commit starts
2639
   *                 add backend 2 to array of listeners
2640
   *                 advance to queue head (this code)
2641
   *                 commit to clog
2642
   *
2643
   * Transaction 2's SELECT has not seen the UPDATE's effects, since that
2644
   * wasn't committed yet.  Ideally we'd ensure that client 2 would
2645
   * eventually get transaction 1's notify message, but there's no way
2646
   * to do that; until we're in the listener array, there's no guarantee
2647
   * that the notify message doesn't get removed from the queue.
2648
   *
2649
   * Therefore the coding technique transaction 2 is using is unsafe:
2650
   * applications must commit a LISTEN before inspecting database state,
2651
   * if they want to ensure they will see notifications about subsequent
2652
   * changes to that state.
2653
   *
2654
   * What we do guarantee is that we'll see all notifications from
2655
   * transactions committing after the snapshot we take here.
2656
   * BecomeRegisteredListener has already added us to the listener array,
2657
   * so no not-yet-committed messages can be removed from the queue
2658
   * before we see them.
2659
   *
2660
   * Since we'll only use this snapshot to check whether XIDs are still
2661
   * running, and we will release it before returning, it's okay to use
2662
   * GetSecondarySnapshot(); that avoids possibly blocking on other
2663
   * transactions in SERIALIZABLE READ ONLY DEFERRABLE mode.
2664
   *----------
2665
   */
2666
0
  snapshot = RegisterSnapshot(GetSecondarySnapshot());
2667
2668
  /*
2669
   * It is possible that we fail while trying to send a message to our
2670
   * frontend (for example, because of encoding conversion failure).  If
2671
   * that happens it is critical that we not try to send the same message
2672
   * over and over again.  Therefore, we set ExitOnAnyError to upgrade any
2673
   * ERRORs to FATAL, causing the client connection to be closed on error.
2674
   *
2675
   * We used to only skip over the offending message and try to soldier on,
2676
   * but it was somewhat questionable to lose a notification and give the
2677
   * client an ERROR instead.  A client application is not be prepared for
2678
   * that and can't tell that a notification was missed.  It was also not
2679
   * very useful in practice because notifications are often processed while
2680
   * a connection is idle and reading a message from the client, and in that
2681
   * state, any error is upgraded to FATAL anyway.  Closing the connection
2682
   * is a clear signal to the application that it might have missed
2683
   * notifications.
2684
   */
2685
0
  {
2686
0
    bool    save_ExitOnAnyError = ExitOnAnyError;
2687
0
    bool    reachedStop;
2688
2689
0
    ExitOnAnyError = true;
2690
2691
0
    do
2692
0
    {
2693
      /*
2694
       * Process messages up to the stop position, end of page, or an
2695
       * uncommitted message.
2696
       *
2697
       * Our stop position is what we found to be the head's position
2698
       * when we entered this function. It might have changed already.
2699
       * But if it has, we will receive (or have already received and
2700
       * queued) another signal and come here again.
2701
       *
2702
       * We are not holding NotifyQueueLock here! The queue can only
2703
       * extend beyond the head pointer (see above) and we leave our
2704
       * backend's pointer where it is so nobody will truncate or
2705
       * rewrite pages under us. Especially we don't want to hold a lock
2706
       * while sending the notifications to the frontend.
2707
       */
2708
0
      reachedStop = asyncQueueProcessPageEntries(&pos, head, snapshot);
2709
0
    } while (!reachedStop);
2710
2711
    /* Update shared state */
2712
0
    LWLockAcquire(NotifyQueueLock, LW_SHARED);
2713
0
    QUEUE_BACKEND_POS(MyProcNumber) = pos;
2714
0
    QUEUE_BACKEND_IS_ADVANCING(MyProcNumber) = false;
2715
0
    LWLockRelease(NotifyQueueLock);
2716
2717
0
    ExitOnAnyError = save_ExitOnAnyError;
2718
0
  }
2719
2720
  /* Done with snapshot */
2721
0
  UnregisterSnapshot(snapshot);
2722
0
}
2723
2724
/*
2725
 * Fetch notifications from the shared queue, beginning at position current,
2726
 * and deliver relevant ones to my frontend.
2727
 *
2728
 * The function returns true once we have reached the stop position or an
2729
 * uncommitted notification, and false if we have finished with the page.
2730
 * In other words: once it returns true there is no need to look further.
2731
 * The QueuePosition *current is advanced past all processed messages.
2732
 */
2733
static bool
2734
asyncQueueProcessPageEntries(QueuePosition *current,
2735
               QueuePosition stop,
2736
               Snapshot snapshot)
2737
0
{
2738
0
  int64   curpage = QUEUE_POS_PAGE(*current);
2739
0
  int     slotno;
2740
0
  char     *page_buffer;
2741
0
  bool    reachedStop = false;
2742
0
  bool    reachedEndOfPage;
2743
2744
  /*
2745
   * We copy the entries into a local buffer to avoid holding the SLRU lock
2746
   * while we transmit them to our frontend.  The local buffer must be
2747
   * adequately aligned.
2748
   */
2749
0
  alignas(AsyncQueueEntry) char local_buf[QUEUE_PAGESIZE];
2750
0
  char     *local_buf_end = local_buf;
2751
2752
0
  slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage, current);
2753
0
  page_buffer = NotifyCtl->shared->page_buffer[slotno];
2754
2755
0
  do
2756
0
  {
2757
0
    QueuePosition thisentry = *current;
2758
0
    AsyncQueueEntry *qe;
2759
2760
0
    if (QUEUE_POS_EQUAL(thisentry, stop))
2761
0
      break;
2762
2763
0
    qe = (AsyncQueueEntry *) (page_buffer + QUEUE_POS_OFFSET(thisentry));
2764
2765
    /*
2766
     * Advance *current over this message, possibly to the next page. As
2767
     * noted in the comments for asyncQueueReadAllNotifications, we must
2768
     * do this before possibly failing while processing the message.
2769
     */
2770
0
    reachedEndOfPage = asyncQueueAdvance(current, qe->length);
2771
2772
    /* Ignore messages destined for other databases */
2773
0
    if (qe->dboid == MyDatabaseId)
2774
0
    {
2775
0
      if (XidInMVCCSnapshot(qe->xid, snapshot))
2776
0
      {
2777
        /*
2778
         * The source transaction is still in progress, so we can't
2779
         * process this message yet.  Break out of the loop, but first
2780
         * back up *current so we will reprocess the message next
2781
         * time.  (Note: it is unlikely but not impossible for
2782
         * TransactionIdDidCommit to fail, so we can't really avoid
2783
         * this advance-then-back-up behavior when dealing with an
2784
         * uncommitted message.)
2785
         *
2786
         * Note that we must test XidInMVCCSnapshot before we test
2787
         * TransactionIdDidCommit, else we might return a message from
2788
         * a transaction that is not yet visible to snapshots; compare
2789
         * the comments at the head of heapam_visibility.c.
2790
         *
2791
         * Also, while our own xact won't be listed in the snapshot,
2792
         * we need not check for TransactionIdIsCurrentTransactionId
2793
         * because our transaction cannot (yet) have queued any
2794
         * messages.
2795
         */
2796
0
        *current = thisentry;
2797
0
        reachedStop = true;
2798
0
        break;
2799
0
      }
2800
2801
      /*
2802
       * Quick check for the case that we're not listening on any
2803
       * channels, before calling TransactionIdDidCommit().  This makes
2804
       * that case a little faster, but more importantly, it ensures
2805
       * that if there's a bad entry in the queue for which
2806
       * TransactionIdDidCommit() fails for some reason, we can skip
2807
       * over it on the first LISTEN in a session, and not get stuck on
2808
       * it indefinitely.  (This is a little trickier than it looks: it
2809
       * works because BecomeRegisteredListener runs this code before we
2810
       * have made the first entry in localChannelTable.)
2811
       */
2812
0
      if (LocalChannelTableIsEmpty())
2813
0
        continue;
2814
2815
0
      if (TransactionIdDidCommit(qe->xid))
2816
0
      {
2817
0
        memcpy(local_buf_end, qe, qe->length);
2818
0
        local_buf_end += qe->length;
2819
0
      }
2820
0
      else
2821
0
      {
2822
        /*
2823
         * The source transaction aborted or crashed, so we just
2824
         * ignore its notifications.
2825
         */
2826
0
      }
2827
0
    }
2828
2829
    /* Loop back if we're not at end of page */
2830
0
  } while (!reachedEndOfPage);
2831
2832
  /* Release lock that we got from SimpleLruReadPage_ReadOnly() */
2833
0
  LWLockRelease(SimpleLruGetBankLock(NotifyCtl, curpage));
2834
2835
  /*
2836
   * Now that we have let go of the SLRU bank lock, send the notifications
2837
   * to our backend
2838
   */
2839
0
  Assert(local_buf_end - local_buf <= BLCKSZ);
2840
0
  for (char *p = local_buf; p < local_buf_end;)
2841
0
  {
2842
0
    AsyncQueueEntry *qe = (AsyncQueueEntry *) p;
2843
2844
    /* qe->data is the null-terminated channel name */
2845
0
    char     *channel = qe->data;
2846
2847
0
    if (IsListeningOn(channel))
2848
0
    {
2849
      /* payload follows channel name */
2850
0
      char     *payload = qe->data + strlen(channel) + 1;
2851
2852
0
      NotifyMyFrontEnd(channel, payload, qe->srcPid);
2853
0
    }
2854
2855
0
    p += qe->length;
2856
0
  }
2857
2858
0
  if (QUEUE_POS_EQUAL(*current, stop))
2859
0
    reachedStop = true;
2860
2861
0
  return reachedStop;
2862
0
}
2863
2864
/*
2865
 * Advance the shared queue tail variable to the minimum of all the
2866
 * per-backend tail pointers.  Truncate pg_notify space if possible.
2867
 *
2868
 * This is (usually) called during CommitTransaction(), so it's important for
2869
 * it to have very low probability of failure.
2870
 */
2871
static void
2872
asyncQueueAdvanceTail(void)
2873
0
{
2874
0
  QueuePosition min;
2875
0
  int64   oldtailpage;
2876
0
  int64   newtailpage;
2877
0
  int64   boundary;
2878
2879
  /* Restrict task to one backend per cluster; see SimpleLruTruncate(). */
2880
0
  LWLockAcquire(NotifyQueueTailLock, LW_EXCLUSIVE);
2881
2882
  /*
2883
   * Compute the new tail.  Pre-v13, it's essential that QUEUE_TAIL be exact
2884
   * (ie, exactly match at least one backend's queue position), so it must
2885
   * be updated atomically with the actual computation.  Since v13, we could
2886
   * get away with not doing it like that, but it seems prudent to keep it
2887
   * so.
2888
   *
2889
   * Also, because incoming backends will scan forward from QUEUE_TAIL, that
2890
   * must be advanced before we can truncate any data.  Thus, QUEUE_TAIL is
2891
   * the logical tail, while QUEUE_STOP_PAGE is the physical tail, or oldest
2892
   * un-truncated page.  When QUEUE_STOP_PAGE != QUEUE_POS_PAGE(QUEUE_TAIL),
2893
   * there are pages we can truncate but haven't yet finished doing so.
2894
   *
2895
   * For concurrency's sake, we don't want to hold NotifyQueueLock while
2896
   * performing SimpleLruTruncate.  This is OK because no backend will try
2897
   * to access the pages we are in the midst of truncating.
2898
   */
2899
0
  LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
2900
0
  min = QUEUE_HEAD;
2901
0
  for (ProcNumber i = QUEUE_FIRST_LISTENER; i != INVALID_PROC_NUMBER; i = QUEUE_NEXT_LISTENER(i))
2902
0
  {
2903
0
    Assert(QUEUE_BACKEND_PID(i) != InvalidPid);
2904
0
    min = QUEUE_POS_MIN(min, QUEUE_BACKEND_POS(i));
2905
0
  }
2906
0
  QUEUE_TAIL = min;
2907
0
  oldtailpage = QUEUE_STOP_PAGE;
2908
0
  LWLockRelease(NotifyQueueLock);
2909
2910
  /*
2911
   * We can truncate something if the global tail advanced across an SLRU
2912
   * segment boundary.
2913
   *
2914
   * XXX it might be better to truncate only once every several segments, to
2915
   * reduce the number of directory scans.
2916
   */
2917
0
  newtailpage = QUEUE_POS_PAGE(min);
2918
0
  boundary = newtailpage - (newtailpage % SLRU_PAGES_PER_SEGMENT);
2919
0
  if (asyncQueuePagePrecedes(oldtailpage, boundary))
2920
0
  {
2921
    /*
2922
     * SimpleLruTruncate() will ask for SLRU bank locks but will also
2923
     * release the lock again.
2924
     */
2925
0
    SimpleLruTruncate(NotifyCtl, newtailpage);
2926
2927
0
    LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
2928
0
    QUEUE_STOP_PAGE = newtailpage;
2929
0
    LWLockRelease(NotifyQueueLock);
2930
0
  }
2931
2932
0
  LWLockRelease(NotifyQueueTailLock);
2933
0
}
2934
2935
/*
2936
 * AsyncNotifyFreezeXids
2937
 *
2938
 * Prepare the async notification queue for CLOG truncation by freezing
2939
 * transaction IDs that are about to become inaccessible.
2940
 *
2941
 * This function is called by VACUUM before advancing datfrozenxid. It scans
2942
 * the notification queue and replaces XIDs that would become inaccessible
2943
 * after CLOG truncation with special markers:
2944
 * - Committed transactions are set to FrozenTransactionId
2945
 * - Aborted/crashed transactions are set to InvalidTransactionId
2946
 *
2947
 * Only XIDs < newFrozenXid are processed, as those are the ones whose CLOG
2948
 * pages will be truncated. If XID < newFrozenXid, it cannot still be running
2949
 * (or it would have held back newFrozenXid through ProcArray).
2950
 * Therefore, if TransactionIdDidCommit returns false, we know the transaction
2951
 * either aborted explicitly or crashed, and we can safely mark it invalid.
2952
 */
2953
void
2954
AsyncNotifyFreezeXids(TransactionId newFrozenXid)
2955
0
{
2956
0
  QueuePosition pos;
2957
0
  QueuePosition head;
2958
0
  int64   curpage = -1;
2959
0
  int     slotno = -1;
2960
0
  char     *page_buffer = NULL;
2961
0
  bool    page_dirty = false;
2962
2963
  /*
2964
   * Acquire locks in the correct order to avoid deadlocks. As per the
2965
   * locking protocol: NotifyQueueTailLock, then NotifyQueueLock, then SLRU
2966
   * bank locks.
2967
   *
2968
   * We only need SHARED mode since we're just reading the head/tail
2969
   * positions, not modifying them.
2970
   */
2971
0
  LWLockAcquire(NotifyQueueTailLock, LW_SHARED);
2972
0
  LWLockAcquire(NotifyQueueLock, LW_SHARED);
2973
2974
0
  pos = QUEUE_TAIL;
2975
0
  head = QUEUE_HEAD;
2976
2977
  /* Release NotifyQueueLock early, we only needed to read the positions */
2978
0
  LWLockRelease(NotifyQueueLock);
2979
2980
  /*
2981
   * Scan the queue from tail to head, freezing XIDs as needed. We hold
2982
   * NotifyQueueTailLock throughout to ensure the tail doesn't move while
2983
   * we're working.
2984
   */
2985
0
  while (!QUEUE_POS_EQUAL(pos, head))
2986
0
  {
2987
0
    AsyncQueueEntry *qe;
2988
0
    TransactionId xid;
2989
0
    int64   pageno = QUEUE_POS_PAGE(pos);
2990
0
    int     offset = QUEUE_POS_OFFSET(pos);
2991
2992
    /* If we need a different page, release old lock and get new one */
2993
0
    if (pageno != curpage)
2994
0
    {
2995
0
      LWLock     *lock;
2996
2997
      /* Release previous page if any */
2998
0
      if (slotno >= 0)
2999
0
      {
3000
0
        if (page_dirty)
3001
0
        {
3002
0
          NotifyCtl->shared->page_dirty[slotno] = true;
3003
0
          page_dirty = false;
3004
0
        }
3005
0
        LWLockRelease(SimpleLruGetBankLock(NotifyCtl, curpage));
3006
0
      }
3007
3008
0
      lock = SimpleLruGetBankLock(NotifyCtl, pageno);
3009
0
      LWLockAcquire(lock, LW_EXCLUSIVE);
3010
0
      slotno = SimpleLruReadPage(NotifyCtl, pageno, true, &pos);
3011
0
      page_buffer = NotifyCtl->shared->page_buffer[slotno];
3012
0
      curpage = pageno;
3013
0
    }
3014
3015
0
    qe = (AsyncQueueEntry *) (page_buffer + offset);
3016
0
    xid = qe->xid;
3017
3018
0
    if (TransactionIdIsNormal(xid) &&
3019
0
      TransactionIdPrecedes(xid, newFrozenXid))
3020
0
    {
3021
0
      if (TransactionIdDidCommit(xid))
3022
0
      {
3023
0
        qe->xid = FrozenTransactionId;
3024
0
        page_dirty = true;
3025
0
      }
3026
0
      else
3027
0
      {
3028
0
        qe->xid = InvalidTransactionId;
3029
0
        page_dirty = true;
3030
0
      }
3031
0
    }
3032
3033
    /* Advance to next entry */
3034
0
    asyncQueueAdvance(&pos, qe->length);
3035
0
  }
3036
3037
  /* Release final page lock if we acquired one */
3038
0
  if (slotno >= 0)
3039
0
  {
3040
0
    if (page_dirty)
3041
0
      NotifyCtl->shared->page_dirty[slotno] = true;
3042
0
    LWLockRelease(SimpleLruGetBankLock(NotifyCtl, curpage));
3043
0
  }
3044
3045
0
  LWLockRelease(NotifyQueueTailLock);
3046
0
}
3047
3048
/*
3049
 * ProcessIncomingNotify
3050
 *
3051
 *    Scan the queue for arriving notifications and report them to the front
3052
 *    end.  The notifications might be from other sessions, or our own;
3053
 *    there's no need to distinguish here.
3054
 *
3055
 *    If "flush" is true, force any frontend messages out immediately.
3056
 *
3057
 *    NOTE: since we are outside any transaction, we must create our own.
3058
 */
3059
static void
3060
ProcessIncomingNotify(bool flush)
3061
{
3062
  /* We *must* reset the flag */
3063
  notifyInterruptPending = false;
3064
3065
  /* Do nothing else if we aren't actively listening */
3066
  if (LocalChannelTableIsEmpty())
3067
    return;
3068
3069
  if (Trace_notify)
3070
    elog(DEBUG1, "ProcessIncomingNotify");
3071
3072
  set_ps_display("notify interrupt");
3073
3074
  /*
3075
   * We must run asyncQueueReadAllNotifications inside a transaction, else
3076
   * bad things happen if it gets an error.
3077
   */
3078
  StartTransactionCommand();
3079
3080
  asyncQueueReadAllNotifications();
3081
3082
  CommitTransactionCommand();
3083
3084
  /*
3085
   * If this isn't an end-of-command case, we must flush the notify messages
3086
   * to ensure frontend gets them promptly.
3087
   */
3088
  if (flush)
3089
    pq_flush();
3090
3091
  set_ps_display("idle");
3092
3093
  if (Trace_notify)
3094
    elog(DEBUG1, "ProcessIncomingNotify: done");
3095
}
3096
3097
/*
3098
 * Send NOTIFY message to my front end.
3099
 */
3100
void
3101
NotifyMyFrontEnd(const char *channel, const char *payload, int32 srcPid)
3102
{
3103
  if (whereToSendOutput == DestRemote)
3104
  {
3105
    StringInfoData buf;
3106
3107
    pq_beginmessage(&buf, PqMsg_NotificationResponse);
3108
    pq_sendint32(&buf, srcPid);
3109
    pq_sendstring(&buf, channel);
3110
    pq_sendstring(&buf, payload);
3111
    pq_endmessage(&buf);
3112
3113
    /*
3114
     * NOTE: we do not do pq_flush() here.  Some level of caller will
3115
     * handle it later, allowing this message to be combined into a packet
3116
     * with other ones.
3117
     */
3118
  }
3119
  else
3120
    elog(INFO, "NOTIFY for \"%s\" payload \"%s\"", channel, payload);
3121
}
3122
3123
/* Does pendingNotifies include a match for the given event? */
3124
static bool
3125
AsyncExistsPendingNotify(Notification *n)
3126
0
{
3127
0
  if (pendingNotifies == NULL)
3128
0
    return false;
3129
3130
0
  if (pendingNotifies->hashtab != NULL)
3131
0
  {
3132
    /* Use the hash table to probe for a match */
3133
0
    if (hash_search(pendingNotifies->hashtab,
3134
0
            &n,
3135
0
            HASH_FIND,
3136
0
            NULL))
3137
0
      return true;
3138
0
  }
3139
0
  else
3140
0
  {
3141
    /* Must scan the event list */
3142
0
    ListCell   *l;
3143
3144
0
    foreach(l, pendingNotifies->events)
3145
0
    {
3146
0
      Notification *oldn = (Notification *) lfirst(l);
3147
3148
0
      if (n->channel_len == oldn->channel_len &&
3149
0
        n->payload_len == oldn->payload_len &&
3150
0
        memcmp(n->data, oldn->data,
3151
0
             n->channel_len + n->payload_len + 2) == 0)
3152
0
        return true;
3153
0
    }
3154
0
  }
3155
3156
0
  return false;
3157
0
}
3158
3159
/*
3160
 * Add a notification event to a pre-existing pendingNotifies list.
3161
 *
3162
 * Because pendingNotifies->events is already nonempty, this works
3163
 * correctly no matter what CurrentMemoryContext is.
3164
 */
3165
static void
3166
AddEventToPendingNotifies(Notification *n)
3167
0
{
3168
0
  Assert(pendingNotifies->events != NIL);
3169
3170
  /* Create the hash tables if it's time to */
3171
0
  if (list_length(pendingNotifies->events) >= MIN_HASHABLE_NOTIFIES &&
3172
0
    pendingNotifies->hashtab == NULL)
3173
0
  {
3174
0
    HASHCTL   hash_ctl;
3175
0
    ListCell   *l;
3176
3177
    /* Create the hash table */
3178
0
    hash_ctl.keysize = sizeof(Notification *);
3179
0
    hash_ctl.entrysize = sizeof(struct NotificationHash);
3180
0
    hash_ctl.hash = notification_hash;
3181
0
    hash_ctl.match = notification_match;
3182
0
    hash_ctl.hcxt = CurTransactionContext;
3183
0
    pendingNotifies->hashtab =
3184
0
      hash_create("Pending Notifies",
3185
0
            256L,
3186
0
            &hash_ctl,
3187
0
            HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
3188
3189
    /* Create the unique channel name table */
3190
0
    Assert(pendingNotifies->uniqueChannelHash == NULL);
3191
0
    hash_ctl.keysize = NAMEDATALEN;
3192
0
    hash_ctl.entrysize = sizeof(ChannelName);
3193
0
    hash_ctl.hcxt = CurTransactionContext;
3194
0
    pendingNotifies->uniqueChannelHash =
3195
0
      hash_create("Pending Notify Channel Names",
3196
0
            64L,
3197
0
            &hash_ctl,
3198
0
            HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
3199
3200
    /* Insert all the already-existing events */
3201
0
    foreach(l, pendingNotifies->events)
3202
0
    {
3203
0
      Notification *oldn = (Notification *) lfirst(l);
3204
0
      char     *channel = oldn->data;
3205
0
      bool    found;
3206
3207
0
      (void) hash_search(pendingNotifies->hashtab,
3208
0
                 &oldn,
3209
0
                 HASH_ENTER,
3210
0
                 &found);
3211
0
      Assert(!found);
3212
3213
      /* Add channel name to uniqueChannelHash; might be there already */
3214
0
      (void) hash_search(pendingNotifies->uniqueChannelHash,
3215
0
                 channel,
3216
0
                 HASH_ENTER,
3217
0
                 NULL);
3218
0
    }
3219
0
  }
3220
3221
  /* Add new event to the list, in order */
3222
0
  pendingNotifies->events = lappend(pendingNotifies->events, n);
3223
3224
  /* Add event to the hash tables if needed */
3225
0
  if (pendingNotifies->hashtab != NULL)
3226
0
  {
3227
0
    char     *channel = n->data;
3228
0
    bool    found;
3229
3230
0
    (void) hash_search(pendingNotifies->hashtab,
3231
0
               &n,
3232
0
               HASH_ENTER,
3233
0
               &found);
3234
0
    Assert(!found);
3235
3236
    /* Add channel name to uniqueChannelHash; might be there already */
3237
0
    (void) hash_search(pendingNotifies->uniqueChannelHash,
3238
0
               channel,
3239
0
               HASH_ENTER,
3240
0
               NULL);
3241
0
  }
3242
0
}
3243
3244
/*
3245
 * notification_hash: hash function for notification hash table
3246
 *
3247
 * The hash "keys" are pointers to Notification structs.
3248
 */
3249
static uint32
3250
notification_hash(const void *key, Size keysize)
3251
0
{
3252
0
  const Notification *k = *(const Notification *const *) key;
3253
3254
0
  Assert(keysize == sizeof(Notification *));
3255
  /* We don't bother to include the payload's trailing null in the hash */
3256
0
  return hash_bytes((const unsigned char *) k->data,
3257
0
            k->channel_len + k->payload_len + 1);
3258
0
}
3259
3260
/*
3261
 * notification_match: match function to use with notification_hash
3262
 */
3263
static int
3264
notification_match(const void *key1, const void *key2, Size keysize)
3265
0
{
3266
0
  const Notification *k1 = *(const Notification *const *) key1;
3267
0
  const Notification *k2 = *(const Notification *const *) key2;
3268
3269
0
  Assert(keysize == sizeof(Notification *));
3270
0
  if (k1->channel_len == k2->channel_len &&
3271
0
    k1->payload_len == k2->payload_len &&
3272
0
    memcmp(k1->data, k2->data,
3273
0
         k1->channel_len + k1->payload_len + 2) == 0)
3274
0
    return 0;       /* equal */
3275
0
  return 1;         /* not equal */
3276
0
}
3277
3278
/* Clear the pendingActions and pendingNotifies lists. */
3279
static void
3280
ClearPendingActionsAndNotifies(void)
3281
0
{
3282
  /*
3283
   * Everything's allocated in either TopTransactionContext or the context
3284
   * for the subtransaction to which it corresponds.  So, there's nothing to
3285
   * do here except reset the pointers; the space will be reclaimed when the
3286
   * contexts are deleted.
3287
   */
3288
0
  pendingActions = NULL;
3289
0
  pendingNotifies = NULL;
3290
  /* Also clear pendingListenActions, which is derived from pendingActions */
3291
0
  pendingListenActions = NULL;
3292
0
}
3293
3294
/*
3295
 * GUC check_hook for notify_buffers
3296
 */
3297
bool
3298
check_notify_buffers(int *newval, void **extra, GucSource source)
3299
2
{
3300
2
  return check_slru_buffers("notify_buffers", newval);
3301
2
}