public inbox for [email protected]
help / color / mirror / Atom feedFrom: Bharath Rupireddy <[email protected]>
To: Jeff Davis <[email protected]>
Cc: Andres Freund <[email protected]>
Cc: Dilip Kumar <[email protected]>
Cc: Kyotaro Horiguchi <[email protected]>
Cc: [email protected]
Cc: Nathan Bossart <[email protected]>
Cc: Masahiko Sawada <[email protected]>
Subject: Re: Improve WALRead() to suck data directly from WAL buffers when possible
Date: Sat, 27 Jan 2024 13:30:00 +0530
Message-ID: <CALj2ACW4oZzmg6joL0ppQN=cCGOLaVKJknc3OLqmo8Q+cGDRKw@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <CALj2ACV123Ke6wn05QCnCgJhFpcarGCv7sKM1p39t+qpUNHUgQ@mail.gmail.com>
<[email protected]>
<CALj2ACUSydTHPcucftm4XdiJ4osH8zeNENyADd-zfDzEMf6akA@mail.gmail.com>
<[email protected]>
<CALj2ACXo-_boM3h-PJuvfuvFzP1nv2wcZBoZN_YbRbQBGQYAUg@mail.gmail.com>
<CALj2ACVfFMfqD5oLzZSQQZWfXiJqd-NdX0_317veP6FuB31QWA@mail.gmail.com>
<[email protected]>
<CALj2ACXy5eA-Y4Qgm3MUYZTGr_opNm2SzGtN+Zj6dbcSzqHrJA@mail.gmail.com>
<[email protected]>
<CALj2ACWPKuoUdA7r4VFKhsTZeNekyUr=ZwM6ckyvJ0qv2f6Whg@mail.gmail.com>
<[email protected]>
<[email protected]>
<CALj2ACV=C1GZT9XQRm4iN1NV1T=hLA_hsGWNx2Y5-G+mSwdhNg@mail.gmail.com>
<[email protected]>
<CALj2ACW65mqn6Ukv57SqDTMzAJgd1N_AdQtDgy+gMDqu6v618Q@mail.gmail.com>
<[email protected]>
On Sat, Jan 27, 2024 at 1:04 AM Jeff Davis <[email protected]> wrote:
>
> All of these things are functionally equivalent -- the same thing is
> happening at the end. This is just a discussion about API style and how
> that will interact with hypothetical callers that don't exist today.
> And it can also be easily changed later, so we aren't stuck with
> whatever decision happens here.
I'll leave that up to you. I'm okay either ways - 1) ensure the caller
doesn't use XLogReadFromBuffers, 2) XLogReadFromBuffers returning
as-if nothing was read when in recovery or on a different timeline.
> > Imagine, implementing an extension (may be for fun or learning or
> > educational or production purposes) to read unflushed WAL directly
> > from WAL buffers using XLogReadFromBuffers as page_read callback with
> > xlogreader facility.
>
> That makes sense, I didn't realize you intended to use this fron an
> extension. I'm fine considering that as a separate patch that could
> potentially be committed soon after this one.
Yes, I've turned that into 0002 patch.
> I'd like some more details, but can I please just commit the basic
> functionality now-ish?
+1.
> > Tried to keep wal_writer quiet with wal_writer_delay=10000ms and
> > wal_writer_flush_after = 1GB to not to flush WAL in the background.
> > Also, disabled autovacuum, and set checkpoint_timeout to a higher
> > value. All of this is done to generate minimal WAL so that WAL
> > buffers
> > don't get overwritten. Do you see any problems with it?
>
> Maybe check it against pg_current_wal_lsn(), and see if the Write
> pointer moved ahead? Perhaps even have a (limited) loop that tries
> again to catch it at the right time?
Adding a loop seems to be reasonable here and done in v21-0003. Also,
I've added wal_level = minimal per
src/test/recovery/t/039_end_of_wal.pl introduced by commit bae868caf22
which also tries to keep WAL activity to minimum.
> > Can the WAL summarizer ever read the WAL on current TLI? I'm not so
> > sure about it, I haven't explored it in detail.
>
> Let's just not call XLogReadFromBuffers from there.
Removed.
PSA v21 patch set.
--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
Attachments:
[application/octet-stream] v21-0001-Add-XLogReadFromBuffers.patch (5.9K, ../CALj2ACW4oZzmg6joL0ppQN=cCGOLaVKJknc3OLqmo8Q+cGDRKw@mail.gmail.com/2-v21-0001-Add-XLogReadFromBuffers.patch)
download | inline diff:
From 95ba60dd3afdc134329f3b017588264103f85985 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <[email protected]>
Date: Sat, 27 Jan 2024 05:48:30 +0000
Subject: [PATCH v21] Add XLogReadFromBuffers().
Allows reading directly from WAL buffers without a lock, avoiding the
need to wait for WAL flushing and read from the filesystem.
For now, the only caller is physical replication, but we can consider
expanding it to other callers as needed.
---
src/backend/access/transam/xlog.c | 106 ++++++++++++++++++++++++
src/backend/access/transam/xlogreader.c | 3 -
src/backend/replication/walsender.c | 8 ++
src/include/access/xlog.h | 3 +
4 files changed, 117 insertions(+), 3 deletions(-)
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 478377c4a2..eea50bea3c 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -1705,6 +1705,112 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli)
return cachedPos + ptr % XLOG_BLCKSZ;
}
+/*
+ * Read WAL directly from WAL buffers, if available.
+ *
+ * This function reads 'count' bytes of WAL from WAL buffers into 'buf'
+ * starting at location 'startptr' and returns total bytes read.
+ *
+ * The bytes read may be fewer than requested if any of the WAL buffers in the
+ * requested range have been evicted.
+ *
+ * This function returns immediately if the requested data is not from the
+ * current timeline, or if the server is in recovery.
+ */
+Size
+XLogReadFromBuffers(char *buf, XLogRecPtr startptr, Size count, TimeLineID tli)
+{
+ XLogRecPtr ptr = startptr;
+ Size nbytes = count;
+ Size ntotal = 0;
+ char *dst = buf;
+
+ if (RecoveryInProgress() ||
+ tli != GetWALInsertionTimeLine())
+ return ntotal;
+
+ Assert(!XLogRecPtrIsInvalid(startptr));
+
+ /*
+ * Loop through the buffers without a lock. For each buffer, atomically
+ * read and verify the end pointer, then copy the data out, and finally
+ * re-read and re-verify the end pointer.
+ *
+ * Once a page is evicted, it never returns to the WAL buffers, so if the
+ * end pointer matches the expected end pointer before and after we copy
+ * the data, then the right page must have been present during the data
+ * copy. Read barriers are necessary to ensure that the data copy actually
+ * happens between the two verification steps.
+ *
+ * If the verification fails, we simply terminate the loop and return with
+ * the data that had been already copied out successfully.
+ */
+ while (nbytes > 0)
+ {
+ XLogRecPtr expectedEndPtr;
+ XLogRecPtr endptr;
+ int idx;
+ const char *page;
+ const char *data;
+ Size nread;
+
+ idx = XLogRecPtrToBufIdx(ptr);
+ expectedEndPtr = ptr;
+ expectedEndPtr += XLOG_BLCKSZ - ptr % XLOG_BLCKSZ;
+
+ /*
+ * First verification step: check that the correct page is present in
+ * the WAL buffers.
+ */
+ endptr = pg_atomic_read_u64(&XLogCtl->xlblocks[idx]);
+ if (expectedEndPtr != endptr)
+ break;
+
+ /*
+ * We found WAL buffer page containing given XLogRecPtr. Get starting
+ * address of the page and a pointer to the right location of given
+ * XLogRecPtr in that page.
+ */
+ page = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ;
+ data = page + ptr % XLOG_BLCKSZ;
+
+ /*
+ * Ensure that the data copy and the first verification step are not
+ * reordered.
+ */
+ pg_read_barrier();
+
+ /* how much is available on this page to read? */
+ nread = Min(nbytes, XLOG_BLCKSZ - (data - page));
+
+ /* data copy */
+ memcpy(dst, data, nread);
+
+ /*
+ * Ensure that the data copy and the second verification step are not
+ * reordered.
+ */
+ pg_read_barrier();
+
+ /*
+ * Second verification step: check that the page we read from wasn't
+ * evicted while we were copying the data.
+ */
+ endptr = pg_atomic_read_u64(&XLogCtl->xlblocks[idx]);
+ if (expectedEndPtr != endptr)
+ break;
+
+ dst += nread;
+ ptr += nread;
+ ntotal += nread;
+ nbytes -= nread;
+ }
+
+ Assert(ntotal <= count);
+
+ return ntotal;
+}
+
/*
* Converts a "usable byte position" to XLogRecPtr. A usable byte position
* is the position starting from the beginning of WAL, excluding all WAL
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 7190156f2f..74a6b11866 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1500,9 +1500,6 @@ err:
*
* Returns true if succeeded, false if an error occurs, in which case
* 'errinfo' receives error details.
- *
- * XXX probably this should be improved to suck data directly from the
- * WAL buffers when possible.
*/
bool
WALRead(XLogReaderState *state,
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index aa80f3de20..7efe9ad010 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -2910,6 +2910,7 @@ XLogSendPhysical(void)
Size nbytes;
XLogSegNo segno;
WALReadError errinfo;
+ Size rbytes;
/* If requested switch the WAL sender to the stopping state. */
if (got_STOPPING)
@@ -3125,6 +3126,13 @@ XLogSendPhysical(void)
enlargeStringInfo(&output_message, nbytes);
retry:
+ /* Read from WAL buffers, if available. */
+ rbytes = XLogReadFromBuffers(&output_message.data[output_message.len],
+ startptr, nbytes, xlogreader->seg.ws_tli);
+ output_message.len += rbytes;
+ startptr += rbytes;
+ nbytes -= rbytes;
+
if (!WALRead(xlogreader,
&output_message.data[output_message.len],
startptr,
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 301c5fa11f..f8c281c799 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -252,6 +252,9 @@ extern XLogRecPtr GetLastImportantRecPtr(void);
extern void SetWalWriterSleeping(bool sleeping);
+extern Size XLogReadFromBuffers(char *buf, XLogRecPtr startptr, Size count,
+ TimeLineID tli);
+
/*
* Routines used by xlogrecovery.c to call back into xlog.c during recovery.
*/
--
2.34.1
[application/octet-stream] v21-0002-Allow-XLogReadFromBuffers-to-wait-for-in-progres.patch (5.1K, ../CALj2ACW4oZzmg6joL0ppQN=cCGOLaVKJknc3OLqmo8Q+cGDRKw@mail.gmail.com/3-v21-0002-Allow-XLogReadFromBuffers-to-wait-for-in-progres.patch)
download | inline diff:
From 1e9dcbfb18c26a8e0fbbf90bac6e7890afcf012d Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <[email protected]>
Date: Sat, 27 Jan 2024 06:23:07 +0000
Subject: [PATCH v21] Allow XLogReadFromBuffers to wait for in-progress
insertions
---
src/backend/access/transam/xlog.c | 43 ++++++++++++++++++++++++-------
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index eea50bea3c..20fc2c6036 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -698,7 +698,7 @@ static void ReserveXLogInsertLocation(int size, XLogRecPtr *StartPos,
XLogRecPtr *EndPos, XLogRecPtr *PrevPtr);
static bool ReserveXLogSwitch(XLogRecPtr *StartPos, XLogRecPtr *EndPos,
XLogRecPtr *PrevPtr);
-static XLogRecPtr WaitXLogInsertionsToFinish(XLogRecPtr upto);
+static XLogRecPtr WaitXLogInsertionsToFinish(XLogRecPtr upto, bool emitLog);
static char *GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli);
static XLogRecPtr XLogBytePosToRecPtr(uint64 bytepos);
static XLogRecPtr XLogBytePosToEndRecPtr(uint64 bytepos);
@@ -1494,7 +1494,7 @@ WALInsertLockUpdateInsertingAt(XLogRecPtr insertingAt)
* to make room for a new one, which in turn requires WALWriteLock.
*/
static XLogRecPtr
-WaitXLogInsertionsToFinish(XLogRecPtr upto)
+WaitXLogInsertionsToFinish(XLogRecPtr upto, bool emitLog)
{
uint64 bytepos;
XLogRecPtr reservedUpto;
@@ -1521,9 +1521,10 @@ WaitXLogInsertionsToFinish(XLogRecPtr upto)
*/
if (upto > reservedUpto)
{
- ereport(LOG,
- (errmsg("request to flush past end of generated WAL; request %X/%X, current position %X/%X",
- LSN_FORMAT_ARGS(upto), LSN_FORMAT_ARGS(reservedUpto))));
+ if (emitLog)
+ ereport(LOG,
+ (errmsg("request to flush past end of generated WAL; request %X/%X, current position %X/%X",
+ LSN_FORMAT_ARGS(upto), LSN_FORMAT_ARGS(reservedUpto))));
upto = reservedUpto;
}
@@ -1712,7 +1713,11 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli)
* starting at location 'startptr' and returns total bytes read.
*
* The bytes read may be fewer than requested if any of the WAL buffers in the
- * requested range have been evicted.
+ * requested range have been evicted, or if the last requested byte is beyond
+ * the current insert position.
+ *
+ * If reading beyond the current write position, this function will wait for
+ * concurrent inserters to finish. Otherwise, it does not wait at all.
*
* This function returns immediately if the requested data is not from the
* current timeline, or if the server is in recovery.
@@ -1724,6 +1729,7 @@ XLogReadFromBuffers(char *buf, XLogRecPtr startptr, Size count, TimeLineID tli)
Size nbytes = count;
Size ntotal = 0;
char *dst = buf;
+ XLogRecPtr upto = startptr + count;
if (RecoveryInProgress() ||
tli != GetWALInsertionTimeLine())
@@ -1731,6 +1737,23 @@ XLogReadFromBuffers(char *buf, XLogRecPtr startptr, Size count, TimeLineID tli)
Assert(!XLogRecPtrIsInvalid(startptr));
+ /*
+ * Caller requested very recent WAL data. Wait for any in-progress WAL
+ * insertions to WAL buffers to finish.
+ *
+ * Most callers will have already updated LogwrtResult when determining
+ * how far to read, but it's OK if it's out of date. XXX: is it worth
+ * taking a spinlock to update LogwrtResult and check again before calling
+ * WaitXLogInsertionsToFinish()?
+ */
+ if (upto > LogwrtResult.Write)
+ {
+ XLogRecPtr writtenUpto = WaitXLogInsertionsToFinish(upto, false);
+
+ upto = Min(upto, writtenUpto);
+ nbytes = upto - startptr;
+ }
+
/*
* Loop through the buffers without a lock. For each buffer, atomically
* read and verify the end pointer, then copy the data out, and finally
@@ -2001,7 +2024,7 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic)
*/
LWLockRelease(WALBufMappingLock);
- WaitXLogInsertionsToFinish(OldPageRqstPtr);
+ WaitXLogInsertionsToFinish(OldPageRqstPtr, true);
LWLockAcquire(WALWriteLock, LW_EXCLUSIVE);
@@ -2795,7 +2818,7 @@ XLogFlush(XLogRecPtr record)
* Before actually performing the write, wait for all in-flight
* insertions to the pages we're about to write to finish.
*/
- insertpos = WaitXLogInsertionsToFinish(WriteRqstPtr);
+ insertpos = WaitXLogInsertionsToFinish(WriteRqstPtr, true);
/*
* Try to get the write lock. If we can't get it immediately, wait
@@ -2846,7 +2869,7 @@ XLogFlush(XLogRecPtr record)
* We're only calling it again to allow insertpos to be moved
* further forward, not to actually wait for anyone.
*/
- insertpos = WaitXLogInsertionsToFinish(insertpos);
+ insertpos = WaitXLogInsertionsToFinish(insertpos, true);
}
/* try to write/flush later additions to XLOG as well */
@@ -3025,7 +3048,7 @@ XLogBackgroundFlush(void)
START_CRIT_SECTION();
/* now wait for any in-progress insertions to finish and get write lock */
- WaitXLogInsertionsToFinish(WriteRqst.Write);
+ WaitXLogInsertionsToFinish(WriteRqst.Write, true);
LWLockAcquire(WALWriteLock, LW_EXCLUSIVE);
LogwrtResult = XLogCtl->LogwrtResult;
if (WriteRqst.Write > LogwrtResult.Write ||
--
2.34.1
[application/octet-stream] v21-0003-Add-test-module-for-verifying-WAL-read-from-WAL-.patch (9.8K, ../CALj2ACW4oZzmg6joL0ppQN=cCGOLaVKJknc3OLqmo8Q+cGDRKw@mail.gmail.com/4-v21-0003-Add-test-module-for-verifying-WAL-read-from-WAL-.patch)
download | inline diff:
From e78206889c4ffd5a52033b4e35814bdc74560f7b Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <[email protected]>
Date: Sat, 27 Jan 2024 07:03:48 +0000
Subject: [PATCH v21] Add test module for verifying WAL read from WAL buffers
This commit adds a test module to verify WAL read from WAL
buffers.
Author: Bharath Rupireddy
Reviewed-by: Dilip Kumar
Discussion: https://www.postgresql.org/message-id/CALj2ACXKKK%3DwbiG5_t6dGao5GoecMwRkhr7GjVBM_jg54%2BNa%3DQ%40mail.gmail.com
---
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
.../modules/read_wal_from_buffers/.gitignore | 4 ++
.../modules/read_wal_from_buffers/Makefile | 23 ++++++
.../modules/read_wal_from_buffers/meson.build | 33 +++++++++
.../read_wal_from_buffers--1.0.sql | 14 ++++
.../read_wal_from_buffers.c | 41 +++++++++++
.../read_wal_from_buffers.control | 4 ++
.../read_wal_from_buffers/t/001_basic.pl | 71 +++++++++++++++++++
9 files changed, 192 insertions(+)
create mode 100644 src/test/modules/read_wal_from_buffers/.gitignore
create mode 100644 src/test/modules/read_wal_from_buffers/Makefile
create mode 100644 src/test/modules/read_wal_from_buffers/meson.build
create mode 100644 src/test/modules/read_wal_from_buffers/read_wal_from_buffers--1.0.sql
create mode 100644 src/test/modules/read_wal_from_buffers/read_wal_from_buffers.c
create mode 100644 src/test/modules/read_wal_from_buffers/read_wal_from_buffers.control
create mode 100644 src/test/modules/read_wal_from_buffers/t/001_basic.pl
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index e32c8925f6..4eba0fa2e2 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -12,6 +12,7 @@ SUBDIRS = \
dummy_seclabel \
libpq_pipeline \
plsample \
+ read_wal_from_buffers \
spgist_name_ops \
test_bloomfilter \
test_copy_callbacks \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 397e0906e6..f0b53eced7 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -32,6 +32,7 @@ subdir('test_resowner')
subdir('test_rls_hooks')
subdir('test_shm_mq')
subdir('test_slru')
+subdir('read_wal_from_buffers')
subdir('unsafe_tests')
subdir('worker_spi')
subdir('xid_wraparound')
diff --git a/src/test/modules/read_wal_from_buffers/.gitignore b/src/test/modules/read_wal_from_buffers/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/src/test/modules/read_wal_from_buffers/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/read_wal_from_buffers/Makefile b/src/test/modules/read_wal_from_buffers/Makefile
new file mode 100644
index 0000000000..9e57a837f9
--- /dev/null
+++ b/src/test/modules/read_wal_from_buffers/Makefile
@@ -0,0 +1,23 @@
+# src/test/modules/read_wal_from_buffers/Makefile
+
+MODULE_big = read_wal_from_buffers
+OBJS = \
+ $(WIN32RES) \
+ read_wal_from_buffers.o
+PGFILEDESC = "read_wal_from_buffers - test module to read WAL from WAL buffers"
+
+EXTENSION = read_wal_from_buffers
+DATA = read_wal_from_buffers--1.0.sql
+
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/read_wal_from_buffers
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/read_wal_from_buffers/meson.build b/src/test/modules/read_wal_from_buffers/meson.build
new file mode 100644
index 0000000000..3fac00d616
--- /dev/null
+++ b/src/test/modules/read_wal_from_buffers/meson.build
@@ -0,0 +1,33 @@
+# Copyright (c) 2024, PostgreSQL Global Development Group
+
+read_wal_from_buffers_sources = files(
+ 'read_wal_from_buffers.c',
+)
+
+if host_system == 'windows'
+ read_wal_from_buffers_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'read_wal_from_buffers',
+ '--FILEDESC', 'read_wal_from_buffers - test module to read WAL from WAL buffers',])
+endif
+
+read_wal_from_buffers = shared_module('read_wal_from_buffers',
+ read_wal_from_buffers_sources,
+ kwargs: pg_test_mod_args,
+)
+test_install_libs += read_wal_from_buffers
+
+test_install_data += files(
+ 'read_wal_from_buffers.control',
+ 'read_wal_from_buffers--1.0.sql',
+)
+
+tests += {
+ 'name': 'read_wal_from_buffers',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'tap': {
+ 'tests': [
+ 't/001_basic.pl',
+ ],
+ },
+}
diff --git a/src/test/modules/read_wal_from_buffers/read_wal_from_buffers--1.0.sql b/src/test/modules/read_wal_from_buffers/read_wal_from_buffers--1.0.sql
new file mode 100644
index 0000000000..82fa097d10
--- /dev/null
+++ b/src/test/modules/read_wal_from_buffers/read_wal_from_buffers--1.0.sql
@@ -0,0 +1,14 @@
+/* src/test/modules/read_wal_from_buffers/read_wal_from_buffers--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION read_wal_from_buffers" to load this file. \quit
+
+--
+-- read_wal_from_buffers()
+--
+-- SQL function to read WAL from WAL buffers. Returns number of bytes read.
+--
+CREATE FUNCTION read_wal_from_buffers(IN lsn pg_lsn, IN bytes_to_read int,
+ bytes_read OUT int)
+AS 'MODULE_PATHNAME', 'read_wal_from_buffers'
+LANGUAGE C STRICT;
diff --git a/src/test/modules/read_wal_from_buffers/read_wal_from_buffers.c b/src/test/modules/read_wal_from_buffers/read_wal_from_buffers.c
new file mode 100644
index 0000000000..da841da3f0
--- /dev/null
+++ b/src/test/modules/read_wal_from_buffers/read_wal_from_buffers.c
@@ -0,0 +1,41 @@
+/*--------------------------------------------------------------------------
+ *
+ * read_wal_from_buffers.c
+ * Test module to read WAL from WAL buffers.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/test/modules/read_wal_from_buffers/read_wal_from_buffers.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xlog.h"
+#include "fmgr.h"
+#include "utils/pg_lsn.h"
+
+PG_MODULE_MAGIC;
+
+/*
+ * SQL function to read WAL from WAL buffers. Returns number of bytes read.
+ */
+PG_FUNCTION_INFO_V1(read_wal_from_buffers);
+Datum
+read_wal_from_buffers(PG_FUNCTION_ARGS)
+{
+ XLogRecPtr startptr = PG_GETARG_LSN(0);
+ int32 bytes_to_read = PG_GETARG_INT32(1);
+ Size bytes_read = 0;
+ char *data = palloc0(bytes_to_read);
+
+ bytes_read = XLogReadFromBuffers(data, startptr,
+ (Size) bytes_to_read,
+ GetWALInsertionTimeLine());
+
+ pfree(data);
+
+ PG_RETURN_INT32(bytes_read);
+}
diff --git a/src/test/modules/read_wal_from_buffers/read_wal_from_buffers.control b/src/test/modules/read_wal_from_buffers/read_wal_from_buffers.control
new file mode 100644
index 0000000000..b14d24751c
--- /dev/null
+++ b/src/test/modules/read_wal_from_buffers/read_wal_from_buffers.control
@@ -0,0 +1,4 @@
+comment = 'Test module to read WAL from WAL buffers'
+default_version = '1.0'
+module_pathname = '$libdir/read_wal_from_buffers'
+relocatable = true
diff --git a/src/test/modules/read_wal_from_buffers/t/001_basic.pl b/src/test/modules/read_wal_from_buffers/t/001_basic.pl
new file mode 100644
index 0000000000..62ea21e541
--- /dev/null
+++ b/src/test/modules/read_wal_from_buffers/t/001_basic.pl
@@ -0,0 +1,71 @@
+# Copyright (c) 2021-2023, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Setup a new node. The configuration chosen here minimizes the number
+# of arbitrary records that could get generated in a cluster. Enlarging
+# checkpoint_timeout avoids noise with checkpoint activity. wal_level
+# set to "minimal" avoids random standby snapshot records. Autovacuum
+# could also trigger randomly, generating random WAL activity of its own.
+# Enlarging wal_writer_delay and wal_writer_flush_after avoid background
+# wal flush by walwriter.
+my $node = PostgreSQL::Test::Cluster->new("node");
+$node->init;
+$node->append_conf(
+ 'postgresql.conf',
+ q[wal_level = minimal
+ autovacuum = off
+ checkpoint_timeout = '30min'
+ wal_writer_delay = 10000ms
+ wal_writer_flush_after = 1GB
+]);
+$node->start;
+
+# Setup.
+$node->safe_psql('postgres', 'CREATE EXTENSION read_wal_from_buffers;');
+
+$node->safe_psql('postgres', 'CREATE TABLE t (c int);');
+
+my $result = 0;
+my $lsn;
+my $to_read;
+
+# Wait until we read from WAL buffers
+for (my $i = 0; $i < 10 * $PostgreSQL::Test::Utils::timeout_default; $i++)
+{
+ # Get current insert LSN. After this, we generate some WAL which is guranteed
+ # to be in WAL buffers as there is no other WAL generating activity is
+ # happening on the server. We then verify if we can read the WAL from WAL
+ # buffers using this LSN.
+ $lsn = $node->safe_psql('postgres', 'SELECT pg_current_wal_insert_lsn();');
+
+ # Generate minimal WAL so that WAL buffers don't get overwritten.
+ $node->safe_psql('postgres', "INSERT INTO t VALUES ($i);");
+
+ $to_read = 8192;
+
+ if ($node->safe_psql('postgres',
+ qq{SELECT read_wal_from_buffers(lsn := '$lsn', bytes_to_read := $to_read) > 0;}))
+ {
+ $result = 1;
+ last;
+ }
+
+ usleep(100_000);
+}
+ok($result, 'waited until WAL is successfully read from WAL buffers');
+
+# Check with a WAL that doesn't yet exist i.e., 16MB starting from current
+# flush LSN.
+$lsn = $node->safe_psql('postgres', 'SELECT pg_current_wal_flush_lsn()+16777216;');
+$to_read = 8192;
+$result = $node->safe_psql('postgres',
+ qq{SELECT read_wal_from_buffers(lsn := '$lsn', bytes_to_read := $to_read) = 0;});
+is($result, 't', "WAL that doesn't yet exist is not read from WAL buffers");
+
+done_testing();
--
2.34.1
[application/octet-stream] v21-0004-Use-XLogReadFromBuffers-in-more-places.patch (2.7K, ../CALj2ACW4oZzmg6joL0ppQN=cCGOLaVKJknc3OLqmo8Q+cGDRKw@mail.gmail.com/5-v21-0004-Use-XLogReadFromBuffers-in-more-places.patch)
download | inline diff:
From 0a3f3ea0f849e0bef731efe4c66ed17745b44c8d Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <[email protected]>
Date: Sat, 27 Jan 2024 07:04:51 +0000
Subject: [PATCH v21] Use XLogReadFromBuffers in more places
---
src/backend/access/transam/xlogutils.c | 12 +++++++++++-
src/backend/replication/walsender.c | 12 +++++++++++-
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index aa8667abd1..de526f7da7 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -894,6 +894,8 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
int count;
WALReadError errinfo;
TimeLineID currTLI;
+ Size nbytes;
+ Size rbytes;
loc = targetPagePtr + reqLen;
@@ -1006,12 +1008,20 @@ read_local_xlog_page_guts(XLogReaderState *state, XLogRecPtr targetPagePtr,
count = read_upto - targetPagePtr;
}
+ /* Read from WAL buffers, if available. */
+ nbytes = XLOG_BLCKSZ;
+ rbytes = XLogReadFromBuffers(cur_page, targetPagePtr,
+ nbytes, currTLI);
+ cur_page += rbytes;
+ targetPagePtr += rbytes;
+ nbytes -= rbytes;
+
/*
* Even though we just determined how much of the page can be validly read
* as 'count', read the whole page anyway. It's guaranteed to be
* zero-padded up to the page boundary if it's incomplete.
*/
- if (!WALRead(state, cur_page, targetPagePtr, XLOG_BLCKSZ, tli,
+ if (!WALRead(state, cur_page, targetPagePtr, nbytes, tli,
&errinfo))
WALReadRaiseError(&errinfo);
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 7efe9ad010..4bc8d5e320 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1059,6 +1059,8 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req
WALReadError errinfo;
XLogSegNo segno;
TimeLineID currTLI;
+ Size nbytes;
+ Size rbytes;
/*
* Make sure we have enough WAL available before retrieving the current
@@ -1095,11 +1097,19 @@ logical_read_xlog_page(XLogReaderState *state, XLogRecPtr targetPagePtr, int req
else
count = flushptr - targetPagePtr; /* part of the page available */
+ /* Read from WAL buffers, if available. */
+ nbytes = XLOG_BLCKSZ;
+ rbytes = XLogReadFromBuffers(cur_page, targetPagePtr,
+ nbytes, currTLI);
+ cur_page += rbytes;
+ targetPagePtr += rbytes;
+ nbytes -= rbytes;
+
/* now actually read the data, we know it's there */
if (!WALRead(state,
cur_page,
targetPagePtr,
- XLOG_BLCKSZ,
+ nbytes,
currTLI, /* Pass the current TLI because only
* WalSndSegmentOpen controls whether new TLI
* is needed. */
--
2.34.1
view thread (22+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: Improve WALRead() to suck data directly from WAL buffers when possible
In-Reply-To: <CALj2ACW4oZzmg6joL0ppQN=cCGOLaVKJknc3OLqmo8Q+cGDRKw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox