v0-0001-Consider-async-queue-min-xid-on-VACUUM-FREEZE.patch
text/plain
Filename: v0-0001-Consider-async-queue-min-xid-on-VACUUM-FREEZE.patch
Type: text/plain
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v0-0001
Subject: Consider async queue min xid on VACUUM FREEZE
| File | + | − |
|---|---|---|
| src/backend/commands/async.c | 58 | 0 |
| src/backend/commands/vacuum.c | 11 | 0 |
| src/include/commands/async.h | 3 | 0 |
From 7ec33dfc14174e9a4424a1738977601a4fd13bf5 Mon Sep 17 00:00:00 2001
From: Matheus Alcantara <mths.dev@pm.me>
Date: Sat, 9 Aug 2025 13:51:21 -0300
Subject: [PATCH v0] Consider async queue min xid on VACUUM FREEZE
---
src/backend/commands/async.c | 58 +++++++++++++++++++++++++++++++++++
src/backend/commands/vacuum.c | 11 +++++++
src/include/commands/async.h | 3 ++
3 files changed, 72 insertions(+)
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 4bd37d5beb5..fff055601a1 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -287,6 +287,7 @@ typedef struct AsyncQueueControl
* tail.page */
ProcNumber firstListener; /* id of first listener, or
* INVALID_PROC_NUMBER */
+ TransactionId minXid;
TimestampTz lastQueueFillWarn; /* time of last queue-full msg */
QueueBackendStatus backend[FLEXIBLE_ARRAY_MEMBER];
} AsyncQueueControl;
@@ -458,6 +459,9 @@ static uint32 notification_hash(const void *key, Size keysize);
static int notification_match(const void *key1, const void *key2, Size keysize);
static void ClearPendingActionsAndNotifies(void);
+static void StoreEntryXid(TransactionId xid);
+static void ReleaseEntryXid(TransactionId xid);
+
/*
* Compute the difference between two queue page numbers.
* Previously this function accounted for a wraparound.
@@ -521,6 +525,7 @@ AsyncShmemInit(void)
QUEUE_STOP_PAGE = 0;
QUEUE_FIRST_LISTENER = INVALID_PROC_NUMBER;
asyncQueueControl->lastQueueFillWarn = 0;
+ asyncQueueControl->minXid = MaxTransactionId;
for (int i = 0; i < MaxBackends; i++)
{
QUEUE_BACKEND_PID(i) = InvalidPid;
@@ -1428,6 +1433,12 @@ asyncQueueAddEntries(ListCell *nextNotify)
&qe,
qe.length);
+ /*
+ * Remember the notification xid so that vacuum don't frozen after
+ * this xid.
+ */
+ StoreEntryXid(qe.xid);
+
/* Advance queue_head appropriately, and detect if page is full */
if (asyncQueueAdvance(&(queue_head), qe.length))
{
@@ -2077,6 +2088,12 @@ asyncQueueProcessPageEntries(volatile QueuePosition *current,
char *payload = qe->data + strlen(channel) + 1;
NotifyMyFrontEnd(channel, payload, qe->srcPid);
+
+ /*
+ * Notification was sent, so release the notification xid
+ * so that vacuum can freeze past this notification.
+ */
+ ReleaseEntryXid(qe->xid);
}
}
else
@@ -2395,3 +2412,44 @@ check_notify_buffers(int *newval, void **extra, GucSource source)
{
return check_slru_buffers("notify_buffers", newval);
}
+
+/*
+ * Return the minimum notification xid on the queue.
+ *
+ * TODO(matheus): handle when the queue is empty, so we don't get locked in
+ * the past with a notification that was already sent.
+ */
+TransactionId
+AsyncQueueMinXid()
+{
+ return asyncQueueControl->minXid;
+}
+
+/*
+ * Subroutine of asyncQueueAddEntries
+ *
+ * We are holding NotifyQueueLock already from the caller.
+ */
+static void
+StoreEntryXid(TransactionId xid)
+{
+ if (xid < asyncQueueControl->minXid)
+ asyncQueueControl->minXid = xid;
+}
+
+/*
+ * Subroutine of asyncQueueAddEntries
+ *
+ * TODO(matheus): may update the code comment on
+ * asyncQeueuReadAllNotifications() when calling
+ * asyncQueueProcessPageEntries().
+ *
+ */
+static void
+ReleaseEntryXid(TransactionId xid)
+{
+ LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
+ if (xid > asyncQueueControl->minXid)
+ asyncQueueControl->minXid = xid;
+ LWLockRelease(NotifyQueueLock);
+}
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 733ef40ae7c..f0d6d868e60 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -21,6 +21,7 @@
*
*-------------------------------------------------------------------------
*/
+#include "commands/async.h"
#include "postgres.h"
#include <math.h>
@@ -1739,6 +1740,16 @@ vac_update_datfrozenxid(void)
if (bogus)
return;
+ /*
+ * We need to check transaction status of notifications before of notify
+ * the client, if there is lag to consume the notifications we need to
+ * consider the older xid of notification on the queue so that the
+ * transaction status can be accessed.
+ *
+ * XXX(matheus): Wrap this behavior into a GUC?
+ */
+ newFrozenXid = Min(newFrozenXid, AsyncQueueMinXid());
+
Assert(TransactionIdIsNormal(newFrozenXid));
Assert(MultiXactIdIsValid(newMinMulti));
diff --git a/src/include/commands/async.h b/src/include/commands/async.h
index f75c3df9556..512a8976126 100644
--- a/src/include/commands/async.h
+++ b/src/include/commands/async.h
@@ -13,6 +13,7 @@
#ifndef ASYNC_H
#define ASYNC_H
+#include <c.h>
#include <signal.h>
extern PGDLLIMPORT bool Trace_notify;
@@ -46,4 +47,6 @@ extern void HandleNotifyInterrupt(void);
/* process interrupts */
extern void ProcessNotifyInterrupt(bool flush);
+extern TransactionId AsyncQueueMinXid(void);
+
#endif /* ASYNC_H */
--
2.39.5 (Apple Git-154)