|
103 | 103 | * until we reach either a notification from an uncommitted transaction or
|
104 | 104 | * the head pointer's position.
|
105 | 105 | *
|
106 |
| - * 6. To avoid SLRU wraparound and limit disk space consumption, the tail |
107 |
| - * pointer needs to be advanced so that old pages can be truncated. |
108 |
| - * This is relatively expensive (notably, it requires an exclusive lock), |
109 |
| - * so we don't want to do it often. We make sending backends do this work |
110 |
| - * if they advanced the queue head into a new page, but only once every |
111 |
| - * QUEUE_CLEANUP_DELAY pages. |
| 106 | + * 6. To limit disk space consumption, the tail pointer needs to be advanced |
| 107 | + * so that old pages can be truncated. This is relatively expensive |
| 108 | + * (notably, it requires an exclusive lock), so we don't want to do it |
| 109 | + * often. We make sending backends do this work if they advanced the queue |
| 110 | + * head into a new page, but only once every QUEUE_CLEANUP_DELAY pages. |
112 | 111 | *
|
113 | 112 | * An application that listens on the same channel it notifies will get
|
114 | 113 | * NOTIFY messages for its own NOTIFYs. These can be ignored, if not useful,
|
|
120 | 119 | * The amount of shared memory used for notify management (NUM_NOTIFY_BUFFERS)
|
121 | 120 | * can be varied without affecting anything but performance. The maximum
|
122 | 121 | * amount of notification data that can be queued at one time is determined
|
123 |
| - * by slru.c's wraparound limit; see QUEUE_MAX_PAGE below. |
| 122 | + * by max_notify_queue_pages GUC. |
124 | 123 | *-------------------------------------------------------------------------
|
125 | 124 | */
|
126 | 125 |
|
@@ -312,23 +311,8 @@ static SlruCtlData NotifyCtlData;
|
312 | 311 |
|
313 | 312 | #define NotifyCtl (&NotifyCtlData)
|
314 | 313 | #define QUEUE_PAGESIZE BLCKSZ
|
315 |
| -#define QUEUE_FULL_WARN_INTERVAL 5000 /* warn at most once every 5s */ |
316 | 314 |
|
317 |
| -/* |
318 |
| - * Use segments 0000 through FFFF. Each contains SLRU_PAGES_PER_SEGMENT pages |
319 |
| - * which gives us the pages from 0 to SLRU_PAGES_PER_SEGMENT * 0x10000 - 1. |
320 |
| - * We could use as many segments as SlruScanDirectory() allows, but this gives |
321 |
| - * us so much space already that it doesn't seem worth the trouble. |
322 |
| - * |
323 |
| - * The most data we can have in the queue at a time is QUEUE_MAX_PAGE/2 |
324 |
| - * pages, because more than that would confuse slru.c into thinking there |
325 |
| - * was a wraparound condition. With the default BLCKSZ this means there |
326 |
| - * can be up to 8GB of queued-and-not-read data. |
327 |
| - * |
328 |
| - * Note: it's possible to redefine QUEUE_MAX_PAGE with a smaller multiple of |
329 |
| - * SLRU_PAGES_PER_SEGMENT, for easier testing of queue-full behaviour. |
330 |
| - */ |
331 |
| -#define QUEUE_MAX_PAGE (SLRU_PAGES_PER_SEGMENT * 0x10000 - 1) |
| 315 | +#define QUEUE_FULL_WARN_INTERVAL 5000 /* warn at most once every 5s */ |
332 | 316 |
|
333 | 317 | /*
|
334 | 318 | * listenChannels identifies the channels we are actually listening to
|
@@ -439,12 +423,15 @@ static bool amRegisteredListener = false;
|
439 | 423 | /* have we advanced to a page that's a multiple of QUEUE_CLEANUP_DELAY? */
|
440 | 424 | static bool tryAdvanceTail = false;
|
441 | 425 |
|
442 |
| -/* GUC parameter */ |
| 426 | +/* GUC parameters */ |
443 | 427 | bool Trace_notify = false;
|
444 | 428 |
|
| 429 | +/* For 8 KB pages this gives 8 GB of disk space */ |
| 430 | +int max_notify_queue_pages = 1048576; |
| 431 | + |
445 | 432 | /* local function prototypes */
|
446 |
| -static int64 asyncQueuePageDiff(int64 p, int64 q); |
447 |
| -static bool asyncQueuePagePrecedes(int64 p, int64 q); |
| 433 | +static inline int64 asyncQueuePageDiff(int64 p, int64 q); |
| 434 | +static inline bool asyncQueuePagePrecedes(int64 p, int64 q); |
448 | 435 | static void queue_listen(ListenActionKind action, const char *channel);
|
449 | 436 | static void Async_UnlistenOnExit(int code, Datum arg);
|
450 | 437 | static void Exec_ListenPreCommit(void);
|
@@ -474,39 +461,23 @@ static int notification_match(const void *key1, const void *key2, Size keysize);
|
474 | 461 | static void ClearPendingActionsAndNotifies(void);
|
475 | 462 |
|
476 | 463 | /*
|
477 |
| - * Compute the difference between two queue page numbers (i.e., p - q), |
478 |
| - * accounting for wraparound. |
| 464 | + * Compute the difference between two queue page numbers. |
| 465 | + * Previously this function accounted for a wraparound. |
479 | 466 | */
|
480 |
| -static int64 |
| 467 | +static inline int64 |
481 | 468 | asyncQueuePageDiff(int64 p, int64 q)
|
482 | 469 | {
|
483 |
| - int64 diff; |
484 |
| - |
485 |
| - /* |
486 |
| - * We have to compare modulo (QUEUE_MAX_PAGE+1)/2. Both inputs should be |
487 |
| - * in the range 0..QUEUE_MAX_PAGE. |
488 |
| - */ |
489 |
| - Assert(p >= 0 && p <= QUEUE_MAX_PAGE); |
490 |
| - Assert(q >= 0 && q <= QUEUE_MAX_PAGE); |
491 |
| - |
492 |
| - diff = p - q; |
493 |
| - if (diff >= ((QUEUE_MAX_PAGE + 1) / 2)) |
494 |
| - diff -= QUEUE_MAX_PAGE + 1; |
495 |
| - else if (diff < -((QUEUE_MAX_PAGE + 1) / 2)) |
496 |
| - diff += QUEUE_MAX_PAGE + 1; |
497 |
| - return diff; |
| 470 | + return p - q; |
498 | 471 | }
|
499 | 472 |
|
500 | 473 | /*
|
501 |
| - * Is p < q, accounting for wraparound? |
502 |
| - * |
503 |
| - * Since asyncQueueIsFull() blocks creation of a page that could precede any |
504 |
| - * extant page, we need not assess entries within a page. |
| 474 | + * Determines whether p precedes q. |
| 475 | + * Previously this function accounted for a wraparound. |
505 | 476 | */
|
506 |
| -static bool |
| 477 | +static inline bool |
507 | 478 | asyncQueuePagePrecedes(int64 p, int64 q)
|
508 | 479 | {
|
509 |
| - return asyncQueuePageDiff(p, q) < 0; |
| 480 | + return p < q; |
510 | 481 | }
|
511 | 482 |
|
512 | 483 | /*
|
@@ -566,12 +537,13 @@ AsyncShmemInit(void)
|
566 | 537 | }
|
567 | 538 |
|
568 | 539 | /*
|
569 |
| - * Set up SLRU management of the pg_notify data. |
| 540 | + * Set up SLRU management of the pg_notify data. Note that long segment |
| 541 | + * names are used in order to avoid wraparound. |
570 | 542 | */
|
571 | 543 | NotifyCtl->PagePrecedes = asyncQueuePagePrecedes;
|
572 | 544 | SimpleLruInit(NotifyCtl, "Notify", NUM_NOTIFY_BUFFERS, 0,
|
573 | 545 | NotifySLRULock, "pg_notify", LWTRANCHE_NOTIFY_BUFFER,
|
574 |
| - SYNC_HANDLER_NONE, false); |
| 546 | + SYNC_HANDLER_NONE, true); |
575 | 547 |
|
576 | 548 | if (!found)
|
577 | 549 | {
|
@@ -1305,27 +1277,11 @@ asyncQueueUnregister(void)
|
1305 | 1277 | static bool
|
1306 | 1278 | asyncQueueIsFull(void)
|
1307 | 1279 | {
|
1308 |
| - int nexthead; |
1309 |
| - int boundary; |
| 1280 | + int headPage = QUEUE_POS_PAGE(QUEUE_HEAD); |
| 1281 | + int tailPage = QUEUE_POS_PAGE(QUEUE_TAIL); |
| 1282 | + int occupied = headPage - tailPage; |
1310 | 1283 |
|
1311 |
| - /* |
1312 |
| - * The queue is full if creating a new head page would create a page that |
1313 |
| - * logically precedes the current global tail pointer, ie, the head |
1314 |
| - * pointer would wrap around compared to the tail. We cannot create such |
1315 |
| - * a head page for fear of confusing slru.c. For safety we round the tail |
1316 |
| - * pointer back to a segment boundary (truncation logic in |
1317 |
| - * asyncQueueAdvanceTail does not do this, so doing it here is optional). |
1318 |
| - * |
1319 |
| - * Note that this test is *not* dependent on how much space there is on |
1320 |
| - * the current head page. This is necessary because asyncQueueAddEntries |
1321 |
| - * might try to create the next head page in any case. |
1322 |
| - */ |
1323 |
| - nexthead = QUEUE_POS_PAGE(QUEUE_HEAD) + 1; |
1324 |
| - if (nexthead > QUEUE_MAX_PAGE) |
1325 |
| - nexthead = 0; /* wrap around */ |
1326 |
| - boundary = QUEUE_STOP_PAGE; |
1327 |
| - boundary -= boundary % SLRU_PAGES_PER_SEGMENT; |
1328 |
| - return asyncQueuePagePrecedes(nexthead, boundary); |
| 1284 | + return occupied >= max_notify_queue_pages; |
1329 | 1285 | }
|
1330 | 1286 |
|
1331 | 1287 | /*
|
@@ -1355,8 +1311,6 @@ asyncQueueAdvance(volatile QueuePosition *position, int entryLength)
|
1355 | 1311 | if (offset + QUEUEALIGN(AsyncQueueEntryEmptySize) > QUEUE_PAGESIZE)
|
1356 | 1312 | {
|
1357 | 1313 | pageno++;
|
1358 |
| - if (pageno > QUEUE_MAX_PAGE) |
1359 |
| - pageno = 0; /* wrap around */ |
1360 | 1314 | offset = 0;
|
1361 | 1315 | pageJump = true;
|
1362 | 1316 | }
|
@@ -1433,9 +1387,6 @@ asyncQueueAddEntries(ListCell *nextNotify)
|
1433 | 1387 | * If this is the first write since the postmaster started, we need to
|
1434 | 1388 | * initialize the first page of the async SLRU. Otherwise, the current
|
1435 | 1389 | * page should be initialized already, so just fetch it.
|
1436 |
| - * |
1437 |
| - * (We could also take the first path when the SLRU position has just |
1438 |
| - * wrapped around, but re-zeroing the page is harmless in that case.) |
1439 | 1390 | */
|
1440 | 1391 | pageno = QUEUE_POS_PAGE(queue_head);
|
1441 | 1392 | if (QUEUE_POS_IS_ZERO(queue_head))
|
@@ -1548,20 +1499,12 @@ asyncQueueUsage(void)
|
1548 | 1499 | {
|
1549 | 1500 | int headPage = QUEUE_POS_PAGE(QUEUE_HEAD);
|
1550 | 1501 | int tailPage = QUEUE_POS_PAGE(QUEUE_TAIL);
|
1551 |
| - int occupied; |
1552 |
| - |
1553 |
| - occupied = headPage - tailPage; |
| 1502 | + int occupied = headPage - tailPage; |
1554 | 1503 |
|
1555 | 1504 | if (occupied == 0)
|
1556 | 1505 | return (double) 0; /* fast exit for common case */
|
1557 | 1506 |
|
1558 |
| - if (occupied < 0) |
1559 |
| - { |
1560 |
| - /* head has wrapped around, tail not yet */ |
1561 |
| - occupied += QUEUE_MAX_PAGE + 1; |
1562 |
| - } |
1563 |
| - |
1564 |
| - return (double) occupied / (double) ((QUEUE_MAX_PAGE + 1) / 2); |
| 1507 | + return (double) occupied / (double) max_notify_queue_pages; |
1565 | 1508 | }
|
1566 | 1509 |
|
1567 | 1510 | /*
|
@@ -2209,11 +2152,6 @@ asyncQueueAdvanceTail(void)
|
2209 | 2152 | */
|
2210 | 2153 | SimpleLruTruncate(NotifyCtl, newtailpage);
|
2211 | 2154 |
|
2212 |
| - /* |
2213 |
| - * Update QUEUE_STOP_PAGE. This changes asyncQueueIsFull()'s verdict |
2214 |
| - * for the segment immediately prior to the old tail, allowing fresh |
2215 |
| - * data into that segment. |
2216 |
| - */ |
2217 | 2155 | LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
|
2218 | 2156 | QUEUE_STOP_PAGE = newtailpage;
|
2219 | 2157 | LWLockRelease(NotifyQueueLock);
|
|
0 commit comments