pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
Map WAL segment files on PMEM as WAL buffers
22+ messages / 7 participants
[nested] [flat]

* Map WAL segment files on PMEM as WAL buffers
@ 2021-05-19 01:25  Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2021-05-19 01:25 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>

Hi hackers,

In response to PMEM-related discussions in the previous thread [1],
especially Tomas' performance report [2], I have worked for the way
that maps WAL segment files on PMEM as WAL buffers. I start this new
thread to go that way since the previous one has focused on another
patchset that I called "Non-volatile WAL buffer."

The patchset using WAL segment files is attached to this mail. Note
that it is tested on 8e4b332 (Mar 22, 2021) and cannot be applied to
the latest master. Also note that it has a known issue related to
checkpoint request (see Section 1.4 in the attached PDF for details).
I'm rebasing and fixing it, so please be patient for an update.

This mail also has a performance report PDF comparing PMEM patchsets
including ones that I have posted to pgsql-hackers ever, and such
zipped and rebased patchsets for reproducibility. The report covers
how to build and configure PostgreSQL with the patchsets, so please
see it before you use them.

Regards,
Takashi

[1] https://www.postgresql.org/message-id/flat/002f01d5d28d%2423c01430%246b403c90%24%40hco.ntt.co.jp_1
[2] https://www.postgresql.org/message-id/9beaac79-2375-8bfc-489b-eb62bd8d4020@enterprisedb.com

-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (7 files, .tar.gz)

0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 5.3K) download From 0422205e4e7f5507f2cb62e6042aacfcf62c83c7 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH 1/4] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index 06ad9aeb71..0bf0952e04 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -8713,6 +8716,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13246,6 +13284,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -13957,6 +14045,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index 92193f35fb..cc00f2c7a6 100644 --- a/configure.ac +++ b/configure.ac @@ -1011,6 +1011,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1341,6 +1350,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1527,6 +1540,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 5e2255a2f5..6194bd7fc6 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -328,6 +328,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -898,6 +901,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.25.1
0002-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 4.6K) download From b3713e6cb1d65fa46d01b537327ee110228a95a0 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH 2/4] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6f8810e149..ae4fd5c35f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -111,6 +111,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5088,13 +5089,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5110,10 +5126,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5207,13 +5230,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 3b36a31a47..c683c1ea41 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1311,6 +1311,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 77187c12be..b402e12ba8 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -132,6 +132,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
0003-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From 7be7427de5d2fced9ff821183d9dd5980c1b6e1a Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH 3/4] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 5 +---- src/include/access/xlog_internal.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index ae4fd5c35f..879a835d37 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -915,9 +915,6 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void); static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno, - bool use_lock); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3627,7 +3624,7 @@ XLogFileCopy(XLogSegNo destsegno, TimeLineID srcTLI, XLogSegNo srcsegno, * max_segno limit was exceeded, or an error occurred while renaming the * file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, bool use_lock) diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index b23e286406..060f5a7a26 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -315,6 +315,10 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno, + bool use_lock); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
0004-Map-WAL-segment-files-as-WAL-buffers.patch (application/octet-stream, 16.8K) download From 82c2d5096d8dac4e06970110fca9576e954f36ab Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH 4/4] Map WAL segment files as WAL buffers --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 145 ++++++++++--- src/backend/access/transam/xlogpmem.c | 295 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 65 ++++++ 4 files changed, 471 insertions(+), 35 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 879a835d37..a7cac96f66 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -1988,7 +1989,13 @@ GetXLogBuffer(XLogRecPtr ptr) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + PmemXLogEnsurePrevMapped(endptr); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2222,7 +2229,13 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + PmemXLogEnsurePrevMapped(NewPageEndPtr); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2430,6 +2443,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2452,6 +2467,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2477,32 +2496,35 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); - /* create/use new log file */ - use_existent = true; - openLogFile = XLogFileInit(openLogSegNo, &use_existent, true); - ReserveExternalFD(); - } + /* create/use new log file */ + use_existent = true; + openLogFile = XLogFileInit(openLogSegNo, &use_existent, true); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogFile = XLogFileOpen(openLogSegNo); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogFile = XLogFileOpen(openLogSegNo); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2510,8 +2532,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2549,7 +2571,37 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + PmemXLogEnsurePrevMapped(endbyteptr); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2607,7 +2659,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2658,12 +2713,14 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -7853,11 +7910,29 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + */ + PmemXLogEnsurePrevMapped(EndOfLog); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..a98a73d489 --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,295 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static void PmemXLogMap(XLogSegNo segno); +static void PmemXLogCreate(XLogSegNo segno); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + */ +void +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + PmemXLogMap(segno); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns true if an existent segment is used, false if a new one is created. + * Does not return if error. + * + * See also XLogFileInit in xlog.c. We assume use_existent=true and + * use_lock=true. + */ +static void +PmemXLogMap(XLogSegNo segno) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, ThisTimeLineID, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. We assume use_existent=true and + * use_lock=true. + */ +static void +PmemXLogCreate(XLogSegNo segno) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (wal_init_zero) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno, true)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file. + */ +static void +PmemXLogUnmap(void) +{ + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + /* TODO Update WalStats */ + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..d5e377351c --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,65 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +/* XXX xlog_internal.h should have this */ +static inline Size +XLogPageOffset(XLogRecPtr ptr) +{ + return (Size) (ptr & ((XLogRecPtr) XLOG_BLCKSZ - 1)); +} + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern void PmemXLogEnsurePrevMapped(XLogRecPtr ptr); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline void +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + abort(); +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
Performance-of-PMEM-patchsets-20210519.pdf (application/pdf, 1.4M) download Applying-PMDK-to-WAL-operations-for-persistent-memory-20210322.zip (application/x-zip-compressed, 16.2K) download Non-volatile-WAL-buffer-20210322.zip (application/x-zip-compressed, 37.7K) download ^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-06-18 06:44  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2021-06-18 06:44 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>

Hi hackers,

The v2 patchset, an updated performance report, and a zipped
supplemental patchset for comparison in the report. I confirmed that
the v2 can be applied to the latest master d24c565 (Jun 17 2021)
without conflict, but the supplemental patchset cannot. If you want to
reproduce the comparison, please apply to eb43bdb (May 25, 2021) as I
did so in the report.

The v2 includes WAL statistics support and WAL pre-allocation feature
in cases of PMEM, and some fixes for the first version. The size of
WAL buffers managed by xlblocks becomes min_wal_size, and the buffers
and underlying segment files are initialized at startup then
periodically by walwriter. This looks to improve performance, as I
wrote in the report.

By the way, I found that Nathan-san posted a PoC patch for WAL
pre-allocation in another thread [1]. I will pay attention to it and
discussions related to WAL pre-allocation in pgsql-hackers.

Best regards,
Takashi

[1] https://www.postgresql.org/message-id/flat/20201225200953.jjkrytlrzojbndh5%40alap3.anarazel.de

-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (8 files, .tar.gz)

v2-0005-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From 0673114a001b0c40e42eec3d098179f56c87a229 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v2 5/6] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index a3cb844304..6969e7c115 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -180,6 +180,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -188,22 +189,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v2-0004-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 17.0K) download From e92ece14797ea8f9272e4004ff4653b836f11432 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v2 4/6] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 145 ++++++++++--- src/backend/access/transam/xlogpmem.c | 299 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 469 insertions(+), 35 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 6fdc2d38b8..c845f0a0fc 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -1988,7 +1989,13 @@ GetXLogBuffer(XLogRecPtr ptr) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(endptr); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2222,7 +2229,13 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2430,6 +2443,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2452,6 +2467,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2477,32 +2496,35 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); - /* create/use new log file */ - use_existent = true; - openLogFile = XLogFileInit(openLogSegNo, &use_existent, true); - ReserveExternalFD(); - } + /* create/use new log file */ + use_existent = true; + openLogFile = XLogFileInit(openLogSegNo, &use_existent, true); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogFile = XLogFileOpen(openLogSegNo); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogFile = XLogFileOpen(openLogSegNo); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2510,8 +2532,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2549,7 +2571,37 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2607,7 +2659,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2658,12 +2713,14 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -7851,11 +7908,29 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + */ + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..a3cb844304 --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,299 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno); +static void PmemXLogCreate(XLogSegNo segno); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. We assume use_existent=true and + * use_lock=true. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, ThisTimeLineID, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. We assume use_existent=true and + * use_lock=true. + */ +static void +PmemXLogCreate(XLogSegNo segno) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno, true)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..62c9e1f5e2 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v2-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 5.3K) download From efab827fbd10eff06101a0ebc3ed24b4c2ff578a Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v2 1/6] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index e9b98f442f..1b3928e383 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -8713,6 +8716,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13246,6 +13284,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -13957,6 +14045,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index 3b42d8bdc9..9d8efa22ec 100644 --- a/configure.ac +++ b/configure.ac @@ -1011,6 +1011,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1341,6 +1350,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1527,6 +1540,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 783b8fc1ba..8fe0472225 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -334,6 +334,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -904,6 +907,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.25.1
v2-0002-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 4.7K) download From 7e9d1a6a369b191092d46cdfb2e1f2540b5460e1 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v2 2/6] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 441a9124cd..595ac3e26f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -111,6 +111,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5088,13 +5089,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5110,10 +5126,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5207,13 +5230,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ee731044b6..9c94069f34 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1334,6 +1334,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 77187c12be..b402e12ba8 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -132,6 +132,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v2-0003-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From 111de332543287fed7d76e4ff3429747c2202034 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v2 3/6] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 5 +---- src/include/access/xlog_internal.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 595ac3e26f..6fdc2d38b8 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -915,9 +915,6 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void); static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno, - bool use_lock); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3627,7 +3624,7 @@ XLogFileCopy(XLogSegNo destsegno, TimeLineID srcTLI, XLogSegNo srcsegno, * max_segno limit was exceeded, or an error occurred while renaming the * file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, bool use_lock) diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 26a743b6b6..6ff88ca252 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -315,6 +315,10 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno, + bool use_lock); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v2-0006-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 3.6K) download From eedbc6982aa5a907b6ede5e97abaac2310200dbc Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v2 6/6] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index c845f0a0fc..8d83dd8480 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1992,7 +1992,9 @@ GetXLogBuffer(XLogRecPtr ptr) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(endptr); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2232,7 +2234,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5146,15 +5150,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -7915,7 +7929,8 @@ StartupXLOG(void) * Note that "len" indicates the size of the valid part. */ openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8101,7 +8116,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, true); + else + PreallocXlogFiles(EndOfLog); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 9c94069f34..df15ec8944 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2830,7 +2830,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
Performance-of-PMEM-patchsets-20210618.pdf (application/pdf, 1.7M) download Non-volatile-WAL-buffer-20210525.zip (application/x-zip-compressed, 37.5K) download ^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-06-30 04:52  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2021-06-30 04:52 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>

Rebased.

-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (6 files, .tar.gz)

v3-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 5.3K) download From 4b286fd6f3b63fa2cb3ba47110ef8e80e6c20d57 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v3 1/6] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index e468def49e..8274162111 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -8713,6 +8716,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13246,6 +13284,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -13957,6 +14045,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index 39666f9727..29a122cc3c 100644 --- a/configure.ac +++ b/configure.ac @@ -1011,6 +1011,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1341,6 +1350,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1527,6 +1540,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 783b8fc1ba..8fe0472225 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -334,6 +334,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -904,6 +907,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.25.1
v3-0002-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 4.7K) download From 39ba8aa70781d82de054714a81b10dab2ebfba19 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v3 2/6] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 9cbca6392d..bce4489eee 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -111,6 +111,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5113,13 +5114,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5135,10 +5151,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5232,13 +5255,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 480e8cd199..063bc22860 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1340,6 +1340,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index ccfcf43d62..8fece28eea 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -132,6 +132,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v3-0003-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From 90b9701f73da0b39d447f1cafdc7b104cbedb5a8 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v3 3/6] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 4 +--- src/include/access/xlog_internal.h | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index bce4489eee..ae5875a404 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -923,8 +923,6 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void); static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3640,7 +3638,7 @@ XLogFileCopy(XLogSegNo destsegno, TimeLineID srcTLI, XLogSegNo srcsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 3b5eceff65..9be4954c13 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -315,6 +315,9 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v3-0004-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 16.8K) download From bf8c3b22c3c0d609f42e9e934bbfbe31a0b2d424 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v3 4/6] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 143 ++++++++++--- src/backend/access/transam/xlogpmem.c | 297 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 466 insertions(+), 34 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index ae5875a404..a978ff0bf1 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -1997,7 +1998,13 @@ GetXLogBuffer(XLogRecPtr ptr) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(endptr); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2231,7 +2238,13 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2438,6 +2451,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2460,6 +2475,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2485,31 +2504,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogFile = XLogFileOpen(openLogSegNo); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogFile = XLogFileOpen(openLogSegNo); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2517,8 +2539,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2556,7 +2578,37 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2614,7 +2666,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2665,12 +2720,14 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -7888,11 +7945,29 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + */ + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..74d49eac92 --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,297 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno); +static void PmemXLogCreate(XLogSegNo segno); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, ThisTimeLineID, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..62c9e1f5e2 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v3-0005-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From e3a124920da3e4c5463c8e3fc0525680f9c06c7e Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v3 5/6] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 74d49eac92..a9c435c0e3 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -178,6 +178,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -186,22 +187,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v3-0006-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 3.6K) download From 1ed0b80b87fb3aff7a134df2d1246885ea358b73 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v3 6/6] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a978ff0bf1..ae99c0a315 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2001,7 +2001,9 @@ GetXLogBuffer(XLogRecPtr ptr) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(endptr); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2241,7 +2243,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5172,15 +5176,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -7952,7 +7966,8 @@ StartupXLOG(void) * Note that "len" indicates the size of the valid part. */ openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8138,7 +8153,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, true); + else + PreallocXlogFiles(EndOfLog); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 063bc22860..76853d1721 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2837,7 +2837,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-10-07 22:46  Matthias van de Meent <boekewurm+postgres@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Matthias van de Meent @ 2021-10-07 22:46 UTC (permalink / raw)
  To: Takashi Menjo <takashi.menjo@gmail.com>; +Cc: pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>

On Wed, 30 Jun 2021 at 06:53, Takashi Menjo <takashi.menjo@gmail.com> wrote:
>
> Rebased.

Thanks for these patches!

I recently took a dive into the WAL subsystem, and got to this
patchset through the previous ones linked from [0]. This patchset
seems straightforward to understand, so thanks!

I've looked over the patches and added some comments below. I haven't
yet tested this though; finding out how to get PMEM on WSL without
actual PMEM is probably going to be difficult.

> [ v3-0002-Add-wal_pmem_map-to-GUC.patch ]
> +extern bool wal_pmem_map;

A lot of the new code in these patches is gated behind this one flag,
but the flag should never be true on !pmem systems. Could you instead
replace it with something like the following?

+#ifdef USE_LIBPMEM
+extern bool wal_pmem_map;
+#else
+#define wal_pmem_map false
+#endif

A good compiler would then eliminate all the dead code from being
generated on non-pmem builds (instead of the compiler needing to keep
that code around just in case some extension decides to set
wal_pmem_map to true on !pmem systems because it has access to that
variable).

> [ v3-0004-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch ]
> +    if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK)
> +        elog(WARNING,
> +             "file not mapped on DAX hugepage boundary: path \"%s\" addr %p",
> +             path, addr);

I'm not sure that we should want to log this every time we detect the
issue; It's likely that once it happens it will happen for the next
file as well. Maybe add a timeout, or do we generally not deduplicate
such messages?


Kind regards,

Matthias

[0] https://wiki.postgresql.org/wiki/Persistent_Memory_for_WAL#Basic_performance





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-10-08 08:07  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Matthias van de Meent <boekewurm+postgres@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2021-10-08 08:07 UTC (permalink / raw)
  To: Matthias van de Meent <boekewurm+postgres@gmail.com>; +Cc: pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>

Hello Matthias,

Thank you for your comment!

> > [ v3-0002-Add-wal_pmem_map-to-GUC.patch ]
> > +extern bool wal_pmem_map;
>
> A lot of the new code in these patches is gated behind this one flag,
> but the flag should never be true on !pmem systems. Could you instead
> replace it with something like the following?
>
> +#ifdef USE_LIBPMEM
> +extern bool wal_pmem_map;
> +#else
> +#define wal_pmem_map false
> +#endif
>
> A good compiler would then eliminate all the dead code from being
> generated on non-pmem builds (instead of the compiler needing to keep
> that code around just in case some extension decides to set
> wal_pmem_map to true on !pmem systems because it has access to that
> variable).

That sounds good. I will introduce it in the next update.

> > [ v3-0004-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch ]
> > +    if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK)
> > +        elog(WARNING,
> > +             "file not mapped on DAX hugepage boundary: path \"%s\" addr %p",
> > +             path, addr);
>
> I'm not sure that we should want to log this every time we detect the
> issue; It's likely that once it happens it will happen for the next
> file as well. Maybe add a timeout, or do we generally not deduplicate
> such messages?

Let me give it some thought.  I have believed this WARNING is most
unlikely to happen, and is mutually independent from other happenings.
I will try to find a case where the WARNING happens repeatedly; or I
will de-duplicate the messages if it is easier.

Best regards,
Takashi

-- 
Takashi Menjo <takashi.menjo@gmail.com>





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-10-28 06:09  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2021-10-28 06:09 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>

Hi,

Rebased, and added the patches below into the patchset.

- (0006) Let wal_pmem_map be constant unless --with-libpmem
wal_pmem_map never changes from false in that case, so let it be
constant.  Thanks, Matthias!

- (0007) Ensure WAL mappings before assertion
This fixes SIGSEGV abortion in GetXLogBuffer when --enable-cassert.

- (0008) Update document
This adds a new entry for wal_pmem_map in the section Write Ahead Log
-> Settings.

Best regards,
Takashi

On Fri, Oct 8, 2021 at 5:07 PM Takashi Menjo <takashi.menjo@gmail.com> wrote:
>
> Hello Matthias,
>
> Thank you for your comment!
>
> > > [ v3-0002-Add-wal_pmem_map-to-GUC.patch ]
> > > +extern bool wal_pmem_map;
> >
> > A lot of the new code in these patches is gated behind this one flag,
> > but the flag should never be true on !pmem systems. Could you instead
> > replace it with something like the following?
> >
> > +#ifdef USE_LIBPMEM
> > +extern bool wal_pmem_map;
> > +#else
> > +#define wal_pmem_map false
> > +#endif
> >
> > A good compiler would then eliminate all the dead code from being
> > generated on non-pmem builds (instead of the compiler needing to keep
> > that code around just in case some extension decides to set
> > wal_pmem_map to true on !pmem systems because it has access to that
> > variable).
>
> That sounds good. I will introduce it in the next update.
>
> > > [ v3-0004-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch ]
> > > +    if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK)
> > > +        elog(WARNING,
> > > +             "file not mapped on DAX hugepage boundary: path \"%s\" addr %p",
> > > +             path, addr);
> >
> > I'm not sure that we should want to log this every time we detect the
> > issue; It's likely that once it happens it will happen for the next
> > file as well. Maybe add a timeout, or do we generally not deduplicate
> > such messages?
>
> Let me give it some thought.  I have believed this WARNING is most
> unlikely to happen, and is mutually independent from other happenings.
> I will try to find a case where the WARNING happens repeatedly; or I
> will de-duplicate the messages if it is easier.
>
> Best regards,
> Takashi
>
> --
> Takashi Menjo <takashi.menjo@gmail.com>



-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (9 files, .tar.gz)

v4-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 5.3K) download From 9782390c679647e2f08c195868514fc8ad7d2a49 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v4 1/9] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index 4ffefe4655..d7c3dc239d 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -8811,6 +8814,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13282,6 +13320,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -13993,6 +14081,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index 44ee3ebe2f..8f117ccccf 100644 --- a/configure.ac +++ b/configure.ac @@ -1020,6 +1020,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1349,6 +1358,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1535,6 +1548,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 15ffdd895a..0ecc132c81 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -343,6 +343,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -910,6 +913,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.25.1
v4-0002-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 4.6K) download From 031ba7fa3fa48306efd4924309c23646b9042840 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v4 2/9] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f547efd294..8a530f88c0 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -112,6 +112,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5131,13 +5132,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5153,10 +5169,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5250,13 +5273,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index e91d5a3cfd..05c9d260d8 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1343,6 +1343,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 5e2c94a05f..9cc2e1f458 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -90,6 +90,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v4-0003-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From ee77915d0595375a3a2ee982e653b8662ca88409 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v4 3/9] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 4 +--- src/include/access/xlog_internal.h | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 8a530f88c0..bc07131e99 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -917,8 +917,6 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void); static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3647,7 +3645,7 @@ XLogFileCopy(XLogSegNo destsegno, TimeLineID srcTLI, XLogSegNo srcsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index c0da76cab4..6e712fdf9d 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -324,6 +324,9 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v4-0005-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From d4140098e5c3492c6ded4440de41bf9c38ee1495 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v4 5/9] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 74d49eac92..a9c435c0e3 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -178,6 +178,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -186,22 +187,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v4-0004-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 16.8K) download From 3c51ef6459228a83f458ef9e48fb4b98e707746d Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v4 4/9] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 143 ++++++++++--- src/backend/access/transam/xlogpmem.c | 297 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 466 insertions(+), 34 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index bc07131e99..0986dbc0a1 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -1992,7 +1993,13 @@ GetXLogBuffer(XLogRecPtr ptr) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(endptr); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2226,7 +2233,13 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2445,6 +2458,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2467,6 +2482,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2492,31 +2511,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogFile = XLogFileOpen(openLogSegNo); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogFile = XLogFileOpen(openLogSegNo); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2524,8 +2546,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2563,7 +2585,37 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2621,7 +2673,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2672,12 +2727,14 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -8033,11 +8090,29 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + */ + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..74d49eac92 --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,297 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno); +static void PmemXLogCreate(XLogSegNo segno); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, ThisTimeLineID, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..62c9e1f5e2 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v4-0006-Let-wal_pmem_map-be-constant-unless-with-libpmem.patch (application/octet-stream, 1.4K) download From 1bb3d1384dcfe2e591eab15f3e774e5f9afa8a44 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 13 Oct 2021 11:10:17 +0900 Subject: [PATCH v4 6/9] Let wal_pmem_map be constant unless --with-libpmem --- src/backend/access/transam/xlog.c | 3 +++ src/include/access/xlog.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 0986dbc0a1..e3e28fb9d3 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -113,7 +113,10 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; + +#ifdef USE_LIBPMEM bool wal_pmem_map = false; +#endif #ifdef WAL_DEBUG bool XLOG_DEBUG = false; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 9cc2e1f458..1b0b75467c 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -90,7 +90,12 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; + +#ifdef USE_LIBPMEM extern bool wal_pmem_map; +#else +#define wal_pmem_map false +#endif /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v4-0007-Ensure-WAL-mappings-before-assertion.patch (application/octet-stream, 1.4K) download From 5e29fdc048f354c3353da40f00ebd7718be43f11 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 28 Oct 2021 13:35:28 +0900 Subject: [PATCH v4 7/9] Ensure WAL mappings before assertion --- src/backend/access/transam/xlog.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index e3e28fb9d3..9753b7b24f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1913,6 +1913,23 @@ GetXLogBuffer(XLogRecPtr ptr) */ if (ptr / XLOG_BLCKSZ == cachedPage) { + /* + * Ensure WAL mappings before assersion. + * + * cachedPos should be recaluculated because it has been probably + * invalidated due to WAL remapping. This should be done even if + * openLogSegNo seems not to change because the address of the + * mapping could have changed (ABA problem). + */ + if (wal_pmem_map) + { + endptr = ptr - ptr % XLOG_BLCKSZ + XLOG_BLCKSZ; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr); + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); + } + Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); return cachedPos + ptr % XLOG_BLCKSZ; -- 2.25.1
v4-0008-Update-document.patch (application/octet-stream, 2.0K) download From 4ef1f6b699057349d3aa6fb9a750a70c147a20c1 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 30 Sep 2021 12:51:56 +0900 Subject: [PATCH v4 8/9] Update document --- doc/src/sgml/config.sgml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index de77f14573..18a1ef4999 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3239,6 +3239,33 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-wal-pmem-map" xreflabel="wal_pmem_map"> + <term><varname>wal_pmem_map</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>wal_pmem_map</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + If set to <literal>on</literal>, this parameter causes WAL files to be + memory-mapped and used as WAL buffer pages. The WAL files in pg_wal + directory (or the directory given by <command>initdb -X</command> + option) should be on <firstterm>persistent memory</firstterm> (PMEM) and + the filesystem for those files should support the <firstterm>Direct + Access</firstterm> (DAX) feature. <varname>wal_sync_method</varname> + for the primary server is ignored and WAL updates are forced out to + PMEM in a more optimal way which avoids calling into the kernel. + <varname>min_wal_size</varname> should be multiple of the size of a + WAL file, and <varname>wal_buffers</varname> is ignored and set to + the equivalent of <varname>min_wal_size</varname>. + </para> + <para> + This parameter can only be set at server start. The default is + <literal>off</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-writer-delay" xreflabel="wal_writer_delay"> <term><varname>wal_writer_delay</varname> (<type>integer</type>) <indexterm> -- 2.25.1
v4-0009-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 3.6K) download From 28440393ed4e58d69f719729e5f0af85bd2997b5 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v4 9/9] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 9753b7b24f..cee88de0b7 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2016,7 +2016,9 @@ GetXLogBuffer(XLogRecPtr ptr) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(endptr); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2256,7 +2258,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5210,15 +5214,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -8117,7 +8131,8 @@ StartupXLOG(void) * Note that "len" indicates the size of the valid part. */ openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8157,7 +8172,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, true); + else + PreallocXlogFiles(EndOfLog); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 05c9d260d8..7799981a34 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2862,7 +2862,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-11-03 13:04  Daniel Gustafsson <daniel@yesql.se>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Daniel Gustafsson @ 2021-11-03 13:04 UTC (permalink / raw)
  To: Takashi Menjo <takashi.menjo@gmail.com>; +Cc: pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>

> On 28 Oct 2021, at 08:09, Takashi Menjo <takashi.menjo@gmail.com> wrote:

> Rebased, and added the patches below into the patchset.

Looks like the 0001 patch needs to be updated to support Windows and MSVC.  See
src/tools/msvc/Mkvcbuild.pm and Solution.pm et.al for inspiration on how to add
the MSVC equivalent of --with-libpmem.  Currently the patch fails in the
"Generating configuration headers" step in Solution.pm.

--
Daniel Gustafsson		https://vmware.com/






^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-11-04 08:46  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Daniel Gustafsson <daniel@yesql.se>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2021-11-04 08:46 UTC (permalink / raw)
  To: Daniel Gustafsson <daniel@yesql.se>; +Cc: pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>

Hello Daniel,

Thank you for your comment. I had the following error message with
MSVC on Windows. It looks the same as what you told me. I'll fix it.

| > cd src\tools\msvc
| > build
| (..snipped..)
| Copying pg_config_os.h...
| Generating configuration headers...
| undefined symbol: HAVE_LIBPMEM at src/include/pg_config.h line 347
at C:/Users/menjo/Documents/git/postgres/src/tools/msvc/Mkvcbuild.pm
line 860.

Best regards,
Takashi


On Wed, Nov 3, 2021 at 10:04 PM Daniel Gustafsson <daniel@yesql.se> wrote:
>
> > On 28 Oct 2021, at 08:09, Takashi Menjo <takashi.menjo@gmail.com> wrote:
>
> > Rebased, and added the patches below into the patchset.
>
> Looks like the 0001 patch needs to be updated to support Windows and MSVC.  See
> src/tools/msvc/Mkvcbuild.pm and Solution.pm et.al for inspiration on how to add
> the MSVC equivalent of --with-libpmem.  Currently the patch fails in the
> "Generating configuration headers" step in Solution.pm.
>
> --
> Daniel Gustafsson               https://vmware.com/
>


-- 
Takashi Menjo <takashi.menjo@gmail.com>





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2021-11-05 06:47  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2021-11-05 06:47 UTC (permalink / raw)
  To: Daniel Gustafsson <daniel@yesql.se>; +Cc: pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>

Hi Daniel,

The issue you told has been fixed.  I attach the v5 patchset to this email.

The v5 has all the patches in the v4, and in addition, has the
following two new patches:

- (v5-0002) Support build with MSVC on Windows: Please have
src\tools\msvc\config.pl as follows to "configure --with-libpmem:"

$config->{pmem} = 'C:\path\to\pmdk\x64-windows';

- (v5-0006) Compatible to Windows: This patch resolves conflicting
mode_t typedefs and libpmem API variants (U or W, like Windows API).

Best regards,
Takashi

On Thu, Nov 4, 2021 at 5:46 PM Takashi Menjo <takashi.menjo@gmail.com> wrote:
>
> Hello Daniel,
>
> Thank you for your comment. I had the following error message with
> MSVC on Windows. It looks the same as what you told me. I'll fix it.
>
> | > cd src\tools\msvc
> | > build
> | (..snipped..)
> | Copying pg_config_os.h...
> | Generating configuration headers...
> | undefined symbol: HAVE_LIBPMEM at src/include/pg_config.h line 347
> at C:/Users/menjo/Documents/git/postgres/src/tools/msvc/Mkvcbuild.pm
> line 860.
>
> Best regards,
> Takashi
>
>
> On Wed, Nov 3, 2021 at 10:04 PM Daniel Gustafsson <daniel@yesql.se> wrote:
> >
> > > On 28 Oct 2021, at 08:09, Takashi Menjo <takashi.menjo@gmail.com> wrote:
> >
> > > Rebased, and added the patches below into the patchset.
> >
> > Looks like the 0001 patch needs to be updated to support Windows and MSVC.  See
> > src/tools/msvc/Mkvcbuild.pm and Solution.pm et.al for inspiration on how to add
> > the MSVC equivalent of --with-libpmem.  Currently the patch fails in the
> > "Generating configuration headers" step in Solution.pm.
> >
> > --
> > Daniel Gustafsson               https://vmware.com/
> >
>
>
> --
> Takashi Menjo <takashi.menjo@gmail.com>



-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (11 files, .tar.gz)

v5-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 5.3K) download From 6acbc499e59131fa19f056b1b398cba388f33f89 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v5 01/11] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index 4ffefe4655..d7c3dc239d 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -8811,6 +8814,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13282,6 +13320,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -13993,6 +14081,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index 44ee3ebe2f..8f117ccccf 100644 --- a/configure.ac +++ b/configure.ac @@ -1020,6 +1020,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1349,6 +1358,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1535,6 +1548,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 15ffdd895a..0ecc132c81 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -343,6 +343,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -910,6 +913,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.25.1
v5-0002-Support-build-with-MSVC-on-Windows.patch (application/octet-stream, 2.8K) download From d0625fbcfed9215b47d5fbdf58ce0207a67f1b52 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:33 +0900 Subject: [PATCH v5 02/11] Support build with MSVC on Windows --- src/tools/msvc/Solution.pm | 13 +++++++++++++ src/tools/msvc/config_default.pl | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index 43fd1be088..913f3ffdbf 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -308,6 +308,7 @@ sub GenerateFiles HAVE_LIBLZ4 => undef, HAVE_LIBM => undef, HAVE_LIBPAM => undef, + HAVE_LIBPMEM => undef, HAVE_LIBREADLINE => undef, HAVE_LIBSELINUX => undef, HAVE_LIBSSL => undef, @@ -492,6 +493,7 @@ sub GenerateFiles USE_BONJOUR => undef, USE_BSD_AUTH => undef, USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBPMEM => undef, USE_LIBXML => undef, USE_LIBXSLT => undef, USE_LZ4 => undef, @@ -542,6 +544,11 @@ sub GenerateFiles $define{HAVE_LZ4_H} = 1; $define{USE_LZ4} = 1; } + if ($self->{options}->{pmem}) + { + $define{HAVE_LIBPMEM} = 1; + $define{USE_LIBPMEM} = 1; + } if ($self->{options}->{openssl}) { $define{USE_OPENSSL} = 1; @@ -1089,6 +1096,11 @@ sub AddProject $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); } + if ($self->{options}->{pmem}) + { + $proj->AddIncludeDir($self->{options}->{pmem} . '\include'); + $proj->AddLibrary($self->{options}->{pmem} . '\lib\libpmem.lib'); + } return $proj; } @@ -1201,6 +1213,7 @@ sub GetFakeConfigure $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); $cfg .= ' --with-perl' if ($self->{options}->{perl}); $cfg .= ' --with-python' if ($self->{options}->{python}); + $cfg .= ' --with-libpmem' if ($self->{options}->{pmem}); my $port = $self->{options}->{'--with-pgport'}; $cfg .= " --with-pgport=$port" if defined($port); diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl index 460c0375d4..774730c9a8 100644 --- a/src/tools/msvc/config_default.pl +++ b/src/tools/msvc/config_default.pl @@ -25,7 +25,8 @@ our $config = { xml => undef, # --with-libxml=<path> xslt => undef, # --with-libxslt=<path> iconv => undef, # (not in configure, path to iconv) - zlib => undef # --with-zlib=<path> + zlib => undef, # --with-zlib=<path> + pmem => undef # --with-libpmem=<path> }; 1; -- 2.25.1
v5-0003-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 4.7K) download From 3377cf33497acba8005a2097f888af776c96eb16 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v5 03/11] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 0a0771a18e..f2277325ec 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -112,6 +112,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5142,13 +5143,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5164,10 +5180,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5261,13 +5284,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index e91d5a3cfd..05c9d260d8 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1343,6 +1343,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index c0a560204b..5ba37ffcdc 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -90,6 +90,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v5-0004-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From ce638ca87300bd36b4e258c010c865922116e3a6 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v5 04/11] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 4 +--- src/include/access/xlog_internal.h | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f2277325ec..39697938bc 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -917,8 +917,6 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void); static void AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3658,7 +3656,7 @@ XLogFileCopy(XLogSegNo destsegno, TimeLineID srcTLI, XLogSegNo srcsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index c0da76cab4..6e712fdf9d 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -324,6 +324,9 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v5-0005-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 16.8K) download From 3e28616b8cc26c08ab1693ddf5810301e89f5f5b Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v5 05/11] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 143 ++++++++++--- src/backend/access/transam/xlogpmem.c | 297 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 466 insertions(+), 34 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 39697938bc..70e54eee4f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -2003,7 +2004,13 @@ GetXLogBuffer(XLogRecPtr ptr) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(endptr); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2237,7 +2244,13 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2456,6 +2469,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2478,6 +2493,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2503,31 +2522,34 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogFile = XLogFileOpen(openLogSegNo); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogFile = XLogFileOpen(openLogSegNo); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2535,8 +2557,8 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2574,7 +2596,37 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2632,7 +2684,10 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2683,12 +2738,14 @@ XLogWrite(XLogwrtRqst WriteRqst, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -8044,11 +8101,29 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + */ + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..74d49eac92 --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,297 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno); +static void PmemXLogCreate(XLogSegNo segno); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, ThisTimeLineID, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..62c9e1f5e2 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v5-0006-Compatible-to-Windows.patch (application/octet-stream, 1.6K) download From 95986d49f273dea2c2980aa7ff49fa3de1a026e8 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:25 +0900 Subject: [PATCH v5 06/11] Compatible to Windows --- src/backend/access/transam/xlogpmem.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 74d49eac92..c5c9e94444 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -8,7 +8,24 @@ #include <stdint.h> /* uintptr_t */ #include <unistd.h> /* getpid, unlink */ +/* + * On Windows, we will have two ported but conflicting mode_t: + * + * mode_t in libpmem: + * libpmem.h -> pmemcompat.h -> typedef int mode_t + * mode_t in PostgreSQL: + * c.h -> port.h -> win32_port.h -> typedef unsigned short mode_t + * + * We want to use PostgreSQL's one, so conseal libpmem's one. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +#define mode_t unused_libpmem_mode_t #include <libpmem.h> +#undef mode_t +/* On other platforms, simply include libpmem.h */ +#else +#include <libpmem.h> +#endif #include "c.h" /* bool, Size */ #include "access/xlog.h" @@ -242,7 +259,11 @@ PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) mapped_len = 0; is_pmem = 0; +#if defined(WIN32) && !defined(__CYGWIN__) + addr = pmem_map_fileU(path, param_len, flags, mode, &mapped_len, &is_pmem); +#else addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); +#endif if (addr == NULL) { -- 2.25.1
v5-0007-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From 34105e4239a4cb7672b57c8dd110ff90b77ada91 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v5 07/11] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index c5c9e94444..db7cb1fc16 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -195,6 +195,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -203,22 +204,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v5-0008-Let-wal_pmem_map-be-constant-unless-with-libpmem.patch (application/octet-stream, 1.4K) download From 24a24974fd96135c36836d819a5a56e28c3f4d7c Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 13 Oct 2021 11:10:17 +0900 Subject: [PATCH v5 08/11] Let wal_pmem_map be constant unless --with-libpmem --- src/backend/access/transam/xlog.c | 3 +++ src/include/access/xlog.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 70e54eee4f..a51847cb7a 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -113,7 +113,10 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; + +#ifdef USE_LIBPMEM bool wal_pmem_map = false; +#endif #ifdef WAL_DEBUG bool XLOG_DEBUG = false; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 5ba37ffcdc..cd677a4c97 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -90,7 +90,12 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; + +#ifdef USE_LIBPMEM extern bool wal_pmem_map; +#else +#define wal_pmem_map false +#endif /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v5-0009-Ensure-WAL-mappings-before-assertion.patch (application/octet-stream, 1.4K) download From 52b3fd58f5fa22884744808bf7c7653a6b0309fb Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 28 Oct 2021 13:35:28 +0900 Subject: [PATCH v5 09/11] Ensure WAL mappings before assertion --- src/backend/access/transam/xlog.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a51847cb7a..668cebb871 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1924,6 +1924,23 @@ GetXLogBuffer(XLogRecPtr ptr) */ if (ptr / XLOG_BLCKSZ == cachedPage) { + /* + * Ensure WAL mappings before assersion. + * + * cachedPos should be recaluculated because it has been probably + * invalidated due to WAL remapping. This should be done even if + * openLogSegNo seems not to change because the address of the + * mapping could have changed (ABA problem). + */ + if (wal_pmem_map) + { + endptr = ptr - ptr % XLOG_BLCKSZ + XLOG_BLCKSZ; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr); + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); + } + Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); return cachedPos + ptr % XLOG_BLCKSZ; -- 2.25.1
v5-0010-Update-document.patch (application/octet-stream, 2.0K) download From 76106df91435ae84cc66b8050958b56ce34ad8b5 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 30 Sep 2021 12:51:56 +0900 Subject: [PATCH v5 10/11] Update document --- doc/src/sgml/config.sgml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index de77f14573..18a1ef4999 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3239,6 +3239,33 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-wal-pmem-map" xreflabel="wal_pmem_map"> + <term><varname>wal_pmem_map</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>wal_pmem_map</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + If set to <literal>on</literal>, this parameter causes WAL files to be + memory-mapped and used as WAL buffer pages. The WAL files in pg_wal + directory (or the directory given by <command>initdb -X</command> + option) should be on <firstterm>persistent memory</firstterm> (PMEM) and + the filesystem for those files should support the <firstterm>Direct + Access</firstterm> (DAX) feature. <varname>wal_sync_method</varname> + for the primary server is ignored and WAL updates are forced out to + PMEM in a more optimal way which avoids calling into the kernel. + <varname>min_wal_size</varname> should be multiple of the size of a + WAL file, and <varname>wal_buffers</varname> is ignored and set to + the equivalent of <varname>min_wal_size</varname>. + </para> + <para> + This parameter can only be set at server start. The default is + <literal>off</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-writer-delay" xreflabel="wal_writer_delay"> <term><varname>wal_writer_delay</varname> (<type>integer</type>) <indexterm> -- 2.25.1
v5-0011-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 3.6K) download From 925b3899baacbf3628e2b0c066dee060754c22d0 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v5 11/11] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 668cebb871..1fa0f2f520 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2027,7 +2027,9 @@ GetXLogBuffer(XLogRecPtr ptr) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(endptr); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2267,7 +2269,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, bool opportunistic) if (wal_pmem_map) { openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5221,15 +5225,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -8128,7 +8142,8 @@ StartupXLOG(void) * Note that "len" indicates the size of the valid part. */ openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8168,7 +8183,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, true); + else + PreallocXlogFiles(EndOfLog); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 05c9d260d8..7799981a34 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2862,7 +2862,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-06 01:32  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2022-01-06 01:32 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

Rebased.

On Fri, Nov 5, 2021 at 3:47 PM Takashi Menjo <takashi.menjo@gmail.com> wrote:
>
> Hi Daniel,
>
> The issue you told has been fixed.  I attach the v5 patchset to this email.
>
> The v5 has all the patches in the v4, and in addition, has the
> following two new patches:
>
> - (v5-0002) Support build with MSVC on Windows: Please have
> src\tools\msvc\config.pl as follows to "configure --with-libpmem:"
>
> $config->{pmem} = 'C:\path\to\pmdk\x64-windows';
>
> - (v5-0006) Compatible to Windows: This patch resolves conflicting
> mode_t typedefs and libpmem API variants (U or W, like Windows API).
>
> Best regards,
> Takashi
>
> On Thu, Nov 4, 2021 at 5:46 PM Takashi Menjo <takashi.menjo@gmail.com> wrote:
> >
> > Hello Daniel,
> >
> > Thank you for your comment. I had the following error message with
> > MSVC on Windows. It looks the same as what you told me. I'll fix it.
> >
> > | > cd src\tools\msvc
> > | > build
> > | (..snipped..)
> > | Copying pg_config_os.h...
> > | Generating configuration headers...
> > | undefined symbol: HAVE_LIBPMEM at src/include/pg_config.h line 347
> > at C:/Users/menjo/Documents/git/postgres/src/tools/msvc/Mkvcbuild.pm
> > line 860.
> >
> > Best regards,
> > Takashi
> >
> >
> > On Wed, Nov 3, 2021 at 10:04 PM Daniel Gustafsson <daniel@yesql.se> wrote:
> > >
> > > > On 28 Oct 2021, at 08:09, Takashi Menjo <takashi.menjo@gmail.com> wrote:
> > >
> > > > Rebased, and added the patches below into the patchset.
> > >
> > > Looks like the 0001 patch needs to be updated to support Windows and MSVC.  See
> > > src/tools/msvc/Mkvcbuild.pm and Solution.pm et.al for inspiration on how to add
> > > the MSVC equivalent of --with-libpmem.  Currently the patch fails in the
> > > "Generating configuration headers" step in Solution.pm.
> > >
> > > --
> > > Daniel Gustafsson               https://vmware.com/
> > >
> >
> >
> > --
> > Takashi Menjo <takashi.menjo@gmail.com>
>
>
>
> --
> Takashi Menjo <takashi.menjo@gmail.com>



-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (11 files, .tar.gz)

v6-0003-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 4.7K) download From 228dddb65c277f759cf78c12146af5be60676871 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v6 03/11] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 87cd05c945..02f63c3138 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -115,6 +115,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5194,13 +5195,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5216,10 +5232,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5313,13 +5336,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index f9504d3aec..ee18a9cf33 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1344,6 +1344,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 34f6c89f06..73900cbc9e 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -88,6 +88,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v6-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 5.3K) download From 55c2245875736c8184d8ef3dfe9452476ab62bf5 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v6 01/11] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index 3b19105328..22c364fac4 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -9033,6 +9036,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13504,6 +13542,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -14215,6 +14303,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index e77d4dcf2d..2bea131375 100644 --- a/configure.ac +++ b/configure.ac @@ -1056,6 +1056,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1385,6 +1394,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1571,6 +1584,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 7525c16597..96604aa130 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -331,6 +331,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -898,6 +901,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.25.1
v6-0004-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From 2dc78a56e58a31777d8ebdf356296a0c53108875 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v6 04/11] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 5 +---- src/include/access/xlog_internal.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 02f63c3138..73a3477be0 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -923,9 +923,6 @@ static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno, - TimeLineID tli); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3700,7 +3697,7 @@ XLogFileCopy(TimeLineID destTLI, XLogSegNo destsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, TimeLineID tli) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index c0da76cab4..328128b48d 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -324,6 +324,10 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno, + TimeLineID tli); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v6-0002-Support-build-with-MSVC-on-Windows.patch (application/octet-stream, 2.8K) download From 72e1ee3fb5f926b54e9502522b05605fc0b235c7 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:33 +0900 Subject: [PATCH v6 02/11] Support build with MSVC on Windows --- src/tools/msvc/Solution.pm | 13 +++++++++++++ src/tools/msvc/config_default.pl | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index 2c8cd521e9..d843391050 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -304,6 +304,7 @@ sub GenerateFiles HAVE_LIBLZ4 => undef, HAVE_LIBM => undef, HAVE_LIBPAM => undef, + HAVE_LIBPMEM => undef, HAVE_LIBREADLINE => undef, HAVE_LIBSELINUX => undef, HAVE_LIBSSL => undef, @@ -488,6 +489,7 @@ sub GenerateFiles USE_BONJOUR => undef, USE_BSD_AUTH => undef, USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBPMEM => undef, USE_LIBXML => undef, USE_LIBXSLT => undef, USE_LZ4 => undef, @@ -538,6 +540,11 @@ sub GenerateFiles $define{HAVE_LZ4_H} = 1; $define{USE_LZ4} = 1; } + if ($self->{options}->{pmem}) + { + $define{HAVE_LIBPMEM} = 1; + $define{USE_LIBPMEM} = 1; + } if ($self->{options}->{openssl}) { $define{USE_OPENSSL} = 1; @@ -1085,6 +1092,11 @@ sub AddProject $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); } + if ($self->{options}->{pmem}) + { + $proj->AddIncludeDir($self->{options}->{pmem} . '\include'); + $proj->AddLibrary($self->{options}->{pmem} . '\lib\libpmem.lib'); + } return $proj; } @@ -1197,6 +1209,7 @@ sub GetFakeConfigure $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); $cfg .= ' --with-perl' if ($self->{options}->{perl}); $cfg .= ' --with-python' if ($self->{options}->{python}); + $cfg .= ' --with-libpmem' if ($self->{options}->{pmem}); my $port = $self->{options}->{'--with-pgport'}; $cfg .= " --with-pgport=$port" if defined($port); diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl index 460c0375d4..774730c9a8 100644 --- a/src/tools/msvc/config_default.pl +++ b/src/tools/msvc/config_default.pl @@ -25,7 +25,8 @@ our $config = { xml => undef, # --with-libxml=<path> xslt => undef, # --with-libxslt=<path> iconv => undef, # (not in configure, path to iconv) - zlib => undef # --with-zlib=<path> + zlib => undef, # --with-zlib=<path> + pmem => undef # --with-libpmem=<path> }; 1; -- 2.25.1
v6-0005-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 17.3K) download From 109d57dff2d4c1f8b233f6d66faa7630d5d32aa1 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v6 05/11] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 153 +++++++++---- src/backend/access/transam/xlogpmem.c | 297 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 474 insertions(+), 36 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 73a3477be0..be56599f9f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -2024,7 +2025,14 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2258,7 +2266,14 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2477,6 +2492,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2499,6 +2516,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2524,33 +2545,36 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo, tli); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo, tli); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; - openLogFile = XLogFileOpen(openLogSegNo, tli); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; + openLogFile = XLogFileOpen(openLogSegNo, tli); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2558,8 +2582,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2597,7 +2621,38 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr, tli); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2655,7 +2710,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo, tli); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo, tli); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2706,12 +2764,14 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -8099,11 +8159,32 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + * + * TODO how about if (newTLI != replayTLI) ? + */ + openLogTLI = newTLI; + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..5b50ba80a7 --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,297 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno, TimeLineID tli); +static void PmemXLogCreate(XLogSegNo segno, TimeLineID tli); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno, tli); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno, TimeLineID tli) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, tli, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno, tli); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno, TimeLineID tli) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno, tli)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..3978640b82 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, tli) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v6-0008-Let-wal_pmem_map-be-constant-unless-with-libpmem.patch (application/octet-stream, 1.4K) download From 1e101b99789ce947913b597eaada66e639d2bc38 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 13 Oct 2021 11:10:17 +0900 Subject: [PATCH v6 08/11] Let wal_pmem_map be constant unless --with-libpmem --- src/backend/access/transam/xlog.c | 3 +++ src/include/access/xlog.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index be56599f9f..62f08cb50b 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -116,7 +116,10 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; + +#ifdef USE_LIBPMEM bool wal_pmem_map = false; +#endif #ifdef WAL_DEBUG bool XLOG_DEBUG = false; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 73900cbc9e..ab3eb3887b 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -88,7 +88,12 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; + +#ifdef USE_LIBPMEM extern bool wal_pmem_map; +#else +#define wal_pmem_map false +#endif /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v6-0006-Compatible-to-Windows.patch (application/octet-stream, 1.6K) download From 2c99d99a7d99c9b20eb341622c2259e4a9efe8e3 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:25 +0900 Subject: [PATCH v6 06/11] Compatible to Windows --- src/backend/access/transam/xlogpmem.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 5b50ba80a7..8bd990f1cd 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -8,7 +8,24 @@ #include <stdint.h> /* uintptr_t */ #include <unistd.h> /* getpid, unlink */ +/* + * On Windows, we will have two ported but conflicting mode_t: + * + * mode_t in libpmem: + * libpmem.h -> pmemcompat.h -> typedef int mode_t + * mode_t in PostgreSQL: + * c.h -> port.h -> win32_port.h -> typedef unsigned short mode_t + * + * We want to use PostgreSQL's one, so conseal libpmem's one. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +#define mode_t unused_libpmem_mode_t #include <libpmem.h> +#undef mode_t +/* On other platforms, simply include libpmem.h */ +#else +#include <libpmem.h> +#endif #include "c.h" /* bool, Size */ #include "access/xlog.h" @@ -242,7 +259,11 @@ PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) mapped_len = 0; is_pmem = 0; +#if defined(WIN32) && !defined(__CYGWIN__) + addr = pmem_map_fileU(path, param_len, flags, mode, &mapped_len, &is_pmem); +#else addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); +#endif if (addr == NULL) { -- 2.25.1
v6-0009-Ensure-WAL-mappings-before-assertion.patch (application/octet-stream, 1.4K) download From 81602888e7bd65a53029dcf7f29ab21bb7d10b60 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 28 Oct 2021 13:35:28 +0900 Subject: [PATCH v6 09/11] Ensure WAL mappings before assertion --- src/backend/access/transam/xlog.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 62f08cb50b..f0d7a317d2 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1945,6 +1945,23 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) */ if (ptr / XLOG_BLCKSZ == cachedPage) { + /* + * Ensure WAL mappings before assersion. + * + * cachedPos should be recaluculated because it has been probably + * invalidated due to WAL remapping. This should be done even if + * openLogSegNo seems not to change because the address of the + * mapping could have changed (ABA problem). + */ + if (wal_pmem_map) + { + endptr = ptr - ptr % XLOG_BLCKSZ + XLOG_BLCKSZ; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); + } + Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); return cachedPos + ptr % XLOG_BLCKSZ; -- 2.25.1
v6-0007-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From e08edd4bf62984eb7b14572a46692570b4ef03fa Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v6 07/11] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 8bd990f1cd..d91fb2175b 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -195,6 +195,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -203,22 +204,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v6-0010-Update-document.patch (application/octet-stream, 2.0K) download From 4ce58cd2c2e7048441b253bf41e826c828f75106 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 30 Sep 2021 12:51:56 +0900 Subject: [PATCH v6 10/11] Update document --- doc/src/sgml/config.sgml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index afbb6c35e3..0fc3b48735 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3239,6 +3239,33 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-wal-pmem-map" xreflabel="wal_pmem_map"> + <term><varname>wal_pmem_map</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>wal_pmem_map</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + If set to <literal>on</literal>, this parameter causes WAL files to be + memory-mapped and used as WAL buffer pages. The WAL files in pg_wal + directory (or the directory given by <command>initdb -X</command> + option) should be on <firstterm>persistent memory</firstterm> (PMEM) and + the filesystem for those files should support the <firstterm>Direct + Access</firstterm> (DAX) feature. <varname>wal_sync_method</varname> + for the primary server is ignored and WAL updates are forced out to + PMEM in a more optimal way which avoids calling into the kernel. + <varname>min_wal_size</varname> should be multiple of the size of a + WAL file, and <varname>wal_buffers</varname> is ignored and set to + the equivalent of <varname>min_wal_size</varname>. + </para> + <para> + This parameter can only be set at server start. The default is + <literal>off</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-writer-delay" xreflabel="wal_writer_delay"> <term><varname>wal_writer_delay</varname> (<type>integer</type>) <indexterm> -- 2.25.1
v6-0011-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 3.6K) download From c803ffd63b18bd87cef842b727b42c4c10eb7e76 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v6 11/11] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f0d7a317d2..1196ae21e8 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2049,7 +2049,9 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2290,7 +2292,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5275,15 +5279,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -8189,7 +8203,8 @@ StartupXLOG(void) */ openLogTLI = newTLI; openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8229,7 +8244,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog, newTLI); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, newTLI, true); + else + PreallocXlogFiles(EndOfLog, newTLI); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ee18a9cf33..6f667d5c43 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2866,7 +2866,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-06 04:00  Justin Pryzby <pryzby@telsasoft.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Justin Pryzby @ 2022-01-06 04:00 UTC (permalink / raw)
  To: Takashi Menjo <takashi.menjo@gmail.com>; +Cc: pgsql-hackers@postgresql.org, Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; tsunakawa.takay@fujitsu.com, "Deng, Gang" <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

The cfbot showed issues compiling on linux and windows.
http://cfbot.cputube.org/takashi-menjo.html

https://cirrus-ci.com/task/6125740327436288
[02:30:06.538] In file included from xlog.c:38:
[02:30:06.538] ../../../../src/include/access/xlogpmem.h:32:42: error: unknown type name ‘tli’
[02:30:06.538]    32 | PmemXLogEnsurePrevMapped(XLogRecPtr ptr, tli)
[02:30:06.538]       |                                          ^~~
[02:30:06.538] xlog.c: In function ‘GetXLogBuffer’:
[02:30:06.538] xlog.c:1959:19: warning: implicit declaration of function ‘PmemXLogEnsurePrevMapped’ [-Wimplicit-function-declaration]
[02:30:06.538]  1959 |    openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli);

https://cirrus-ci.com/task/6688690280857600?logs=build#L379
[02:33:25.752] c:\cirrus\src\include\access\xlogpmem.h(33,1): error C2081: 'tli': name in formal parameter list illegal (compiling source file src/backend/access/transam/xlog.c) [c:\cirrus\postgres.vcxproj]

I'm attaching a probable fix.  Unfortunately, for patches like this, most of
the functionality isn't exercised unless the library is installed and
compilation and runtime are enabled by default.

In 0009: recaluculated => recalculated

0010-Update-document should be squished with 0003-Add-wal_pmem_map-to-GUC (and
maybe 0002 and 0001).  I believe the patches after 0005 are more WIP, so it's
fine if they're not squished yet.  I'm not sure what the point is of this one:
0008-Let-wal_pmem_map-be-constant-unl

+               ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not pmem_map_file \"%s\": %m", path)));

=> The outer parenthesis are not needed since e3a87b4.

Attachments: download all (12 files, .tar.gz)

0001-Add-with-libpmem-option-for-PMEM-support.patch (text/x-diff, 5.3K) download From e5614f2ea3ff6aaf016343f81f74366440e18f6f Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH 01/13] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index 3b19105328d..22c364fac4f 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -9033,6 +9036,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13504,6 +13542,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -14215,6 +14303,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index e77d4dcf2d2..2bea131375a 100644 --- a/configure.ac +++ b/configure.ac @@ -1056,6 +1056,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1385,6 +1394,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1571,6 +1584,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 7525c165974..96604aa130d 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -331,6 +331,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -898,6 +901,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.17.1
0002-Support-build-with-MSVC-on-Windows.patch (text/x-diff, 2.8K) download From f014b7923e9cee1f1f3cfd3ea23fc91ace3f1474 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:33 +0900 Subject: [PATCH 02/13] Support build with MSVC on Windows --- src/tools/msvc/Solution.pm | 13 +++++++++++++ src/tools/msvc/config_default.pl | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index 2c8cd521e94..d843391050b 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -304,6 +304,7 @@ sub GenerateFiles HAVE_LIBLZ4 => undef, HAVE_LIBM => undef, HAVE_LIBPAM => undef, + HAVE_LIBPMEM => undef, HAVE_LIBREADLINE => undef, HAVE_LIBSELINUX => undef, HAVE_LIBSSL => undef, @@ -488,6 +489,7 @@ sub GenerateFiles USE_BONJOUR => undef, USE_BSD_AUTH => undef, USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBPMEM => undef, USE_LIBXML => undef, USE_LIBXSLT => undef, USE_LZ4 => undef, @@ -538,6 +540,11 @@ sub GenerateFiles $define{HAVE_LZ4_H} = 1; $define{USE_LZ4} = 1; } + if ($self->{options}->{pmem}) + { + $define{HAVE_LIBPMEM} = 1; + $define{USE_LIBPMEM} = 1; + } if ($self->{options}->{openssl}) { $define{USE_OPENSSL} = 1; @@ -1085,6 +1092,11 @@ sub AddProject $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); } + if ($self->{options}->{pmem}) + { + $proj->AddIncludeDir($self->{options}->{pmem} . '\include'); + $proj->AddLibrary($self->{options}->{pmem} . '\lib\libpmem.lib'); + } return $proj; } @@ -1197,6 +1209,7 @@ sub GetFakeConfigure $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); $cfg .= ' --with-perl' if ($self->{options}->{perl}); $cfg .= ' --with-python' if ($self->{options}->{python}); + $cfg .= ' --with-libpmem' if ($self->{options}->{pmem}); my $port = $self->{options}->{'--with-pgport'}; $cfg .= " --with-pgport=$port" if defined($port); diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl index 460c0375d4b..774730c9a8f 100644 --- a/src/tools/msvc/config_default.pl +++ b/src/tools/msvc/config_default.pl @@ -25,7 +25,8 @@ our $config = { xml => undef, # --with-libxml=<path> xslt => undef, # --with-libxslt=<path> iconv => undef, # (not in configure, path to iconv) - zlib => undef # --with-zlib=<path> + zlib => undef, # --with-zlib=<path> + pmem => undef # --with-libpmem=<path> }; 1; -- 2.17.1
0003-Add-wal_pmem_map-to-GUC.patch (text/x-diff, 4.7K) download From 7c2d6665a925dc5615f6ffd999374e10c3ea2199 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH 03/13] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 87cd05c9454..02f63c31387 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -115,6 +115,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5194,13 +5195,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5216,10 +5232,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5313,13 +5336,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index f9504d3aec4..ee18a9cf338 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1344,6 +1344,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 34f6c89f067..73900cbc9e7 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -88,6 +88,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.17.1
0004-Export-InstallXLogFileSegment.patch (text/x-diff, 2.1K) download From a09110f5932cac18d7822d296d189de47698f70a Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH 04/13] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 5 +---- src/include/access/xlog_internal.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 02f63c31387..73a3477be04 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -923,9 +923,6 @@ static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno, - TimeLineID tli); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3700,7 +3697,7 @@ XLogFileCopy(TimeLineID destTLI, XLogSegNo destsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, TimeLineID tli) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index c0da76cab49..328128b48d5 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -324,6 +324,10 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno, + TimeLineID tli); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.17.1
0005-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (text/x-diff, 17.3K) download From 443e47112de2d8c735be0ce9bddb01e7e77de672 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH 05/13] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 153 +++++++++---- src/backend/access/transam/xlogpmem.c | 297 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 474 insertions(+), 36 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de722..3a29583bc03 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 73a3477be04..be56599f9fd 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -2024,7 +2025,14 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2258,7 +2266,14 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2477,6 +2492,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2499,6 +2516,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2524,33 +2545,36 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo, tli); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo, tli); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; - openLogFile = XLogFileOpen(openLogSegNo, tli); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; + openLogFile = XLogFileOpen(openLogSegNo, tli); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2558,8 +2582,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2597,7 +2621,38 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr, tli); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2655,7 +2710,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo, tli); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo, tli); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2706,12 +2764,14 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -8099,11 +8159,32 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + * + * TODO how about if (newTLI != replayTLI) ? + */ + openLogTLI = newTLI; + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 00000000000..5b50ba80a7a --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,297 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno, TimeLineID tli); +static void PmemXLogCreate(XLogSegNo segno, TimeLineID tli); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno, tli); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno, TimeLineID tli) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, tli, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno, tli); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno, TimeLineID tli) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno, tli)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 00000000000..3978640b82f --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, tli) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.17.1
0006-compile-fix-without-pmem.patch (text/x-diff, 727B) download From 90ea943904793c5212cd1fed450e4f3b1f97f8a7 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryzbyj@telsasoft.com> Date: Wed, 5 Jan 2022 21:28:51 -0600 Subject: [PATCH 06/13] compile-fix-without-pmem --- src/include/access/xlogpmem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h index 3978640b82f..cd83bffc883 100644 --- a/src/include/access/xlogpmem.h +++ b/src/include/access/xlogpmem.h @@ -29,7 +29,7 @@ extern void PmemXLogSync(void); #include <stdlib.h> /* abort */ static inline XLogSegNo -PmemXLogEnsurePrevMapped(XLogRecPtr ptr, tli) +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) { abort(); return 0; -- 2.17.1
0007-Compatible-to-Windows.patch (text/x-diff, 1.6K) download From 9b8091780f0156b35297beaecc5868a2df835e80 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:25 +0900 Subject: [PATCH 07/13] Compatible to Windows --- src/backend/access/transam/xlogpmem.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 5b50ba80a7a..8bd990f1cd0 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -8,7 +8,24 @@ #include <stdint.h> /* uintptr_t */ #include <unistd.h> /* getpid, unlink */ +/* + * On Windows, we will have two ported but conflicting mode_t: + * + * mode_t in libpmem: + * libpmem.h -> pmemcompat.h -> typedef int mode_t + * mode_t in PostgreSQL: + * c.h -> port.h -> win32_port.h -> typedef unsigned short mode_t + * + * We want to use PostgreSQL's one, so conseal libpmem's one. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +#define mode_t unused_libpmem_mode_t +#include <libpmem.h> +#undef mode_t +/* On other platforms, simply include libpmem.h */ +#else #include <libpmem.h> +#endif #include "c.h" /* bool, Size */ #include "access/xlog.h" @@ -242,7 +259,11 @@ PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) mapped_len = 0; is_pmem = 0; +#if defined(WIN32) && !defined(__CYGWIN__) + addr = pmem_map_fileU(path, param_len, flags, mode, &mapped_len, &is_pmem); +#else addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); +#endif if (addr == NULL) { -- 2.17.1
0008-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (text/x-diff, 2.2K) download From 35c77c59d2e4aec95d6c1489b052d4456855f72f Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH 08/13] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 8bd990f1cd0..d91fb2175b7 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -195,6 +195,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -203,22 +204,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.17.1
0009-Let-wal_pmem_map-be-constant-unless-with-libpmem.patch (text/x-diff, 1.4K) download From 557ae7c84ea24bc6c7c42104144c422b0154d166 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 13 Oct 2021 11:10:17 +0900 Subject: [PATCH 09/13] Let wal_pmem_map be constant unless --with-libpmem --- src/backend/access/transam/xlog.c | 3 +++ src/include/access/xlog.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index be56599f9fd..62f08cb50bb 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -116,7 +116,10 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; + +#ifdef USE_LIBPMEM bool wal_pmem_map = false; +#endif #ifdef WAL_DEBUG bool XLOG_DEBUG = false; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 73900cbc9e7..ab3eb3887b9 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -88,7 +88,12 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; + +#ifdef USE_LIBPMEM extern bool wal_pmem_map; +#else +#define wal_pmem_map false +#endif /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.17.1
0010-Ensure-WAL-mappings-before-assertion.patch (text/x-diff, 1.4K) download From 133e1281d4b308df6f63edebae94cf1856598af4 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 28 Oct 2021 13:35:28 +0900 Subject: [PATCH 10/13] Ensure WAL mappings before assertion --- src/backend/access/transam/xlog.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 62f08cb50bb..f0d7a317d23 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1945,6 +1945,23 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) */ if (ptr / XLOG_BLCKSZ == cachedPage) { + /* + * Ensure WAL mappings before assersion. + * + * cachedPos should be recaluculated because it has been probably + * invalidated due to WAL remapping. This should be done even if + * openLogSegNo seems not to change because the address of the + * mapping could have changed (ABA problem). + */ + if (wal_pmem_map) + { + endptr = ptr - ptr % XLOG_BLCKSZ + XLOG_BLCKSZ; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); + } + Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); return cachedPos + ptr % XLOG_BLCKSZ; -- 2.17.1
0011-Update-document.patch (text/x-diff, 2.0K) download From f3a91d9198d6498c156b8078b77f0e4c401e224f Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 30 Sep 2021 12:51:56 +0900 Subject: [PATCH 11/13] Update document --- doc/src/sgml/config.sgml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 10aa18b7636..5e55564f42a 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3239,6 +3239,33 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-wal-pmem-map" xreflabel="wal_pmem_map"> + <term><varname>wal_pmem_map</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>wal_pmem_map</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + If set to <literal>on</literal>, this parameter causes WAL files to be + memory-mapped and used as WAL buffer pages. The WAL files in pg_wal + directory (or the directory given by <command>initdb -X</command> + option) should be on <firstterm>persistent memory</firstterm> (PMEM) and + the filesystem for those files should support the <firstterm>Direct + Access</firstterm> (DAX) feature. <varname>wal_sync_method</varname> + for the primary server is ignored and WAL updates are forced out to + PMEM in a more optimal way which avoids calling into the kernel. + <varname>min_wal_size</varname> should be multiple of the size of a + WAL file, and <varname>wal_buffers</varname> is ignored and set to + the equivalent of <varname>min_wal_size</varname>. + </para> + <para> + This parameter can only be set at server start. The default is + <literal>off</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-writer-delay" xreflabel="wal_writer_delay"> <term><varname>wal_writer_delay</varname> (<type>integer</type>) <indexterm> -- 2.17.1
0012-Preallocate-and-initialize-more-WAL-if-wal_pmem_map-.patch (text/x-diff, 3.6K) download From b5d695e660b2dad66c96c5944d343ba0e825c7c3 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH 12/13] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f0d7a317d23..1196ae21e80 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2049,7 +2049,9 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2290,7 +2292,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5275,15 +5279,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -8189,7 +8203,8 @@ StartupXLOG(void) */ openLogTLI = newTLI; openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8229,7 +8244,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog, newTLI); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, newTLI, true); + else + PreallocXlogFiles(EndOfLog, newTLI); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ee18a9cf338..6f667d5c43f 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2866,7 +2866,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.17.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-06 04:52  Thomas Munro <thomas.munro@gmail.com>
  parent: Justin Pryzby <pryzby@telsasoft.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Thomas Munro @ 2022-01-06 04:52 UTC (permalink / raw)
  To: Justin Pryzby <pryzby@telsasoft.com>; +Cc: Takashi Menjo <takashi.menjo@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

On Thu, Jan 6, 2022 at 5:00 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
> I'm attaching a probable fix.  Unfortunately, for patches like this, most of
> the functionality isn't exercised unless the library is installed and
> compilation and runtime are enabled by default.

By the way, you could add a separate patch marked not-for-commit that
adds, say, an apt-get command to the Linux task in the .cirrus.yml
file, and whatever --with-blah stuff you might need to the configure
part, if that'd be useful to test this code.  Eventually, if we wanted
to support that permanently for all CI testing, we'd want to push
package installation down to the image building scripts (not in the pg
source tree) so that CI starts with everything we need pre-installed,
but one of the goals of the recent CI work was to make it possible for
patches that include dependency changes to be possible (for example
the alternative SSL library threads).





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-06 05:19  Justin Pryzby <pryzby@telsasoft.com>
  parent: Thomas Munro <thomas.munro@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Justin Pryzby @ 2022-01-06 05:19 UTC (permalink / raw)
  To: Thomas Munro <thomas.munro@gmail.com>; +Cc: Takashi Menjo <takashi.menjo@gmail.com>; pgsql-hackers@postgresql.org, Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

On Thu, Jan 06, 2022 at 05:52:01PM +1300, Thomas Munro wrote:
> On Thu, Jan 6, 2022 at 5:00 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
> > I'm attaching a probable fix.  Unfortunately, for patches like this, most of
> > the functionality isn't exercised unless the library is installed and
> > compilation and runtime are enabled by default.
> 
> By the way, you could add a separate patch marked not-for-commit that
> adds, say, an apt-get command to the Linux task in the .cirrus.yml
> file, and whatever --with-blah stuff you might need to the configure
> part, if that'd be useful to test this code.

In general, I think that's more or less essential.

But in this case it really doesn't work :(

running bootstrap script ... 2022-01-05 23:17:30.244 CST [12088] FATAL:  file not on PMEM: path "pg_wal/000000010000000000000001"

-- 
Justin





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-07 03:50  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Justin Pryzby <pryzby@telsasoft.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2022-01-07 03:50 UTC (permalink / raw)
  To: Justin Pryzby <pryzby@telsasoft.com>; +Cc: Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

Hi Justin,

Thank you for your build test and comments. The v7 patchset attached
to this email fixes the issues you reported.


> The cfbot showed issues compiling on linux and windows.
> http://cfbot.cputube.org/takashi-menjo.html
>
> https://cirrus-ci.com/task/6125740327436288
> [02:30:06.538] In file included from xlog.c:38:
> [02:30:06.538] ../../../../src/include/access/xlogpmem.h:32:42: error: unknown type name ‘tli’
> [02:30:06.538]    32 | PmemXLogEnsurePrevMapped(XLogRecPtr ptr, tli)
> [02:30:06.538]       |                                          ^~~
> [02:30:06.538] xlog.c: In function ‘GetXLogBuffer’:
> [02:30:06.538] xlog.c:1959:19: warning: implicit declaration of function ‘PmemXLogEnsurePrevMapped’ [-Wimplicit-function-declaration]
> [02:30:06.538]  1959 |    openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli);
>
> https://cirrus-ci.com/task/6688690280857600?logs=build#L379
> [02:33:25.752] c:\cirrus\src\include\access\xlogpmem.h(33,1): error C2081: 'tli': name in formal parameter list illegal (compiling source file src/backend/access/transam/xlog.c) [c:\cirrus\postgres.vcxproj]
>
> I'm attaching a probable fix.  Unfortunately, for patches like this, most of
> the functionality isn't exercised unless the library is installed and
> compilation and runtime are enabled by default.

I got the same error when without --with-libpmem. Your fix looks
reasonable. My v7-0008 fixes this error.


> In 0009: recaluculated => recalculated

v7-0011 fixes this typo.


> 0010-Update-document should be squished with 0003-Add-wal_pmem_map-to-GUC (and
> maybe 0002 and 0001).  I believe the patches after 0005 are more WIP, so it's
> fine if they're not squished yet.

As you say, the patch updating document should melt into a related
fix, probably "Add wal_pmem_map to GUC".  For now I want it to be a
separate patch (v7-0014).


> I'm not sure what the point is of this one: 0008-Let-wal_pmem_map-be-constant-unl

If USE_LIBPMEM is not defined (that is, no --with-libpmem),
wal_pmem_map is always false and is never used essentially. Using
#if(n)def everywhere is not good for code readability, so I let
wal_pmem_map be constant. This may help compilers optimize conditional
branches.

v7-0005 adds the comment above.


> +               ereport(ERROR,
> +                               (errcode_for_file_access(),
> +                                errmsg("could not pmem_map_file \"%s\": %m", path)));
>
> => The outer parenthesis are not needed since e3a87b4.

v7-0009 fixes this.


> But in this case it really doesn't work :(
>
> running bootstrap script ... 2022-01-05 23:17:30.244 CST [12088] FATAL:  file not on PMEM: path "pg_wal/000000010000000000000001"

Do you have a real PMEM device such as NVDIMM-N or Intel Optane PMem?
If so, please use a PMEM mounted with Filesystem DAX option for
pg_wal, or the FATAL error will occur.

If you don't, you have two alternatives below. Note that neither of
them ensures durability. Each of them is just for testing.

1. Emulate PMEM with memmap=nn[KMG]!ss[KMG]. This can be used only on
Linux. Please see [1][2] for details; or
2. Set the environment variable PMEM_IS_PMEM_FORCE=1 to tell libpmem
to treat any devices as if they were PMEM.


Regards,
Takashi


[1] https://www.intel.com/content/www/us/en/developer/articles/training/how-to-emulate-persistent-memory...
[2] https://nvdimm.wiki.kernel.org/

-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (15 files, .tar.gz)

v7-0004-Let-wal_pmem_map-be-constant-unless-with-libpmem.patch (application/octet-stream, 1.4K) download From 271860d2450411e056de4fa17f3f812effaaedd3 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 13 Oct 2021 11:10:17 +0900 Subject: [PATCH v7 04/15] Let wal_pmem_map be constant unless --with-libpmem This may melt into "Add wal_pmem_map to GUC". --- src/backend/access/transam/xlog.c | 3 +++ src/include/access/xlog.h | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 02f63c3138..b3f08cdaec 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -115,7 +115,10 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; + +#ifdef USE_LIBPMEM bool wal_pmem_map = false; +#endif #ifdef WAL_DEBUG bool XLOG_DEBUG = false; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 73900cbc9e..ab3eb3887b 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -88,7 +88,12 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; + +#ifdef USE_LIBPMEM extern bool wal_pmem_map; +#else +#define wal_pmem_map false +#endif /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v7-0002-Support-build-with-MSVC-on-Windows.patch (application/octet-stream, 2.9K) download From f79bcfa46a097dc46bef56195d892b6c32bca7f3 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:33 +0900 Subject: [PATCH v7 02/15] Support build with MSVC on Windows This may melt into "Add --with-libpmem option for PMEM support". --- src/tools/msvc/Solution.pm | 13 +++++++++++++ src/tools/msvc/config_default.pl | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index 2c8cd521e9..d843391050 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -304,6 +304,7 @@ sub GenerateFiles HAVE_LIBLZ4 => undef, HAVE_LIBM => undef, HAVE_LIBPAM => undef, + HAVE_LIBPMEM => undef, HAVE_LIBREADLINE => undef, HAVE_LIBSELINUX => undef, HAVE_LIBSSL => undef, @@ -488,6 +489,7 @@ sub GenerateFiles USE_BONJOUR => undef, USE_BSD_AUTH => undef, USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBPMEM => undef, USE_LIBXML => undef, USE_LIBXSLT => undef, USE_LZ4 => undef, @@ -538,6 +540,11 @@ sub GenerateFiles $define{HAVE_LZ4_H} = 1; $define{USE_LZ4} = 1; } + if ($self->{options}->{pmem}) + { + $define{HAVE_LIBPMEM} = 1; + $define{USE_LIBPMEM} = 1; + } if ($self->{options}->{openssl}) { $define{USE_OPENSSL} = 1; @@ -1085,6 +1092,11 @@ sub AddProject $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); } + if ($self->{options}->{pmem}) + { + $proj->AddIncludeDir($self->{options}->{pmem} . '\include'); + $proj->AddLibrary($self->{options}->{pmem} . '\lib\libpmem.lib'); + } return $proj; } @@ -1197,6 +1209,7 @@ sub GetFakeConfigure $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); $cfg .= ' --with-perl' if ($self->{options}->{perl}); $cfg .= ' --with-python' if ($self->{options}->{python}); + $cfg .= ' --with-libpmem' if ($self->{options}->{pmem}); my $port = $self->{options}->{'--with-pgport'}; $cfg .= " --with-pgport=$port" if defined($port); diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl index 460c0375d4..774730c9a8 100644 --- a/src/tools/msvc/config_default.pl +++ b/src/tools/msvc/config_default.pl @@ -25,7 +25,8 @@ our $config = { xml => undef, # --with-libxml=<path> xslt => undef, # --with-libxslt=<path> iconv => undef, # (not in configure, path to iconv) - zlib => undef # --with-zlib=<path> + zlib => undef, # --with-zlib=<path> + pmem => undef # --with-libpmem=<path> }; 1; -- 2.25.1
v7-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 5.3K) download From 6d75f3d8d9e87bef1257947f0918bdd3a85cb43f Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v7 01/15] Add --with-libpmem option for PMEM support --- configure | 99 ++++++++++++++++++++++++++++++++++++++ configure.ac | 17 +++++++ src/include/pg_config.h.in | 6 +++ 3 files changed, 122 insertions(+) diff --git a/configure b/configure index 3b19105328..22c364fac4 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -9033,6 +9036,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13504,6 +13542,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -14215,6 +14303,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index e77d4dcf2d..2bea131375 100644 --- a/configure.ac +++ b/configure.ac @@ -1056,6 +1056,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1385,6 +1394,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1571,6 +1584,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 7525c16597..96604aa130 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -331,6 +331,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -898,6 +901,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML -- 2.25.1
v7-0005-Comment-for-constant-wal_pmem_map.patch (application/octet-stream, 989B) download From 41feb8927ee1b121dc46038fcf80e5c26c08b3d6 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 7 Jan 2022 11:06:46 +0900 Subject: [PATCH v7 05/15] Comment for constant wal_pmem_map This may melt into "Let wal_pmem_map be constant unless --with-libpmem". --- src/include/access/xlog.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index ab3eb3887b..eb0987313e 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -92,6 +92,12 @@ extern bool track_wal_io_timing; #ifdef USE_LIBPMEM extern bool wal_pmem_map; #else +/* + * If USE_LIBPMEM is not defined (that is, no --with-libpmem), wal_pmem_map + * is always false and is never used essentially. Using #if(n)def everywhere + * is not good for code readability, so let wal_pmem_map be constant. This + * may help compilers optimize conditional branches. + */ #define wal_pmem_map false #endif -- 2.25.1
v7-0003-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 4.7K) download From 5ef8dff0a5dfc07c482c519925ae45c9559c48bd Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v7 03/15] Add wal_pmem_map to GUC --- src/backend/access/transam/xlog.c | 51 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 +++++++++ src/include/access/xlog.h | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 87cd05c945..02f63c3138 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -115,6 +115,7 @@ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +bool wal_pmem_map = false; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -5194,13 +5195,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5216,10 +5232,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5313,13 +5336,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index f9504d3aec..ee18a9cf33 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1344,6 +1344,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 34f6c89f06..73900cbc9e 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -88,6 +88,7 @@ extern char *PrimaryConnInfo; extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +extern bool wal_pmem_map; /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; -- 2.25.1
v7-0006-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From c1713880c720052eeb2850abdbe5b07382b23132 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v7 06/15] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 5 +---- src/include/access/xlog_internal.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index b3f08cdaec..ee000bf181 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -926,9 +926,6 @@ static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno, - TimeLineID tli); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3703,7 +3700,7 @@ XLogFileCopy(TimeLineID destTLI, XLogSegNo destsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, TimeLineID tli) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index c0da76cab4..328128b48d 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -324,6 +324,10 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno, + TimeLineID tli); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v7-0008-Fix-invalid-declaration-of-PmemXLogEnsurePrevMapp.patch (application/octet-stream, 892B) download From 8cbaa4c049e417eca383c1ba5a50ce1e0d84196e Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 6 Jan 2022 15:11:55 +0900 Subject: [PATCH v7 08/15] Fix invalid declaration of PmemXLogEnsurePrevMapped This lets build pass when without --with-libpmem option. This may melt into "Map WAL segment files on PMEM as WAL buffers". --- src/include/access/xlogpmem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h index 3978640b82..cd83bffc88 100644 --- a/src/include/access/xlogpmem.h +++ b/src/include/access/xlogpmem.h @@ -29,7 +29,7 @@ extern void PmemXLogSync(void); #include <stdlib.h> /* abort */ static inline XLogSegNo -PmemXLogEnsurePrevMapped(XLogRecPtr ptr, tli) +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) { abort(); return 0; -- 2.25.1
v7-0007-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 17.3K) download From 68382d9b7c2734f42679d069f892d2e055265ee6 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v7 07/15] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 153 +++++++++---- src/backend/access/transam/xlogpmem.c | 297 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 474 insertions(+), 36 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index ee000bf181..62f08cb50b 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -2027,7 +2028,14 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2261,7 +2269,14 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2480,6 +2495,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2502,6 +2519,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2527,33 +2548,36 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo, tli); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo, tli); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; - openLogFile = XLogFileOpen(openLogSegNo, tli); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; + openLogFile = XLogFileOpen(openLogSegNo, tli); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2561,8 +2585,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2600,7 +2624,38 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr, tli); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2658,7 +2713,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo, tli); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo, tli); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2709,12 +2767,14 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -8102,11 +8162,32 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + * + * TODO how about if (newTLI != replayTLI) ? + */ + openLogTLI = newTLI; + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..5b50ba80a7 --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,297 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +#include <libpmem.h> + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno, TimeLineID tli); +static void PmemXLogCreate(XLogSegNo segno, TimeLineID tli); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno, tli); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno, TimeLineID tli) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, tli, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno, tli); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno, TimeLineID tli) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno, tli)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path))); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..3978640b82 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, tli) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v7-0009-Remove-redundant-parentheses-from-ereport-call.patch (application/octet-stream, 983B) download From b8ec84cc5427814e5b52c2c89f0fd939f958ad0f Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 6 Jan 2022 15:31:40 +0900 Subject: [PATCH v7 09/15] Remove redundant parentheses from ereport call This may melt into "Map WAL segment files on PMEM as WAL buffers". --- src/backend/access/transam/xlogpmem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 5b50ba80a7..73adde190c 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -250,8 +250,8 @@ PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) return NULL; ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not pmem_map_file \"%s\": %m", path))); + errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path)); } if (mapped_len > INT_MAX) -- 2.25.1
v7-0011-Fix-typo-in-comment.patch (application/octet-stream, 1016B) download From d9cbdc88c3817f5d9ceefc80c1c56f18519f225d Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 6 Jan 2022 15:18:46 +0900 Subject: [PATCH v7 11/15] Fix typo in comment This may melt into "Ensure WAL mappings before assertion". --- src/backend/access/transam/xlog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f0d7a317d2..fa0c605ff7 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1948,7 +1948,7 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) /* * Ensure WAL mappings before assersion. * - * cachedPos should be recaluculated because it has been probably + * cachedPos should be recalculated because it has been probably * invalidated due to WAL remapping. This should be done even if * openLogSegNo seems not to change because the address of the * mapping could have changed (ABA problem). -- 2.25.1
v7-0013-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From 76847dc47c0dd861681b53a0e02270fcd4bd84a8 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v7 13/15] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 95a5920a8f..f63d00f302 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -195,6 +195,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -203,22 +204,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v7-0012-Compatible-to-Windows.patch (application/octet-stream, 1.6K) download From b8bf06b8985ea8b93c8300f6035e1f43c1cf3052 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Fri, 5 Nov 2021 14:16:25 +0900 Subject: [PATCH v7 12/15] Compatible to Windows This may melt into "Map WAL segment files on PMEM as WAL buffers". --- src/backend/access/transam/xlogpmem.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 73adde190c..95a5920a8f 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -8,7 +8,24 @@ #include <stdint.h> /* uintptr_t */ #include <unistd.h> /* getpid, unlink */ +/* + * On Windows, we will have two ported but conflicting mode_t: + * + * mode_t in libpmem: + * libpmem.h -> pmemcompat.h -> typedef int mode_t + * mode_t in PostgreSQL: + * c.h -> port.h -> win32_port.h -> typedef unsigned short mode_t + * + * We want to use PostgreSQL's one, so conseal libpmem's one. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +#define mode_t unused_libpmem_mode_t #include <libpmem.h> +#undef mode_t +/* On other platforms, simply include libpmem.h */ +#else +#include <libpmem.h> +#endif #include "c.h" /* bool, Size */ #include "access/xlog.h" @@ -242,7 +259,11 @@ PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) mapped_len = 0; is_pmem = 0; +#if defined(WIN32) && !defined(__CYGWIN__) + addr = pmem_map_fileU(path, param_len, flags, mode, &mapped_len, &is_pmem); +#else addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); +#endif if (addr == NULL) { -- 2.25.1
v7-0010-Ensure-WAL-mappings-before-assertion.patch (application/octet-stream, 1.5K) download From df3b3a74e3aa25515af806aaf13402d98eb4ead7 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 28 Oct 2021 13:35:28 +0900 Subject: [PATCH v7 10/15] Ensure WAL mappings before assertion This may melt into "Map WAL segment files on PMEM as WAL buffers". --- src/backend/access/transam/xlog.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 62f08cb50b..f0d7a317d2 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1945,6 +1945,23 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) */ if (ptr / XLOG_BLCKSZ == cachedPage) { + /* + * Ensure WAL mappings before assersion. + * + * cachedPos should be recaluculated because it has been probably + * invalidated due to WAL remapping. This should be done even if + * openLogSegNo seems not to change because the address of the + * mapping could have changed (ABA problem). + */ + if (wal_pmem_map) + { + endptr = ptr - ptr % XLOG_BLCKSZ + XLOG_BLCKSZ; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); + } + Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); return cachedPos + ptr % XLOG_BLCKSZ; -- 2.25.1
v7-0014-Update-document.patch (application/octet-stream, 2.0K) download From c2ea9e7038b0f2149a63839e084add485c625312 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 30 Sep 2021 12:51:56 +0900 Subject: [PATCH v7 14/15] Update document --- doc/src/sgml/config.sgml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index afbb6c35e3..0fc3b48735 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3239,6 +3239,33 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-wal-pmem-map" xreflabel="wal_pmem_map"> + <term><varname>wal_pmem_map</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>wal_pmem_map</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + If set to <literal>on</literal>, this parameter causes WAL files to be + memory-mapped and used as WAL buffer pages. The WAL files in pg_wal + directory (or the directory given by <command>initdb -X</command> + option) should be on <firstterm>persistent memory</firstterm> (PMEM) and + the filesystem for those files should support the <firstterm>Direct + Access</firstterm> (DAX) feature. <varname>wal_sync_method</varname> + for the primary server is ignored and WAL updates are forced out to + PMEM in a more optimal way which avoids calling into the kernel. + <varname>min_wal_size</varname> should be multiple of the size of a + WAL file, and <varname>wal_buffers</varname> is ignored and set to + the equivalent of <varname>min_wal_size</varname>. + </para> + <para> + This parameter can only be set at server start. The default is + <literal>off</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-writer-delay" xreflabel="wal_writer_delay"> <term><varname>wal_writer_delay</varname> (<type>integer</type>) <indexterm> -- 2.25.1
v7-0015-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 3.6K) download From c9b1413d7fea17f9bc649bc6b3d76848b0854ae2 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v7 15/15] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index fa0c605ff7..6899c5740c 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2049,7 +2049,9 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2290,7 +2292,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5275,15 +5279,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -8189,7 +8203,8 @@ StartupXLOG(void) */ openLogTLI = newTLI; openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8229,7 +8244,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog, newTLI); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, newTLI, true); + else + PreallocXlogFiles(EndOfLog, newTLI); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ee18a9cf33..6f667d5c43 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2866,7 +2866,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-07 04:43  Justin Pryzby <pryzby@telsasoft.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Justin Pryzby @ 2022-01-07 04:43 UTC (permalink / raw)
  To: Takashi Menjo <takashi.menjo@gmail.com>; +Cc: Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers@postgresql.org, Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

On Fri, Jan 07, 2022 at 12:50:01PM +0900, Takashi Menjo wrote:
> > But in this case it really doesn't work :(
> >
> > running bootstrap script ... 2022-01-05 23:17:30.244 CST [12088] FATAL:  file not on PMEM: path "pg_wal/000000010000000000000001"
> 
> Do you have a real PMEM device such as NVDIMM-N or Intel Optane PMem?

No - the point is that we'd like to have a way to exercise this patch on the
cfbot.  Particularly the new code introduced by this patch, not just the
--without-pmem case...

I was able to make this pass "make check" by adding this to main() in
src/backend/main/main.c:
| setenv("PMEM_IS_PMEM_FORCE", "1", 0);

I think you should add a patch which does what Thomas suggested: 1) add to
./.cirrus.yaml installation of the libpmem package for debian/bsd/mac/windows;
2) add setenv to main(), as above; 3) change configure.ac and guc.c to default
to --with-libpmem and wal_pmem_map=on.  This should be the last patch, for
cfbot only, not meant to be merged.

You can test that the package installation part works before mailing patches to
the list with the instructions here:

src/tools/ci/README:
Enabling cirrus-ci in a github repository..

> If you don't, you have two alternatives below. Note that neither of
> them ensures durability. Each of them is just for testing.
> 2. Set the environment variable PMEM_IS_PMEM_FORCE=1 to tell libpmem
> to treat any devices as if they were PMEM.

The next revision should surely squish all the fixes into their corresponding
patches to be fixed.  Each of the patches ought to be compile and pass tests
without depending on the "following" patches:  0001 without 0002-, 0001-0002
without 0003-, etc.

-- 
Justin





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-17 07:34  Justin Pryzby <pryzby@telsasoft.com>
  parent: Justin Pryzby <pryzby@telsasoft.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Justin Pryzby @ 2022-01-17 07:34 UTC (permalink / raw)
  To: Takashi Menjo <takashi.menjo@gmail.com>; +Cc: Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers@postgresql.org, Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

On Thu, Jan 06, 2022 at 10:43:37PM -0600, Justin Pryzby wrote:
> On Fri, Jan 07, 2022 at 12:50:01PM +0900, Takashi Menjo wrote:
> > > But in this case it really doesn't work :(
> > >
> > > running bootstrap script ... 2022-01-05 23:17:30.244 CST [12088] FATAL:  file not on PMEM: path "pg_wal/000000010000000000000001"
> > 
> > Do you have a real PMEM device such as NVDIMM-N or Intel Optane PMem?
> 
> No - the point is that we'd like to have a way to exercise this patch on the
> cfbot.  Particularly the new code introduced by this patch, not just the
> --without-pmem case...
..
> I think you should add a patch which does what Thomas suggested: 1) add to
> ./.cirrus.yaml installation of the libpmem package for debian/bsd/mac/windows;
> 2) add setenv to main(), as above; 3) change configure.ac and guc.c to default
> to --with-libpmem and wal_pmem_map=on.  This should be the last patch, for
> cfbot only, not meant to be merged.

I was able to get the cirrus CI to compile on linux and bsd with the below
changes.  I don't know if there's an easy package installation for mac OSX.  I
think it's okay if mac CI doesn't use --enable-pmem for now.

> You can test that the package installation part works before mailing patches to
> the list with the instructions here:
> 
> src/tools/ci/README:
> Enabling cirrus-ci in a github repository..

I ran the CI under my own github account.
Linux crashes in the recovery check.
And freebsd has been stuck for 45min.

I'm not sure, but maybe those are legimate consequence of using
PMEM_IS_PMEM_FORCE (?)  If so, maybe the recovery check would need to be
disabled for this patch to run on CI...  Or maybe my suggestion to enable it by
default for CI doesn't work for this patch.  It would need to be specially
tested with real hardware.

https://cirrus-ci.com/task/6245151591890944

https://cirrus-ci.com/task/6162551485497344?logs=test_world#L3941
#2  0x000055ff43c6edad in ExceptionalCondition (conditionName=0x55ff43d18108 "!XLogRecPtrIsInvalid(missingContrecPtr)", errorType=0x55ff43d151c4 "FailedAssertion", fileName=0x55ff43d151bd "xlog.c", lineNumber=8297) at assert.c:69

commit 15533794e465a381eb23634d67700afa809a0210
Author: Justin Pryzby <pryzbyj@telsasoft.com>
Date:   Thu Jan 6 22:53:28 2022 -0600

    tmp: enable pmem by default, for CI

inline patch 1: .cirrus.yml, src/backend/main/main.c, src/backend/utils/misc/guc.c (+10/-1 lines) download inline-1.patchdiff --git a/.cirrus.yml b/.cirrus.yml index 677bdf0e65e..0cb961c8103 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -81,6 +81,7 @@ task: mkdir -m 770 /tmp/cores chown root:postgres /tmp/cores sysctl kern.corefile='/tmp/cores/%N.%P.core' + pkg install -y devel/pmdk # NB: Intentionally build without --with-llvm. The freebsd image size is # already large enough to make VM startup slow, and even without llvm @@ -99,6 +100,7 @@ task: --with-lz4 \ --with-pam \ --with-perl \ + --with-libpmem \ --with-python \ --with-ssl=openssl \ --with-tcl --with-tclconfig=/usr/local/lib/tcl8.6/ \ @@ -138,6 +140,7 @@ LINUX_CONFIGURE_FEATURES: &LINUX_CONFIGURE_FEATURES >- --with-lz4 --with-pam --with-perl + --with-libpmem --with-python --with-selinux --with-ssl=openssl @@ -188,6 +191,9 @@ task: mkdir -m 770 /tmp/cores chown root:postgres /tmp/cores sysctl kernel.core_pattern='/tmp/cores/%e-%s-%p.core' + echo 'deb http://deb.debian.org/debian bullseye universe' >>/etc/apt/sources.list + apt-get update + apt-get -y install libpmem-dev configure_script: | su postgres <<-EOF @@ -267,6 +273,7 @@ task: make \ openldap \ openssl \ + pmem \ python \ tcl-tk @@ -301,6 +308,7 @@ task: --with-libxslt \ --with-lz4 \ --with-perl \ + --with-libpmem \ --with-python \ --with-ssl=openssl \ --with-tcl --with-tclconfig=${brewpath}/opt/tcl-tk/lib/ \ diff --git a/src/backend/main/main.c b/src/backend/main/main.c index 9124060bde7..b814269675d 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -69,6 +69,7 @@ main(int argc, char *argv[]) #endif progname = get_progname(argv[0]); + setenv("PMEM_IS_PMEM_FORCE", "1", 0); /* * Platform-specific startup hacks diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ffc55f33e86..32d650cb9b2 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1354,7 +1354,7 @@ static struct config_bool ConfigureNamesBool[] = "traditional volatile ones."), }, &wal_pmem_map, - false, + true, NULL, NULL, NULL }, #endif
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-18 10:58  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Justin Pryzby <pryzby@telsasoft.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2022-01-18 10:58 UTC (permalink / raw)
  To: Justin Pryzby <pryzby@telsasoft.com>; +Cc: Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

Hi Justin,

Thanks for your help. I'm making an additional patch for Cirrus CI.

I'm also trying to reproduce the "make check-world" error you
reported, on my Linux environment that has neither a real PMem nor an
emulated one, with PMEM_IS_PMEM_FORCE=1. I'll keep you updated.

Regards,
Takashi

On Mon, Jan 17, 2022 at 4:34 PM Justin Pryzby <pryzby@telsasoft.com> wrote:
>
> On Thu, Jan 06, 2022 at 10:43:37PM -0600, Justin Pryzby wrote:
> > On Fri, Jan 07, 2022 at 12:50:01PM +0900, Takashi Menjo wrote:
> > > > But in this case it really doesn't work :(
> > > >
> > > > running bootstrap script ... 2022-01-05 23:17:30.244 CST [12088] FATAL:  file not on PMEM: path "pg_wal/000000010000000000000001"
> > >
> > > Do you have a real PMEM device such as NVDIMM-N or Intel Optane PMem?
> >
> > No - the point is that we'd like to have a way to exercise this patch on the
> > cfbot.  Particularly the new code introduced by this patch, not just the
> > --without-pmem case...
> ..
> > I think you should add a patch which does what Thomas suggested: 1) add to
> > ./.cirrus.yaml installation of the libpmem package for debian/bsd/mac/windows;
> > 2) add setenv to main(), as above; 3) change configure.ac and guc.c to default
> > to --with-libpmem and wal_pmem_map=on.  This should be the last patch, for
> > cfbot only, not meant to be merged.
>
> I was able to get the cirrus CI to compile on linux and bsd with the below
> changes.  I don't know if there's an easy package installation for mac OSX.  I
> think it's okay if mac CI doesn't use --enable-pmem for now.
>
> > You can test that the package installation part works before mailing patches to
> > the list with the instructions here:
> >
> > src/tools/ci/README:
> > Enabling cirrus-ci in a github repository..
>
> I ran the CI under my own github account.
> Linux crashes in the recovery check.
> And freebsd has been stuck for 45min.
>
> I'm not sure, but maybe those are legimate consequence of using
> PMEM_IS_PMEM_FORCE (?)  If so, maybe the recovery check would need to be
> disabled for this patch to run on CI...  Or maybe my suggestion to enable it by
> default for CI doesn't work for this patch.  It would need to be specially
> tested with real hardware.
>
> https://cirrus-ci.com/task/6245151591890944
>
> https://cirrus-ci.com/task/6162551485497344?logs=test_world#L3941
> #2  0x000055ff43c6edad in ExceptionalCondition (conditionName=0x55ff43d18108 "!XLogRecPtrIsInvalid(missingContrecPtr)", errorType=0x55ff43d151c4 "FailedAssertion", fileName=0x55ff43d151bd "xlog.c", lineNumber=8297) at assert.c:69
>
> commit 15533794e465a381eb23634d67700afa809a0210
> Author: Justin Pryzby <pryzbyj@telsasoft.com>
> Date:   Thu Jan 6 22:53:28 2022 -0600
>
>     tmp: enable pmem by default, for CI
>
> diff --git a/.cirrus.yml b/.cirrus.yml
> index 677bdf0e65e..0cb961c8103 100644
> --- a/.cirrus.yml
> +++ b/.cirrus.yml
> @@ -81,6 +81,7 @@ task:
>      mkdir -m 770 /tmp/cores
>      chown root:postgres /tmp/cores
>      sysctl kern.corefile='/tmp/cores/%N.%P.core'
> +    pkg install -y devel/pmdk
>
>    # NB: Intentionally build without --with-llvm. The freebsd image size is
>    # already large enough to make VM startup slow, and even without llvm
> @@ -99,6 +100,7 @@ task:
>          --with-lz4 \
>          --with-pam \
>          --with-perl \
> +        --with-libpmem \
>          --with-python \
>          --with-ssl=openssl \
>          --with-tcl --with-tclconfig=/usr/local/lib/tcl8.6/ \
> @@ -138,6 +140,7 @@ LINUX_CONFIGURE_FEATURES: &LINUX_CONFIGURE_FEATURES >-
>    --with-lz4
>    --with-pam
>    --with-perl
> +  --with-libpmem
>    --with-python
>    --with-selinux
>    --with-ssl=openssl
> @@ -188,6 +191,9 @@ task:
>      mkdir -m 770 /tmp/cores
>      chown root:postgres /tmp/cores
>      sysctl kernel.core_pattern='/tmp/cores/%e-%s-%p.core'
> +    echo 'deb http://deb.debian.org/debian bullseye universe' >>/etc/apt/sources.list
> +    apt-get update
> +    apt-get -y install libpmem-dev
>
>    configure_script: |
>      su postgres <<-EOF
> @@ -267,6 +273,7 @@ task:
>        make \
>        openldap \
>        openssl \
> +      pmem \
>        python \
>        tcl-tk
>
> @@ -301,6 +308,7 @@ task:
>        --with-libxslt \
>        --with-lz4 \
>        --with-perl \
> +      --with-libpmem \
>        --with-python \
>        --with-ssl=openssl \
>        --with-tcl --with-tclconfig=${brewpath}/opt/tcl-tk/lib/ \
> diff --git a/src/backend/main/main.c b/src/backend/main/main.c
> index 9124060bde7..b814269675d 100644
> --- a/src/backend/main/main.c
> +++ b/src/backend/main/main.c
> @@ -69,6 +69,7 @@ main(int argc, char *argv[])
>  #endif
>
>         progname = get_progname(argv[0]);
> +       setenv("PMEM_IS_PMEM_FORCE", "1", 0);
>
>         /*
>          * Platform-specific startup hacks
> diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
> index ffc55f33e86..32d650cb9b2 100644
> --- a/src/backend/utils/misc/guc.c
> +++ b/src/backend/utils/misc/guc.c
> @@ -1354,7 +1354,7 @@ static struct config_bool ConfigureNamesBool[] =
>                                                  "traditional volatile ones."),
>                 },
>                 &wal_pmem_map,
> -               false,
> +               true,
>                 NULL, NULL, NULL
>         },
>  #endif



-- 
Takashi Menjo <takashi.menjo@gmail.com>





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-19 04:41  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2022-01-19 04:41 UTC (permalink / raw)
  To: Justin Pryzby <pryzby@telsasoft.com>; +Cc: Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

Hi Justin,

I can reproduce the error you reported, with PMEM_IS_PMEM_FORCE=1.

Moreover, I can reproduce it **on a real PMem device**. So the causes
are in my patchset, not in PMem environment.

I'll fix it in the next patchset version.

Regards,
Takashi

--
Takashi Menjo <takashi.menjo@gmail.com>





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-01-20 05:55  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2022-01-20 05:55 UTC (permalink / raw)
  To: Justin Pryzby <pryzby@telsasoft.com>; +Cc: Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

Hi Justin,

Here is patchset v8. It will have "make check-world" and Cirrus to
pass. Would you try this one?

The v8 squashes some patches in v7 into related ones, and adds the
following patches:

- v8-0003: Add wal_pmem_map to postgresql.conf.sample. It also helps v8-0011.

- v8-0009: Fix wrong handling of missingContrecPtr for
test/recovery/t/026 to pass. It is the cause of the error. Thanks for
your report.

- v8-0010 and v8-0011: Each of the two is for CI only. v8-0010 adds
--with-libpmem and v8-0011 enables "wal_pmem_map = on". Please note
that, unlike your suggestion, in my patchset PMEM_IS_PMEM_FORCE=1 will
be given as an environment variable in .cirrus.yml and "wal_pmem_map =
on" will be given by initdb.

Regards,
Takashi

-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (11 files, .tar.gz)

v8-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 8.0K) download From d13367462e761a55be1a984b6b8744676bf85dfb Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v8 01/11] Add --with-libpmem option for PMEM support Fixes introduced in patchset v8: - Support build with MSVC on Windows --- configure | 99 ++++++++++++++++++++++++++++++++ configure.ac | 17 ++++++ src/include/pg_config.h.in | 6 ++ src/tools/msvc/Solution.pm | 13 +++++ src/tools/msvc/config_default.pl | 3 +- 5 files changed, 137 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 3f2aea0d7d..3529005e9b 100755 --- a/configure +++ b/configure @@ -699,6 +699,7 @@ with_gnu_ld LD LDFLAGS_SL LDFLAGS_EX +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -868,6 +869,7 @@ with_libxslt with_system_tzdata with_zlib with_lz4 +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1576,6 +1578,7 @@ Optional Packages: use system time zone data in DIR --without-zlib do not use Zlib --with-lz4 build with LZ4 support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -9033,6 +9036,41 @@ fi done fi +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13504,6 +13542,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -14215,6 +14303,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index 95287705f6..a7e3fc8a3b 100644 --- a/configure.ac +++ b/configure.ac @@ -1056,6 +1056,15 @@ if test "$with_lz4" = yes; then done fi +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1385,6 +1394,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1572,6 +1585,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 9d9bd6b9ef..2b40b208bc 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -331,6 +331,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -901,6 +904,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index e47c2d648c..3be3332799 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -304,6 +304,7 @@ sub GenerateFiles HAVE_LIBLZ4 => undef, HAVE_LIBM => undef, HAVE_LIBPAM => undef, + HAVE_LIBPMEM => undef, HAVE_LIBREADLINE => undef, HAVE_LIBSELINUX => undef, HAVE_LIBSSL => undef, @@ -489,6 +490,7 @@ sub GenerateFiles USE_BONJOUR => undef, USE_BSD_AUTH => undef, USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBPMEM => undef, USE_LIBXML => undef, USE_LIBXSLT => undef, USE_LZ4 => undef, @@ -539,6 +541,11 @@ sub GenerateFiles $define{HAVE_LZ4_H} = 1; $define{USE_LZ4} = 1; } + if ($self->{options}->{pmem}) + { + $define{HAVE_LIBPMEM} = 1; + $define{USE_LIBPMEM} = 1; + } if ($self->{options}->{openssl}) { $define{USE_OPENSSL} = 1; @@ -1086,6 +1093,11 @@ sub AddProject $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); } + if ($self->{options}->{pmem}) + { + $proj->AddIncludeDir($self->{options}->{pmem} . '\include'); + $proj->AddLibrary($self->{options}->{pmem} . '\lib\libpmem.lib'); + } return $proj; } @@ -1198,6 +1210,7 @@ sub GetFakeConfigure $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); $cfg .= ' --with-perl' if ($self->{options}->{perl}); $cfg .= ' --with-python' if ($self->{options}->{python}); + $cfg .= ' --with-libpmem' if ($self->{options}->{pmem}); my $port = $self->{options}->{'--with-pgport'}; $cfg .= " --with-pgport=$port" if defined($port); diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl index 7a9b00be72..7b887bc68f 100644 --- a/src/tools/msvc/config_default.pl +++ b/src/tools/msvc/config_default.pl @@ -25,7 +25,8 @@ our $config = { xml => undef, # --with-libxml=<path> xslt => undef, # --with-libxslt=<path> iconv => undef, # (not in configure, path to iconv) - zlib => undef # --with-zlib=<path> + zlib => undef, # --with-zlib=<path> + pmem => undef # --with-libpmem=<path> }; 1; -- 2.25.1
v8-0002-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 5.1K) download From dbed72b389fcff57e6a60e4a9e508d7a9360a45e Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v8 02/11] Add wal_pmem_map to GUC Fixes introduced in patchset v8: - Let wal_pmem_map be constant unless --with-libpmem - Comment for constant wal_pmem_map --- src/backend/access/transam/xlog.c | 54 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 ++++++++ src/include/access/xlog.h | 12 +++++++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index c9d4cbf3ff..63cf5dd9b9 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -116,6 +116,10 @@ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +#ifdef USE_LIBPMEM +bool wal_pmem_map = false; +#endif + #ifdef WAL_DEBUG bool XLOG_DEBUG = false; #endif @@ -5194,13 +5198,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -5216,10 +5235,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -5313,13 +5339,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 4c94f09c64..c29f46bebb 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1345,6 +1345,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index bb0c52686a..5603a714d0 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -89,6 +89,18 @@ extern char *PrimarySlotName; extern bool wal_receiver_create_temp_slot; extern bool track_wal_io_timing; +#ifdef USE_LIBPMEM +extern bool wal_pmem_map; +#else +/* + * If USE_LIBPMEM is not defined (that is, no --with-libpmem), wal_pmem_map + * is always false and is never used essentially. Using #if(n)def everywhere + * is not good for code readability, so let wal_pmem_map be constant. This + * may help compilers optimize conditional branches. + */ +#define wal_pmem_map false +#endif + /* indirectly set via GUC system */ extern TransactionId recoveryTargetXid; extern char *recovery_target_time_string; -- 2.25.1
v8-0005-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 19.0K) download From fee42ebdeb7d9db9381dfa570b9952c72d8a4e7f Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v8 05/11] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset Fixes introduced in patchset v8: - Fix invalid declaration of PmemXLogEnsurePrevMapped - Remove redundant parentheses from ereport call - Ensure WAL mappings before assertion - Fix typo in comment - Compatible to Windows --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 170 +++++++++++--- src/backend/access/transam/xlogpmem.c | 318 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 512 insertions(+), 36 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 595e02de72..3a29583bc0 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index fcb1dba680..9484152806 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -35,6 +35,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogutils.h" #include "catalog/catversion.h" @@ -1944,6 +1945,23 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) */ if (ptr / XLOG_BLCKSZ == cachedPage) { + /* + * Ensure WAL mappings before assersion. + * + * cachedPos should be recalculated because it has been probably + * invalidated due to WAL remapping. This should be done even if + * openLogSegNo seems not to change because the address of the + * mapping could have changed (ABA problem). + */ + if (wal_pmem_map) + { + endptr = ptr - ptr % XLOG_BLCKSZ + XLOG_BLCKSZ; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); + } + Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); return cachedPos + ptr % XLOG_BLCKSZ; @@ -2027,7 +2045,14 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -2261,7 +2286,14 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2480,6 +2512,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2502,6 +2536,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2527,33 +2565,36 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo, tli); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo, tli); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; - openLogFile = XLogFileOpen(openLogSegNo, tli); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; + openLogFile = XLogFileOpen(openLogSegNo, tli); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2561,8 +2602,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2600,7 +2641,38 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr, tli); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2658,7 +2730,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo, tli); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo, tli); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2709,12 +2784,14 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -8102,11 +8179,32 @@ StartupXLOG(void) firstIdx = XLogRecPtrToBufIdx(EndOfLog); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - len = EndOfLog % XLOG_BLCKSZ; - memcpy(page, xlogreader->readBuf, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + * + * TODO how about if (newTLI != replayTLI) ? + */ + openLogTLI = newTLI; + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + len = EndOfLog % XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, pageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + len = EndOfLog % XLOG_BLCKSZ; + memcpy(page, xlogreader->readBuf, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = pageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = pageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..95a5920a8f --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,318 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +/* + * On Windows, we will have two ported but conflicting mode_t: + * + * mode_t in libpmem: + * libpmem.h -> pmemcompat.h -> typedef int mode_t + * mode_t in PostgreSQL: + * c.h -> port.h -> win32_port.h -> typedef unsigned short mode_t + * + * We want to use PostgreSQL's one, so conseal libpmem's one. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +#define mode_t unused_libpmem_mode_t +#include <libpmem.h> +#undef mode_t +/* On other platforms, simply include libpmem.h */ +#else +#include <libpmem.h> +#endif + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno, TimeLineID tli); +static void PmemXLogCreate(XLogSegNo segno, TimeLineID tli); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno, tli); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno, TimeLineID tli) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, tli, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno, tli); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno, TimeLineID tli) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno, tli)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; +#if defined(WIN32) && !defined(__CYGWIN__) + addr = pmem_map_fileU(path, param_len, flags, mode, &mapped_len, &is_pmem); +#else + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); +#endif + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path)); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..cd83bffc88 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v8-0003-Add-wal_pmem_map-to-postgresql.conf.sample.patch (application/octet-stream, 1.0K) download From 09ee412f16a2699c0fa9a4b653453dfecd18e034 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 20 Jan 2022 13:04:42 +0900 Subject: [PATCH v8 03/11] Add wal_pmem_map to postgresql.conf.sample This may melt into "Add wal_pmem_map to GUC". --- src/backend/utils/misc/postgresql.conf.sample | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 817d5f5324..59608d8d8d 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -225,6 +225,8 @@ #wal_recycle = on # recycle WAL files #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers # (change requires restart) +#wal_pmem_map = off # use WAL segment files as WAL buffers + # wal_buffers will be ignored #wal_writer_delay = 200ms # 1-10000 milliseconds #wal_writer_flush_after = 1MB # measured in pages, 0 disables #wal_skip_threshold = 2MB -- 2.25.1
v8-0004-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From d15aa62ea1ad46802ac15dc5171ece47cf64c826 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v8 04/11] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 5 +---- src/include/access/xlog_internal.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 63cf5dd9b9..fcb1dba680 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -926,9 +926,6 @@ static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic); static bool XLogCheckpointNeeded(XLogSegNo new_segno); static void XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno, - TimeLineID tli); static int XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli, XLogSource source, bool notfoundOk); static int XLogFileReadAnyTLI(XLogSegNo segno, int emode, XLogSource source); @@ -3703,7 +3700,7 @@ XLogFileCopy(TimeLineID destTLI, XLogSegNo destsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, TimeLineID tli) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index e27fca0cc0..b6cb8486a4 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -324,6 +324,10 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno, + TimeLineID tli); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v8-0007-Update-document.patch (application/octet-stream, 2.0K) download From e5ad5fb6fdb108d8914cd771787fd6f66c097ab1 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 30 Sep 2021 12:51:56 +0900 Subject: [PATCH v8 07/11] Update document --- doc/src/sgml/config.sgml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 4cd9818acf..21226c581f 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3239,6 +3239,33 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-wal-pmem-map" xreflabel="wal_pmem_map"> + <term><varname>wal_pmem_map</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>wal_pmem_map</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + If set to <literal>on</literal>, this parameter causes WAL files to be + memory-mapped and used as WAL buffer pages. The WAL files in pg_wal + directory (or the directory given by <command>initdb -X</command> + option) should be on <firstterm>persistent memory</firstterm> (PMEM) and + the filesystem for those files should support the <firstterm>Direct + Access</firstterm> (DAX) feature. <varname>wal_sync_method</varname> + for the primary server is ignored and WAL updates are forced out to + PMEM in a more optimal way which avoids calling into the kernel. + <varname>min_wal_size</varname> should be multiple of the size of a + WAL file, and <varname>wal_buffers</varname> is ignored and set to + the equivalent of <varname>min_wal_size</varname>. + </para> + <para> + This parameter can only be set at server start. The default is + <literal>off</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-writer-delay" xreflabel="wal_writer_delay"> <term><varname>wal_writer_delay</varname> (<type>integer</type>) <indexterm> -- 2.25.1
v8-0008-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 3.6K) download From 3347e9a237f38bdc6e51a7488dc5e64857178cc1 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v8 08/11] Preallocate and initialize more WAL if wal_pmem_map=true --- src/backend/access/transam/xlog.c | 30 ++++++++++++++++++++++++------ src/backend/utils/misc/guc.c | 2 +- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 9484152806..15379481fa 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -2049,7 +2049,9 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -2290,7 +2292,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -5275,15 +5279,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -8189,7 +8203,8 @@ StartupXLOG(void) */ openLogTLI = newTLI; openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(pageBeginPtr, wal_segment_size); len = EndOfLog % XLOG_BLCKSZ; memset(page + len, 0, XLOG_BLCKSZ - len); @@ -8229,7 +8244,10 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog, newTLI); + if (wal_pmem_map) + AdvanceXLInsertBuffer(InvalidXLogRecPtr, newTLI, true); + else + PreallocXlogFiles(EndOfLog, newTLI); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index c29f46bebb..ffc55f33e8 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2878,7 +2878,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
v8-0006-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From 8463e9eafd07fc95ddf327a3dc48536f53b4ecde Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v8 06/11] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 95a5920a8f..f63d00f302 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -195,6 +195,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -203,22 +204,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v8-0009-Fix-wrong-handling-of-missingContrecPtr.patch (application/octet-stream, 1.1K) download From 3bfa6ec401d14054ede1d9c3c0351a5a045049cf Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 20 Jan 2022 12:01:56 +0900 Subject: [PATCH v8 09/11] Fix wrong handling of missingContrecPtr For test/recovery/t/026 to pass. This may melt into "Preallocate and initialize more WAL if wal_pmem_map=true". --- src/backend/access/transam/xlog.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 15379481fa..f19a451f63 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -8245,7 +8245,15 @@ StartupXLOG(void) * Preallocate additional log files, if wanted. */ if (wal_pmem_map) + { + /* + * AdvanceXLInsertBuffer may reset missingContrecPtr, so we + * save and restore it for CreateOverwriteContrecordRecord. + */ + XLogRecPtr savedMissingContrecPtr = missingContrecPtr; AdvanceXLInsertBuffer(InvalidXLogRecPtr, newTLI, true); + missingContrecPtr = savedMissingContrecPtr; + } else PreallocXlogFiles(EndOfLog, newTLI); -- 2.25.1
v8-0011-For-CI-only-Modify-initdb-for-wal_pmem_map-on.patch (application/octet-stream, 887B) download From b29f596bae2cca5eaf252d1cb2d69bcba72986be Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 20 Jan 2022 13:00:37 +0900 Subject: [PATCH v8 11/11] [For CI only] Modify initdb for "wal_pmem_map = on" This is only for testing, not for production use, because it will modify initdb's binary. --- src/bin/initdb/initdb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index f964a000f8..d571c7846f 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1172,6 +1172,12 @@ setup_config(void) repltok); #endif +#ifdef USE_LIBPMEM + conflines = replace_token(conflines, + "#wal_pmem_map = off", + "wal_pmem_map = on"); +#endif + #ifndef USE_PREFETCH conflines = replace_token(conflines, "#effective_io_concurrency = 1", -- 2.25.1
v8-0010-For-CI-only-Setup-Cirrus-CI-for-with-libpmem.patch (application/octet-stream, 3.0K) download From 88934a365d53dbbc10d8c45d5a641d7c70d530bd Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 20 Jan 2022 12:01:56 +0900 Subject: [PATCH v8 10/11] [For CI only] Setup Cirrus CI for --with-libpmem This is work-in-progress. It supports BSD and Linux, but does not support Windows and macOS yet. --- .cirrus.yml | 11 +++++++++++ src/tools/ci/windows_build_config.pl | 1 + 2 files changed, 12 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index 677bdf0e65..a2539cd2b4 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -25,6 +25,7 @@ env: PGCTLTIMEOUT: 120 # avoids spurious failures during parallel tests TEMP_CONFIG: ${CIRRUS_WORKING_DIR}/src/tools/ci/pg_ci_base.conf PG_TEST_EXTRA: kerberos ldap ssl + PMEM_IS_PMEM_FORCE: 1 # What files to preserve in case tests fail @@ -81,6 +82,7 @@ task: mkdir -m 770 /tmp/cores chown root:postgres /tmp/cores sysctl kern.corefile='/tmp/cores/%N.%P.core' + pkg install -y devel/pmdk # NB: Intentionally build without --with-llvm. The freebsd image size is # already large enough to make VM startup slow, and even without llvm @@ -94,6 +96,7 @@ task: --with-gssapi \ --with-icu \ --with-ldap \ + --with-libpmem \ --with-libxml \ --with-libxslt \ --with-lz4 \ @@ -132,6 +135,7 @@ LINUX_CONFIGURE_FEATURES: &LINUX_CONFIGURE_FEATURES >- --with-gssapi --with-icu --with-ldap + --with-libpmem --with-libxml --with-libxslt --with-llvm @@ -188,6 +192,9 @@ task: mkdir -m 770 /tmp/cores chown root:postgres /tmp/cores sysctl kernel.core_pattern='/tmp/cores/%e-%s-%p.core' + echo 'deb http://deb.debian.org/debian bullseye universe' >>/etc/apt/sources.list + apt-get -y update + apt-get -y install libpmem-dev configure_script: | su postgres <<-EOF @@ -257,6 +264,7 @@ task: # packages do not need to be downloaded. homebrew_cache: folder: $HOMEBREW_CACHE + # TODO: Install libpmem homebrew_install_script: | brew install \ ccache \ @@ -297,6 +305,7 @@ task: --with-gssapi \ --with-icu \ --with-ldap \ + --with-libpmem \ --with-libxml \ --with-libxslt \ --with-lz4 \ @@ -379,6 +388,8 @@ task: powershell -Command get-psdrive -psprovider filesystem set + # TODO: install libpmem + configure_script: # copy errors out when using forward slashes - copy src\tools\ci\windows_build_config.pl src\tools\msvc\config.pl diff --git a/src/tools/ci/windows_build_config.pl b/src/tools/ci/windows_build_config.pl index b0d4360c74..ae5fdd635a 100644 --- a/src/tools/ci/windows_build_config.pl +++ b/src/tools/ci/windows_build_config.pl @@ -8,6 +8,7 @@ $config->{"asserts"} = 1; $config->{"openssl"} = "c:/openssl/1.1/"; $config->{"perl"} = "c:/strawberry/$ENV{DEFAULT_PERL_VERSION}/perl/"; +$config->{"pmem"} = 1; # TODO $config->{"python"} = "c:/python/"; 1; -- 2.25.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-03-22 00:44  Andres Freund <andres@anarazel.de>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 1 reply; 22+ messages in thread

From: Andres Freund @ 2022-03-22 00:44 UTC (permalink / raw)
  To: Takashi Menjo <takashi.menjo@gmail.com>; +Cc: Justin Pryzby <pryzby@telsasoft.com>; Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

Hi,

On 2022-01-20 14:55:13 +0900, Takashi Menjo wrote:
> Here is patchset v8. It will have "make check-world" and Cirrus to
> pass.

This unfortunately does not apply anymore: http://cfbot.cputube.org/patch_37_3181.log

Could you rebase?

- Andres





^ permalink  raw  reply  [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-03-23 08:58  Takashi Menjo <takashi.menjo@gmail.com>
  parent: Andres Freund <andres@anarazel.de>
  0 siblings, 1 reply; 22+ messages in thread

From: Takashi Menjo @ 2022-03-23 08:58 UTC (permalink / raw)
  To: Andres Freund <andres@anarazel.de>; +Cc: Justin Pryzby <pryzby@telsasoft.com>; Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

Hi Andres,

Thank you for your report. I rebased and made patchset v9 attached to
this email. Note that v9-0009 and v9-0010 are for those who want to
pass their own Cirrus CI.

Regards,
Takashi


On Tue, Mar 22, 2022 at 9:44 AM Andres Freund <andres@anarazel.de> wrote:
>
> Hi,
>
> On 2022-01-20 14:55:13 +0900, Takashi Menjo wrote:
> > Here is patchset v8. It will have "make check-world" and Cirrus to
> > pass.
>
> This unfortunately does not apply anymore: http://cfbot.cputube.org/patch_37_3181.log
>
> Could you rebase?
>
> - Andres



-- 
Takashi Menjo <takashi.menjo@gmail.com>

Attachments: download all (10 files, .tar.gz)

v9-0003-Add-wal_pmem_map-to-postgresql.conf.sample.patch (application/octet-stream, 1.0K) download From 58203f5efb2aadcf62215c7b2f2a3f904de25d5e Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 20 Jan 2022 13:04:42 +0900 Subject: [PATCH v9 03/10] Add wal_pmem_map to postgresql.conf.sample This may melt into "Add wal_pmem_map to GUC". --- src/backend/utils/misc/postgresql.conf.sample | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 4cf5b26a36..7fd08d515d 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -225,6 +225,8 @@ #wal_recycle = on # recycle WAL files #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers # (change requires restart) +#wal_pmem_map = off # use WAL segment files as WAL buffers + # wal_buffers will be ignored #wal_writer_delay = 200ms # 1-10000 milliseconds #wal_writer_flush_after = 1MB # measured in pages, 0 disables #wal_skip_threshold = 2MB -- 2.25.1
v9-0001-Add-with-libpmem-option-for-PMEM-support.patch (application/octet-stream, 8.0K) download From 3197c54a0e29957ef194564769a12e56736df488 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 13:32:27 +0900 Subject: [PATCH v9 01/10] Add --with-libpmem option for PMEM support Fixes introduced in patchset v8: - Support build with MSVC on Windows --- configure | 100 +++++++++++++++++++++++++++++++ configure.ac | 18 ++++++ src/include/pg_config.h.in | 6 ++ src/tools/msvc/Solution.pm | 13 ++++ src/tools/msvc/config_default.pl | 3 +- 5 files changed, 139 insertions(+), 1 deletion(-) diff --git a/configure b/configure index f3cb5c2b51..dfdcf74355 100755 --- a/configure +++ b/configure @@ -704,6 +704,7 @@ LDFLAGS_EX ZSTD_LIBS ZSTD_CFLAGS with_zstd +with_libpmem LZ4_LIBS LZ4_CFLAGS with_lz4 @@ -874,6 +875,7 @@ with_system_tzdata with_zlib with_lz4 with_zstd +with_libpmem with_gnu_ld with_ssl with_openssl @@ -1585,6 +1587,7 @@ Optional Packages: --without-zlib do not use Zlib --with-lz4 build with LZ4 support --with-zstd build with ZSTD support + --with-libpmem build with PMEM support --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-ssl=LIB use LIB for SSL/TLS support (openssl) --with-openssl obsolete spelling of --with-ssl=openssl @@ -9184,6 +9187,42 @@ fi esac done fi + +# +# libpmem +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with PMEM support" >&5 +$as_echo_n "checking whether to build with PMEM support... " >&6; } + + + +# Check whether --with-libpmem was given. +if test "${with_libpmem+set}" = set; then : + withval=$with_libpmem; + case $withval in + yes) + +$as_echo "#define USE_LIBPMEM 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-libpmem option" "$LINENO" 5 + ;; + esac + +else + with_libpmem=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libpmem" >&5 +$as_echo "$with_libpmem" >&6; } + + # # Assignments # @@ -13698,6 +13737,56 @@ fi fi +if test "$with_libpmem" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pmem_memcpy in -lpmem" >&5 +$as_echo_n "checking for pmem_memcpy in -lpmem... " >&6; } +if ${ac_cv_lib_pmem_pmem_memcpy+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpmem $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pmem_memcpy (); +int +main () +{ +return pmem_memcpy (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pmem_pmem_memcpy=yes +else + ac_cv_lib_pmem_pmem_memcpy=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pmem_pmem_memcpy" >&5 +$as_echo "$ac_cv_lib_pmem_pmem_memcpy" >&6; } +if test "x$ac_cv_lib_pmem_pmem_memcpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPMEM 1 +_ACEOF + + LIBS="-lpmem $LIBS" + +else + as_fn_error $? "library 'libpmem' (version >= 1.5) is required for PMEM support" "$LINENO" 5 +fi + +fi + ## ## Header files @@ -14528,6 +14617,17 @@ fi done +fi + +if test "$with_libpmem" = yes ; then + ac_fn_c_check_header_mongrel "$LINENO" "libpmem.h" "ac_cv_header_libpmem_h" "$ac_includes_default" +if test "x$ac_cv_header_libpmem_h" = xyes; then : + +else + as_fn_error $? "header file <libpmem.h> is required for PMEM support" "$LINENO" 5 +fi + + fi ## diff --git a/configure.ac b/configure.ac index 19d1a80367..32eb9f88c5 100644 --- a/configure.ac +++ b/configure.ac @@ -1080,6 +1080,16 @@ if test "$with_zstd" = yes; then esac done fi + +# +# libpmem +# +AC_MSG_CHECKING([whether to build with PMEM support]) +PGAC_ARG_BOOL(with, libpmem, no, [build with PMEM support], + [AC_DEFINE([USE_LIBPMEM], 1, [Define to 1 to build with PMEM support. (--with-libpmem)])]) +AC_MSG_RESULT([$with_libpmem]) +AC_SUBST(with_libpmem) + # # Assignments # @@ -1413,6 +1423,10 @@ elif test "$with_uuid" = ossp ; then fi AC_SUBST(UUID_LIBS) +if test "$with_libpmem" = yes; then + AC_CHECK_LIB(pmem, pmem_memcpy, [], [AC_MSG_ERROR([library 'libpmem' (version >= 1.5) is required for PMEM support])]) +fi + ## ## Header files @@ -1607,6 +1621,10 @@ if test "$PORTNAME" = "win32" ; then AC_CHECK_HEADERS(crtdefs.h) fi +if test "$with_libpmem" = yes ; then + AC_CHECK_HEADER(libpmem.h, [], [AC_MSG_ERROR([header file <libpmem.h> is required for PMEM support])]) +fi + ## ## Types, structures, compiler characteristics ## diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 635fbb2181..08c949e74f 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -331,6 +331,9 @@ /* Define to 1 if you have the `pam' library (-lpam). */ #undef HAVE_LIBPAM +/* Define to 1 if you have the `pmem' library (-lpmem). */ +#undef HAVE_LIBPMEM + /* Define if you have a function readline library */ #undef HAVE_LIBREADLINE @@ -906,6 +909,9 @@ /* Define to 1 to build with LDAP support. (--with-ldap) */ #undef USE_LDAP +/* Define to 1 to build with PMEM support. (--with-libpmem) */ +#undef USE_LIBPMEM + /* Define to 1 to build with XML support. (--with-libxml) */ #undef USE_LIBXML diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index a21ea9bef9..ead475a985 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -304,6 +304,7 @@ sub GenerateFiles HAVE_LIBLZ4 => undef, HAVE_LIBM => undef, HAVE_LIBPAM => undef, + HAVE_LIBPMEM => undef, HAVE_LIBREADLINE => undef, HAVE_LIBSELINUX => undef, HAVE_LIBSSL => undef, @@ -491,6 +492,7 @@ sub GenerateFiles USE_BONJOUR => undef, USE_BSD_AUTH => undef, USE_ICU => $self->{options}->{icu} ? 1 : undef, + USE_LIBPMEM => undef, USE_LIBXML => undef, USE_LIBXSLT => undef, USE_LZ4 => undef, @@ -547,6 +549,11 @@ sub GenerateFiles $define{HAVE_LIBZSTD} = 1; $define{USE_ZSTD} = 1; } + if ($self->{options}->{pmem}) + { + $define{HAVE_LIBPMEM} = 1; + $define{USE_LIBPMEM} = 1; + } if ($self->{options}->{openssl}) { $define{USE_OPENSSL} = 1; @@ -1099,6 +1106,11 @@ sub AddProject $proj->AddIncludeDir($self->{options}->{uuid} . '\include'); $proj->AddLibrary($self->{options}->{uuid} . '\lib\uuid.lib'); } + if ($self->{options}->{pmem}) + { + $proj->AddIncludeDir($self->{options}->{pmem} . '\include'); + $proj->AddLibrary($self->{options}->{pmem} . '\lib\libpmem.lib'); + } return $proj; } @@ -1212,6 +1224,7 @@ sub GetFakeConfigure $cfg .= ' --with-tcl' if ($self->{options}->{tcl}); $cfg .= ' --with-perl' if ($self->{options}->{perl}); $cfg .= ' --with-python' if ($self->{options}->{python}); + $cfg .= ' --with-libpmem' if ($self->{options}->{pmem}); my $port = $self->{options}->{'--with-pgport'}; $cfg .= " --with-pgport=$port" if defined($port); diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl index 186849a09a..2f9b2a5d7d 100644 --- a/src/tools/msvc/config_default.pl +++ b/src/tools/msvc/config_default.pl @@ -26,7 +26,8 @@ our $config = { xml => undef, # --with-libxml=<path> xslt => undef, # --with-libxslt=<path> iconv => undef, # (not in configure, path to iconv) - zlib => undef # --with-zlib=<path> + zlib => undef, # --with-zlib=<path> + pmem => undef # --with-libpmem=<path> }; 1; -- 2.25.1
v9-0002-Add-wal_pmem_map-to-GUC.patch (application/octet-stream, 5.0K) download From 9a054c86d8fe6fb84291cc4076d44adf4ed99cf5 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 11 Mar 2021 17:55:53 +0900 Subject: [PATCH v9 02/10] Add wal_pmem_map to GUC Fixes introduced in patchset v8: - Let wal_pmem_map be constant unless --with-libpmem - Comment for constant wal_pmem_map --- src/backend/access/transam/xlog.c | 54 ++++++++++++++++++++++++------- src/backend/utils/misc/guc.c | 14 ++++++++ src/include/access/xlog.h | 12 +++++++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 4ac3871c74..f6a08713ea 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -135,6 +135,10 @@ int wal_retrieve_retry_interval = 5000; int max_slot_wal_keep_size_mb = -1; bool track_wal_io_timing = false; +#ifdef USE_LIBPMEM +bool wal_pmem_map = false; +#endif + #ifdef WAL_DEBUG bool XLOG_DEBUG = false; #endif @@ -4363,13 +4367,28 @@ XLOGShmemSize(void) { Size size; + /* + * If we use WAL segment files as WAL buffers, we don't use the given + * value of wal_buffers. Instead, we set it to the value based on the + * segment size and the page size. This should be done before calculating + * the size of xlblocks array. + */ + if (wal_pmem_map) + { + int npages; + char buf[32]; + + npages = wal_segment_size / XLOG_BLCKSZ; + snprintf(buf, sizeof(buf), "%d", (int) npages); + SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); + } /* * If the value of wal_buffers is -1, use the preferred auto-tune value. * This isn't an amazingly clean place to do this, but we must wait till * NBuffers has received its final value, and must do it before using the * value of XLOGbuffers to do anything important. */ - if (XLOGbuffers == -1) + else if (XLOGbuffers == -1) { char buf[32]; @@ -4385,10 +4404,17 @@ XLOGShmemSize(void) size = add_size(size, mul_size(sizeof(WALInsertLockPadded), NUM_XLOGINSERT_LOCKS + 1)); /* xlblocks array */ size = add_size(size, mul_size(sizeof(XLogRecPtr), XLOGbuffers)); - /* extra alignment padding for XLOG I/O buffers */ - size = add_size(size, XLOG_BLCKSZ); - /* and the buffers themselves */ - size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + + /* + * If we use WAL segment files as WAL buffers, we don't need volatile ones. + */ + if (!wal_pmem_map) + { + /* extra alignment padding for XLOG I/O buffers */ + size = add_size(size, XLOG_BLCKSZ); + /* and the buffers themselves */ + size = add_size(size, mul_size(XLOG_BLCKSZ, XLOGbuffers)); + } /* * Note: we don't count ControlFileData, it comes out of the "slop factor" @@ -4482,13 +4508,19 @@ XLOGShmemInit(void) } /* - * Align the start of the page buffers to a full xlog block size boundary. - * This simplifies some calculations in XLOG insertion. It is also - * required for O_DIRECT. + * If we use WAL segment files as WAL buffers, we don't need volatile ones. */ - allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); - XLogCtl->pages = allocptr; - memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + if (!wal_pmem_map) + { + /* + * Align the start of the page buffers to a full xlog block size boundary. + * This simplifies some calculations in XLOG insertion. It is also + * required for O_DIRECT. + */ + allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr); + XLogCtl->pages = allocptr; + memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers); + } /* * Do basic initialization of XLogCtl shared data. (StartupXLOG will fill diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 932aefc777..5268cd95f4 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1353,6 +1353,20 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, +#ifdef USE_LIBPMEM + { + {"wal_pmem_map", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Map WAL segment files on PMEM as WAL buffers."), + gettext_noop("If true, postgres will memory-map WAL segment files " + "on PMEM to use them as WAL buffers instead of the " + "traditional volatile ones."), + }, + &wal_pmem_map, + false, + NULL, NULL, NULL + }, +#endif + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 09f6464331..9813e65e3e 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -51,6 +51,18 @@ extern char *wal_consistency_checking_string; extern bool log_checkpoints; extern bool track_wal_io_timing; +#ifdef USE_LIBPMEM +extern bool wal_pmem_map; +#else +/* + * If USE_LIBPMEM is not defined (that is, no --with-libpmem), wal_pmem_map + * is always false and is never used essentially. Using #if(n)def everywhere + * is not good for code readability, so let wal_pmem_map be constant. This + * may help compilers optimize conditional branches. + */ +#define wal_pmem_map false +#endif + extern int CheckPointSegments; /* Archive modes */ -- 2.25.1
v9-0005-Map-WAL-segment-files-on-PMEM-as-WAL-buffers.patch (application/octet-stream, 19.1K) download From f7b10f8aed56cf9ce60a9ceba494bc33f9d7c32c Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 11:45:44 +0900 Subject: [PATCH v9 05/10] Map WAL segment files on PMEM as WAL buffers Fixes introduced in patchset v2: - Keep openLogSegNo even if wal_pmem_map=true - Fix sync issue of PmemXLogCreate - Fix unmapping issue of PmemXLogUnmap - Remove unused XLogPageOffset Fixes introduced in patchset v8: - Fix invalid declaration of PmemXLogEnsurePrevMapped - Remove redundant parentheses from ereport call - Ensure WAL mappings before assertion - Fix typo in comment - Compatible to Windows --- src/backend/access/transam/Makefile | 1 + src/backend/access/transam/xlog.c | 167 +++++++++++--- src/backend/access/transam/xlogpmem.c | 318 ++++++++++++++++++++++++++ src/include/access/xlogpmem.h | 59 +++++ 4 files changed, 510 insertions(+), 35 deletions(-) create mode 100644 src/backend/access/transam/xlogpmem.c create mode 100644 src/include/access/xlogpmem.h diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile index 79314c69ab..757738e813 100644 --- a/src/backend/access/transam/Makefile +++ b/src/backend/access/transam/Makefile @@ -31,6 +31,7 @@ OBJS = \ xlogarchive.o \ xlogfuncs.o \ xloginsert.o \ + xlogpmem.o \ xlogreader.o \ xlogrecovery.o \ xlogutils.o diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index cc601ee25b..afbb439dfc 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -59,6 +59,7 @@ #include "access/xlog_internal.h" #include "access/xlogarchive.h" #include "access/xloginsert.h" +#include "access/xlogpmem.h" #include "access/xlogreader.h" #include "access/xlogrecovery.h" #include "access/xlogutils.h" @@ -1571,6 +1572,23 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) */ if (ptr / XLOG_BLCKSZ == cachedPage) { + /* + * Ensure WAL mappings before assersion. + * + * cachedPos should be recalculated because it has been probably + * invalidated due to WAL remapping. This should be done even if + * openLogSegNo seems not to change because the address of the + * mapping could have changed (ABA problem). + */ + if (wal_pmem_map) + { + endptr = ptr - ptr % XLOG_BLCKSZ + XLOG_BLCKSZ; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); + } + Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); return cachedPos + ptr % XLOG_BLCKSZ; @@ -1654,7 +1672,14 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) * offset within the page. */ cachedPage = ptr / XLOG_BLCKSZ; - cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); + cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + } + else + cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; Assert(((XLogPageHeader) cachedPos)->xlp_magic == XLOG_PAGE_MAGIC); Assert(((XLogPageHeader) cachedPos)->xlp_pageaddr == ptr - (ptr % XLOG_BLCKSZ)); @@ -1888,7 +1913,14 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) Assert(XLogRecPtrToBufIdx(NewPageBeginPtr) == nextidx); - NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); + if (wal_pmem_map) + { + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); + NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + } + else + NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); /* * Be sure to re-zero the buffer so that bytes beyond what we've @@ -2095,6 +2127,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) int npages; int startidx; uint32 startoffset; + bool isfirstpage; + XLogRecPtr startpageptr; /* We should always be inside a critical section here */ Assert(CritSectionCount > 0); @@ -2117,6 +2151,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) startidx = 0; startoffset = 0; + /* Those are used actually only if wal_pmem_map=true */ + isfirstpage = true; + startpageptr = 0; + /* * Within the loop, curridx is the cache block index of the page to * consider writing. Begin at the buffer containing the next unwritten @@ -2142,33 +2180,36 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) LogwrtResult.Write = EndPtr; ispartialpage = WriteRqst.Write < LogwrtResult.Write; - if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size)) + if (!wal_pmem_map) { - /* - * Switch to new logfile segment. We cannot have any pending - * pages here (since we dump what we have at segment end). - */ - Assert(npages == 0); - if (openLogFile >= 0) - XLogFileClose(); - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; + if (!XLByteInPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size)) + { + /* + * Switch to new logfile segment. We cannot have any pending + * pages here (since we dump what we have at segment end). + */ + Assert(npages == 0); + if (openLogFile >= 0) + XLogFileClose(); + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; - /* create/use new log file */ - openLogFile = XLogFileInit(openLogSegNo, tli); - ReserveExternalFD(); - } + /* create/use new log file */ + openLogFile = XLogFileInit(openLogSegNo, tli); + ReserveExternalFD(); + } - /* Make sure we have the current logfile open */ - if (openLogFile < 0) - { - XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, - wal_segment_size); - openLogTLI = tli; - openLogFile = XLogFileOpen(openLogSegNo, tli); - ReserveExternalFD(); + /* Make sure we have the current logfile open */ + if (openLogFile < 0) + { + XLByteToPrevSeg(LogwrtResult.Write, openLogSegNo, + wal_segment_size); + openLogTLI = tli; + openLogFile = XLogFileOpen(openLogSegNo, tli); + ReserveExternalFD(); + } } /* Add current page to the set of pending pages-to-dump */ @@ -2176,8 +2217,8 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) { /* first of group */ startidx = curridx; - startoffset = XLogSegmentOffset(LogwrtResult.Write - XLOG_BLCKSZ, - wal_segment_size); + startpageptr = LogwrtResult.Write - XLOG_BLCKSZ; + startoffset = XLogSegmentOffset(startpageptr, wal_segment_size); } npages++; @@ -2215,7 +2256,38 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) INSTR_TIME_SET_CURRENT(start); pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); - written = pg_pwrite(openLogFile, from, nleft, startoffset); + + /* + * If we use a WAL segment file as WAL buffers, we cache-flush + * records on the buffers byte by byte, not page by page. To do + * so, here we fix the range being cache-flushed. + */ + if (wal_pmem_map) + { + XLogRecPtr startbyteptr; + XLogRecPtr endbyteptr; + + startbyteptr = (isfirstpage) + ? XLogCtl->LogwrtResult.Write + : startpageptr; + + endbyteptr = (ispartialpage) + ? WriteRqst.Write + : LogwrtResult.Write; + + /* Now we cache-flush records */ + openLogTLI = tli; + openLogSegNo = PmemXLogEnsurePrevMapped(endbyteptr, tli); + PmemXLogFlush(startbyteptr, endbyteptr); + + /* Mark the first page is consumed */ + isfirstpage = false; + + /* Tell all the "pages" have been written successfully */ + written = nleft; + } + else + written = pg_pwrite(openLogFile, from, nleft, startoffset); pgstat_report_wait_end(); /* @@ -2273,7 +2345,10 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) */ if (finishing_seg) { - issue_xlog_fsync(openLogFile, openLogSegNo, tli); + if (wal_pmem_map) + PmemXLogSync(); + else + issue_xlog_fsync(openLogFile, openLogSegNo, tli); /* signal that we need to wakeup walsenders later */ WalSndWakeupRequest(); @@ -2323,12 +2398,14 @@ XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible) if (LogwrtResult.Flush < WriteRqst.Flush && LogwrtResult.Flush < LogwrtResult.Write) { + if (wal_pmem_map) + PmemXLogSync(); /* * Could get here without iterating above loop, in which case we might * have no open file or the wrong one. However, we do not need to * fsync more than one file. */ - if (sync_method != SYNC_METHOD_OPEN && + else if (sync_method != SYNC_METHOD_OPEN && sync_method != SYNC_METHOD_OPEN_DSYNC) { if (openLogFile >= 0 && @@ -5498,10 +5575,30 @@ StartupXLOG(void) len = EndOfLog - endOfRecoveryInfo->lastPageBeginPtr; Assert(len < XLOG_BLCKSZ); - /* Copy the valid part of the last block, and zero the rest */ - page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; - memcpy(page, endOfRecoveryInfo->lastPage, len); - memset(page + len, 0, XLOG_BLCKSZ - len); + if (wal_pmem_map) + { + /* + * Keep the valid part of the last block, and zero the rest. + * Note that "len" indicates the size of the valid part. + * + * TODO how about if (newTLI != replayTLI) ? + */ + openLogTLI = newTLI; + openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); + page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + memset(page + len, 0, XLOG_BLCKSZ - len); + + /* Cache-flush and sync now */ + PmemXLogFlush(EndOfLog, endOfRecoveryInfo->lastPageBeginPtr + XLOG_BLCKSZ); + PmemXLogSync(); + } + else + { + /* Copy the valid part of the last block, and zero the rest */ + page = &XLogCtl->pages[firstIdx * XLOG_BLCKSZ]; + memcpy(page, endOfRecoveryInfo->lastPage, len); + memset(page + len, 0, XLOG_BLCKSZ - len); + } XLogCtl->xlblocks[firstIdx] = endOfRecoveryInfo->lastPageBeginPtr + XLOG_BLCKSZ; XLogCtl->InitializedUpTo = endOfRecoveryInfo->lastPageBeginPtr + XLOG_BLCKSZ; diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c new file mode 100644 index 0000000000..95a5920a8f --- /dev/null +++ b/src/backend/access/transam/xlogpmem.c @@ -0,0 +1,318 @@ +#include "postgres.h" + +#ifdef USE_LIBPMEM + +#include <errno.h> +#include <limits.h> /* INT_MAX */ +#include <stddef.h> /* size_t */ +#include <stdint.h> /* uintptr_t */ +#include <unistd.h> /* getpid, unlink */ + +/* + * On Windows, we will have two ported but conflicting mode_t: + * + * mode_t in libpmem: + * libpmem.h -> pmemcompat.h -> typedef int mode_t + * mode_t in PostgreSQL: + * c.h -> port.h -> win32_port.h -> typedef unsigned short mode_t + * + * We want to use PostgreSQL's one, so conseal libpmem's one. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +#define mode_t unused_libpmem_mode_t +#include <libpmem.h> +#undef mode_t +/* On other platforms, simply include libpmem.h */ +#else +#include <libpmem.h> +#endif + +#include "c.h" /* bool, Size */ +#include "access/xlog.h" +#include "access/xlog_internal.h" /* XLogFilePath, XLByteToSeg */ +#include "access/xlogpmem.h" +#include "common/file_perm.h" /* pg_file_create_mode */ +#include "miscadmin.h" /* enableFsync */ +#include "pgstat.h" + +static char *mappedPages = NULL; +static XLogSegNo mappedSegNo = 0; + +#define PG_DAX_HUGEPAGE_SIZE (((uintptr_t) 1) << 21) +#define PG_DAX_HUGEPAGE_MASK (~(PG_DAX_HUGEPAGE_SIZE - 1)) + +static XLogSegNo PmemXLogMap(XLogSegNo segno, TimeLineID tli); +static void PmemXLogCreate(XLogSegNo segno, TimeLineID tli); +static void PmemXLogUnmap(void); + +static void *PmemCreateMapFile(const char *path, size_t len); +static void *PmemOpenMapFile(const char *path, size_t expected_len); +static void *PmemTryOpenMapFile(const char *path, size_t expected_len); +static void *PmemMapFile(const char *path, size_t expected_len, int flags, + bool try_open); +static void PmemUnmapForError(void *addr, size_t len); + +/* + * Ensures the WAL segment containg {ptr-1} to be mapped. + * + * Returns mapped XLogSegNo. + */ +XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) +{ + XLogSegNo segno; + + Assert(wal_pmem_map); + + XLByteToPrevSeg(ptr, segno, wal_segment_size); + + if (mappedPages != NULL) + { + /* Fast return: The segment we need is already mapped */ + if (mappedSegNo == segno) + return mappedSegNo; + + /* Unmap the current segment we don't need */ + PmemXLogUnmap(); + } + + return PmemXLogMap(segno, tli); +} + +/* + * Creates a new XLOG file segment, or open a pre-existing one, for WAL buffers. + * + * Returns mapped XLogSegNo. + * + * See also XLogFileInit in xlog.c. + */ +static XLogSegNo +PmemXLogMap(XLogSegNo segno, TimeLineID tli) +{ + char path[MAXPGPATH]; + + Assert(mappedPages == NULL); + + XLogFilePath(path, tli, segno, wal_segment_size); + + /* PmemTryOpenMapFile will handle error except ENOENT */ + mappedPages = PmemTryOpenMapFile(path, wal_segment_size); + + /* Fast return if already exists */ + if (mappedPages != NULL) + { + mappedSegNo = segno; + return mappedSegNo; + } + + elog(DEBUG2, "creating and filling new WAL file"); + PmemXLogCreate(segno, tli); + + /* PmemCreateMapFile will handle error */ + mappedPages = PmemOpenMapFile(path, wal_segment_size); + mappedSegNo = segno; + + elog(DEBUG2, "done creating and filling new WAL file"); + return mappedSegNo; +} + +/* + * Creates a new XLOG file segment. + * + * See also XLogFileInit in xlog.c. + */ +static void +PmemXLogCreate(XLogSegNo segno, TimeLineID tli) +{ + char *addr; + char tmppath[MAXPGPATH]; + XLogSegNo inst_segno; + XLogSegNo max_segno; + + snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid()); + unlink(tmppath); + + /* PmemCreateMapFile will handle error */ + addr = PmemCreateMapFile(tmppath, wal_segment_size); + + /* + * Initialize whole the buffers. + * + * Note that we don't put any single byte if not wal_init_zero. It's okay + * because we already have a new segment file truncated to the proper size. + */ + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_WRITE); + if (wal_init_zero) + pmem_memset_nodrain(addr, 0, wal_segment_size); + pgstat_report_wait_end(); + + pgstat_report_wait_start(WAIT_EVENT_WAL_INIT_SYNC); + if (enableFsync) + pmem_drain(); + pgstat_report_wait_end(); + + if (pmem_unmap(addr, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap temporal WAL buffers: %m"); + + inst_segno = segno; + max_segno = segno + CheckPointSegments; + if (!InstallXLogFileSegment(&inst_segno, tmppath, true, max_segno, tli)) + unlink(tmppath); +} + +/* + * Unmaps the current WAL segment file if mapped. + */ +static void +PmemXLogUnmap(void) +{ + /* Fast return if not mapped */ + if (mappedPages == NULL) + return; + + if (pmem_unmap(mappedPages, wal_segment_size) < 0) + elog(ERROR, "could not pmem_unmap WAL buffers: %m"); + + mappedPages = NULL; +} + +/* + * Gets the head address of the WAL buffers. + */ +char * +PmemXLogGetBufferPages(void) +{ + Assert(wal_pmem_map); + Assert(mappedPages != NULL); + + return mappedPages; +} + +/* + * Flushes records in the given range [start, end) within a single segment. + */ +void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + Size off; + + Assert(wal_pmem_map); + Assert(start < end); + Assert(mappedPages != NULL); + Assert(XLByteInSeg(start, mappedSegNo, wal_segment_size)); + Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); + + off = XLogSegmentOffset(start, wal_segment_size); + pmem_flush(mappedPages + off, end - start); +} + +/* + * Wait for cache-flush to finish. + */ +void +PmemXLogSync(void) +{ + Assert(wal_pmem_map); + + /* Fast return */ + if (!enableFsync) + return; + + pmem_drain(); +} + +/* + * Wrappers for pmem_map_file. + */ +static void * +PmemCreateMapFile(const char *path, size_t len) +{ + return PmemMapFile(path, len, PMEM_FILE_CREATE | PMEM_FILE_EXCL, false); +} + +static void * +PmemOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, false); +} + +static void * +PmemTryOpenMapFile(const char *path, size_t expected_len) +{ + return PmemMapFile(path, expected_len, 0, true); +} + +static void * +PmemMapFile(const char *path, size_t expected_len, int flags, bool try_open) +{ + size_t param_len; + int mode; + size_t mapped_len; + int is_pmem; + void *addr; + + Assert(expected_len > 0); + Assert(expected_len <= INT_MAX); + + param_len = (flags & PMEM_FILE_CREATE) ? expected_len : 0; + mode = (flags & PMEM_FILE_CREATE) ? pg_file_create_mode : 0; + + mapped_len = 0; + is_pmem = 0; +#if defined(WIN32) && !defined(__CYGWIN__) + addr = pmem_map_fileU(path, param_len, flags, mode, &mapped_len, &is_pmem); +#else + addr = pmem_map_file(path, param_len, flags, mode, &mapped_len, &is_pmem); +#endif + + if (addr == NULL) + { + if (try_open && errno == ENOENT) + return NULL; + + ereport(ERROR, + errcode_for_file_access(), + errmsg("could not pmem_map_file \"%s\": %m", path)); + } + + if (mapped_len > INT_MAX) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual (greater than %d) expected %d", + path, INT_MAX, (int) expected_len); + } + + if (mapped_len != expected_len) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, + "unexpected file size: path \"%s\" actual %d expected %d", + path, (int) mapped_len, (int) expected_len); + } + + if (!is_pmem) + { + PmemUnmapForError(addr, mapped_len); + elog(ERROR, "file not on PMEM: path \"%s\"", path); + } + + if ((uintptr_t) addr & ~PG_DAX_HUGEPAGE_MASK) + elog(WARNING, + "file not mapped on DAX hugepage boundary: path \"%s\" addr %p", + path, addr); + + return addr; +} + +static void +PmemUnmapForError(void *addr, size_t len) +{ + int saved_errno; + + saved_errno = errno; + (void) pmem_unmap(addr, len); + errno = saved_errno; +} + +#endif /* USE_LIBPMEM */ diff --git a/src/include/access/xlogpmem.h b/src/include/access/xlogpmem.h new file mode 100644 index 0000000000..cd83bffc88 --- /dev/null +++ b/src/include/access/xlogpmem.h @@ -0,0 +1,59 @@ +/* + * xlogpmem.h + * + * Definitions for PMEM-mapped WAL buffers. + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/xlogpmem.h + */ +#ifndef XLOGPMEM_H +#define XLOGPMEM_H + +#include "postgres.h" + +#include "c.h" /* Size */ +#include "access/xlogdefs.h" /* XLogRecPtr, XLogSegNo */ + +#ifdef USE_LIBPMEM + +/* Prototypes */ +extern XLogSegNo PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli); +extern char *PmemXLogGetBufferPages(void); +extern void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end); +extern void PmemXLogSync(void); + +#else /* USE_LIBPMEM */ + +#include <stdlib.h> /* abort */ + +static inline XLogSegNo +PmemXLogEnsurePrevMapped(XLogRecPtr ptr, TimeLineID tli) +{ + abort(); + return 0; +} + +static inline char * +PmemXLogGetBufferPages(void) +{ + abort(); + return NULL; +} + +static inline void +PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) +{ + abort(); +} + +static inline void +PmemXLogSync(void) +{ + abort(); +} + +#endif /* USE_LIBPMEM */ + +#endif /* XLOGPMEM_H */ -- 2.25.1
v9-0004-Export-InstallXLogFileSegment.patch (application/octet-stream, 2.1K) download From 17b01a2d7edc0d5488e907fdca049061435bea07 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 23 Mar 2021 07:32:05 +0900 Subject: [PATCH v9 04/10] Export InstallXLogFileSegment --- src/backend/access/transam/xlog.c | 5 +---- src/include/access/xlog_internal.h | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f6a08713ea..cc601ee25b 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -679,9 +679,6 @@ static XLogRecPtr XLogGetReplicationSlotMinimumLSN(void); static void AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic); static void XLogWrite(XLogwrtRqst WriteRqst, TimeLineID tli, bool flexible); -static bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, - bool find_free, XLogSegNo max_segno, - TimeLineID tli); static void XLogFileClose(void); static void PreallocXlogFiles(XLogRecPtr endptr, TimeLineID tli); static void RemoveTempXlogFiles(void); @@ -3313,7 +3310,7 @@ XLogFileCopy(TimeLineID destTLI, XLogSegNo destsegno, * max_segno limit was exceeded, the startup process has disabled this * function for now, or an error occurred while renaming the file into place. */ -static bool +bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, bool find_free, XLogSegNo max_segno, TimeLineID tli) { diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 0e94833129..632dd12b4f 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -329,6 +329,10 @@ extern XLogRecPtr RequestXLogSwitch(bool mark_unimportant); extern void GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli); +extern bool InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, + bool find_free, XLogSegNo max_segno, + TimeLineID tli); + /* * Exported for the functions in timeline.c and xlogarchive.c. Only valid * in the startup process. -- 2.25.1
v9-0006-WAL-statistics-in-cases-of-wal_pmem_map-true.patch (application/octet-stream, 2.2K) download From 82a13144d30368b075a41c27497a64cf5da31929 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Wed, 19 May 2021 11:57:49 +0900 Subject: [PATCH v9 06/10] WAL statistics in cases of wal_pmem_map=true --- src/backend/access/transam/xlogpmem.c | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/backend/access/transam/xlogpmem.c b/src/backend/access/transam/xlogpmem.c index 95a5920a8f..f63d00f302 100644 --- a/src/backend/access/transam/xlogpmem.c +++ b/src/backend/access/transam/xlogpmem.c @@ -195,6 +195,7 @@ void PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) { Size off; + instr_time start_time; Assert(wal_pmem_map); Assert(start < end); @@ -203,22 +204,68 @@ PmemXLogFlush(XLogRecPtr start, XLogRecPtr end) Assert(XLByteInPrevSeg(end, mappedSegNo, wal_segment_size)); off = XLogSegmentOffset(start, wal_segment_size); + + /* Measure I/O timing to write WAL data */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start_time); + + pgstat_report_wait_start(WAIT_EVENT_WAL_WRITE); pmem_flush(mappedPages + off, end - start); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL data + * were written out to disk. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start_time); + WalStats.m_wal_write_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_write++; } /* * Wait for cache-flush to finish. + * + * See also issue_xlog_fsync in xlog.c. */ void PmemXLogSync(void) { + instr_time start; + Assert(wal_pmem_map); /* Fast return */ if (!enableFsync) return; + /* Measure I/O timing to sync the WAL file */ + if (track_wal_io_timing) + INSTR_TIME_SET_CURRENT(start); + + pgstat_report_wait_start(WAIT_EVENT_WAL_SYNC); pmem_drain(); + pgstat_report_wait_end(); + + /* + * Increment the I/O timing and the number of times WAL files were synced. + */ + if (track_wal_io_timing) + { + instr_time duration; + + INSTR_TIME_SET_CURRENT(duration); + INSTR_TIME_SUBTRACT(duration, start); + WalStats.m_wal_sync_time += INSTR_TIME_GET_MICROSEC(duration); + } + + WalStats.m_wal_sync++; } /* -- 2.25.1
v9-0007-Update-document.patch (application/octet-stream, 2.0K) download From ca802c5b705c6342553efebdb8dddf6bd1ddc2de Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 30 Sep 2021 12:51:56 +0900 Subject: [PATCH v9 07/10] Update document --- doc/src/sgml/config.sgml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 7a48973b3c..200cc2f6f4 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3241,6 +3241,33 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="guc-wal-pmem-map" xreflabel="wal_pmem_map"> + <term><varname>wal_pmem_map</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>wal_pmem_map</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + If set to <literal>on</literal>, this parameter causes WAL files to be + memory-mapped and used as WAL buffer pages. The WAL files in pg_wal + directory (or the directory given by <command>initdb -X</command> + option) should be on <firstterm>persistent memory</firstterm> (PMEM) and + the filesystem for those files should support the <firstterm>Direct + Access</firstterm> (DAX) feature. <varname>wal_sync_method</varname> + for the primary server is ignored and WAL updates are forced out to + PMEM in a more optimal way which avoids calling into the kernel. + <varname>min_wal_size</varname> should be multiple of the size of a + WAL file, and <varname>wal_buffers</varname> is ignored and set to + the equivalent of <varname>min_wal_size</varname>. + </para> + <para> + This parameter can only be set at server start. The default is + <literal>off</literal>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-writer-delay" xreflabel="wal_writer_delay"> <term><varname>wal_writer_delay</varname> (<type>integer</type>) <indexterm> -- 2.25.1
v9-0008-Preallocate-and-initialize-more-WAL-if-wal_pmem_m.patch (application/octet-stream, 4.0K) download From e953e2465a73259eb15553cadbaf6d689cdc53d0 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Tue, 1 Jun 2021 19:29:22 +0900 Subject: [PATCH v9 08/10] Preallocate and initialize more WAL if wal_pmem_map=true Fixes introduced in patchset v9: - Fix wrong handling of missingContrecPtr --- src/backend/access/transam/xlog.c | 39 ++++++++++++++++++++++++++----- src/backend/utils/misc/guc.c | 2 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index afbb439dfc..11b0b7aec5 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -1676,7 +1676,9 @@ GetXLogBuffer(XLogRecPtr ptr, TimeLineID tli) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(endptr, tli); - cachedPos = PmemXLogGetBufferPages() + idx * (Size) XLOG_BLCKSZ; + cachedPos = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endptr - XLOG_BLCKSZ, + wal_segment_size); } else cachedPos = XLogCtl->pages + idx * (Size) XLOG_BLCKSZ; @@ -1917,7 +1919,9 @@ AdvanceXLInsertBuffer(XLogRecPtr upto, TimeLineID tli, bool opportunistic) { openLogTLI = tli; openLogSegNo = PmemXLogEnsurePrevMapped(NewPageEndPtr, tli); - NewPage = (XLogPageHeader) (PmemXLogGetBufferPages() + nextidx * (Size) XLOG_BLCKSZ); + NewPage = (XLogPageHeader) + (PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(NewPageBeginPtr, wal_segment_size)); } else NewPage = (XLogPageHeader) (XLogCtl->pages + nextidx * (Size) XLOG_BLCKSZ); @@ -4444,15 +4448,25 @@ XLOGShmemSize(void) /* * If we use WAL segment files as WAL buffers, we don't use the given * value of wal_buffers. Instead, we set it to the value based on the - * segment size and the page size. This should be done before calculating + * min_wal_size and the page size. This should be done before calculating * the size of xlblocks array. + * + * TODO Do not allow changing min_wal_size by SIGHUP if wal_pmem_map=true. + * + * TODO Move validations to check_hook functions. */ if (wal_pmem_map) { int npages; char buf[32]; - npages = wal_segment_size / XLOG_BLCKSZ; + if (min_wal_size_mb % (wal_segment_size / (1024 * 1024)) != 0) + elog(PANIC, "min_wal_size should be multiple of wal_segment_size when wal_pmem_map=true"); + + if (min_wal_size_mb / (XLOG_BLCKSZ / 1024) > INT_MAX / 1024) + elog(PANIC, "too many wal buffer pages"); + + npages = min_wal_size_mb / (XLOG_BLCKSZ / 1024) * 1024; snprintf(buf, sizeof(buf), "%d", (int) npages); SetConfigOption("wal_buffers", buf, PGC_POSTMASTER, PGC_S_OVERRIDE); } @@ -5585,7 +5599,9 @@ StartupXLOG(void) */ openLogTLI = newTLI; openLogSegNo = PmemXLogEnsurePrevMapped(EndOfLog, newTLI); - page = PmemXLogGetBufferPages() + firstIdx * (Size) XLOG_BLCKSZ; + page = PmemXLogGetBufferPages() + + (Size) XLogSegmentOffset(endOfRecoveryInfo->lastPageBeginPtr, + wal_segment_size); memset(page + len, 0, XLOG_BLCKSZ - len); /* Cache-flush and sync now */ @@ -5623,7 +5639,18 @@ StartupXLOG(void) /* * Preallocate additional log files, if wanted. */ - PreallocXlogFiles(EndOfLog, newTLI); + if (wal_pmem_map) + { + /* + * AdvanceXLInsertBuffer may reset missingContrecPtr, so we + * save and restore it for CreateOverwriteContrecordRecord. + */ + XLogRecPtr savedMissingContrecPtr = missingContrecPtr; + AdvanceXLInsertBuffer(InvalidXLogRecPtr, newTLI, true); + missingContrecPtr = savedMissingContrecPtr; + } + else + PreallocXlogFiles(EndOfLog, newTLI); /* * Okay, we're officially UP. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 5268cd95f4..776a8f7618 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -2886,7 +2886,7 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_XBLOCKS }, &XLOGbuffers, - -1, -1, (INT_MAX / XLOG_BLCKSZ), + -1, -1, INT_MAX, check_wal_buffers, NULL, NULL }, -- 2.25.1
v9-0009-For-CI-only-Setup-Cirrus-CI-for-with-libpmem.patch (application/octet-stream, 2.9K) download From 897bdbfe4a6c9a8d4c594dbea69dd96a528bd1bf Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 20 Jan 2022 12:01:56 +0900 Subject: [PATCH v9 09/10] [For CI only] Setup Cirrus CI for --with-libpmem This is work-in-progress. It supports BSD and Linux, but does not support Windows and macOS yet. --- .cirrus.yml | 10 ++++++++++ src/tools/ci/windows_build_config.pl | 1 + 2 files changed, 11 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index e5335fede7..e59e54ce45 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -25,6 +25,7 @@ env: PGCTLTIMEOUT: 120 # avoids spurious failures during parallel tests TEMP_CONFIG: ${CIRRUS_WORKING_DIR}/src/tools/ci/pg_ci_base.conf PG_TEST_EXTRA: kerberos ldap ssl + PMEM_IS_PMEM_FORCE: 1 # What files to preserve in case tests fail @@ -79,6 +80,7 @@ task: sysctl kern.corefile='/tmp/cores/%N.%P.core' setup_additional_packages_script: | #pkg install -y ... + pkg install -y devel/pmdk # NB: Intentionally build without --with-llvm. The freebsd image size is # already large enough to make VM startup slow, and even without llvm @@ -92,6 +94,7 @@ task: --with-gssapi \ --with-icu \ --with-ldap \ + --with-libpmem \ --with-libxml \ --with-libxslt \ --with-lz4 \ @@ -130,6 +133,7 @@ LINUX_CONFIGURE_FEATURES: &LINUX_CONFIGURE_FEATURES >- --with-gssapi --with-icu --with-ldap + --with-libpmem --with-libxml --with-libxslt --with-llvm @@ -189,6 +193,9 @@ task: setup_additional_packages_script: | #apt-get update #DEBIAN_FRONTEND=noninteractive apt-get -y install ... + echo 'deb http://deb.debian.org/debian bullseye universe' >>/etc/apt/sources.list + apt-get -y update + DEBIAN_FRONTEND=noninteractive apt-get -y install libpmem-dev configure_script: | su postgres <<-EOF @@ -259,6 +266,7 @@ task: # packages do not need to be downloaded. homebrew_cache: folder: $HOMEBREW_CACHE + # TODO: Install libpmem setup_additional_packages_script: | brew install \ ccache \ @@ -299,6 +307,7 @@ task: --with-gssapi \ --with-icu \ --with-ldap \ + --with-libpmem \ --with-libxml \ --with-libxslt \ --with-lz4 \ @@ -395,6 +404,7 @@ task: powershell -Command get-psdrive -psprovider filesystem set + # TODO: install libpmem setup_additional_packages_script: | REM choco install -y --no-progress ... diff --git a/src/tools/ci/windows_build_config.pl b/src/tools/ci/windows_build_config.pl index b0d4360c74..ae5fdd635a 100644 --- a/src/tools/ci/windows_build_config.pl +++ b/src/tools/ci/windows_build_config.pl @@ -8,6 +8,7 @@ $config->{"asserts"} = 1; $config->{"openssl"} = "c:/openssl/1.1/"; $config->{"perl"} = "c:/strawberry/$ENV{DEFAULT_PERL_VERSION}/perl/"; +$config->{"pmem"} = 1; # TODO $config->{"python"} = "c:/python/"; 1; -- 2.25.1
v9-0010-For-CI-only-Modify-initdb-for-wal_pmem_map-on.patch (application/octet-stream, 887B) download From 3b56af034b51fa45ab15cc51a6e3be644a5f2461 Mon Sep 17 00:00:00 2001 From: Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp> Date: Thu, 20 Jan 2022 13:00:37 +0900 Subject: [PATCH v9 10/10] [For CI only] Modify initdb for "wal_pmem_map = on" This is only for testing, not for production use, because it will modify initdb's binary. --- src/bin/initdb/initdb.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 5e36943ef3..91cb258a6f 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1175,6 +1175,12 @@ setup_config(void) repltok); #endif +#ifdef USE_LIBPMEM + conflines = replace_token(conflines, + "#wal_pmem_map = off", + "wal_pmem_map = on"); +#endif + #ifndef USE_PREFETCH conflines = replace_token(conflines, "#effective_io_concurrency = 1", -- 2.25.1
^ permalink raw reply [nested|flat] 22+ messages in thread

* Re: Map WAL segment files on PMEM as WAL buffers
@ 2022-08-01 20:40  Jacob Champion <jchampion@timescale.com>
  parent: Takashi Menjo <takashi.menjo@gmail.com>
  0 siblings, 0 replies; 22+ messages in thread

From: Jacob Champion @ 2022-08-01 20:40 UTC (permalink / raw)
  To: Takashi Menjo <takashi.menjo@gmail.com>; Andres Freund <andres@anarazel.de>; +Cc: Justin Pryzby <pryzby@telsasoft.com>; Thomas Munro <thomas.munro@gmail.com>; pgsql-hackers; Tomas Vondra <tomas.vondra@enterprisedb.com>; Masahiko Sawada <sawada.mshk@gmail.com>; Heikki Linnakangas <hlinnaka@iki.fi>; Tsunakawa, Takayuki <tsunakawa.takay@fujitsu.com>; Deng, Gang <gang.deng@intel.com>; Takashi Menjo <takashi.menjou.vg@hco.ntt.co.jp>; Matthias van de Meent <boekewurm+postgres@gmail.com>; Daniel Gustafsson <daniel@yesql.se>

As discussed in [1], we're taking this opportunity to return some
patchsets that don't appear to be getting enough reviewer interest.

This is not a rejection, since we don't necessarily think there's
anything unacceptable about the entry, but it differs from a standard
"Returned with Feedback" in that there's probably not much actionable
feedback at all. Rather than code changes, what this patch needs is more
community interest. You might

- ask people for help with your approach,
- see if there are similar patches that your code could supplement,
- get interested parties to agree to review your patch in a CF, or
- possibly present the functionality in a way that's easier to review
  overall.

(Doing these things is no guarantee that there will be interest, but
it's hopefully better than endlessly rebasing a patchset that is not
receiving any feedback from the community.)

Once you think you've built up some community support and the patchset
is ready for review, you (or any interested party) can resurrect the
patch entry by visiting

    https://commitfest.postgresql.org/38/3181/

and changing the status to "Needs Review", and then changing the
status again to "Move to next CF". (Don't forget the second step;
hopefully we will have streamlined this in the near future!)

Thanks,
--Jacob

[1] https://postgr.es/m/86140760-8ba5-6f3a-3e6e-5ca6c060bd24@timescale.com







^ permalink  raw  reply  [nested|flat] 22+ messages in thread


end of thread, other threads:[~2022-08-01 20:40 UTC | newest]

Thread overview: 22+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-05-19 01:25 Map WAL segment files on PMEM as WAL buffers Takashi Menjo <takashi.menjo@gmail.com>
2021-06-18 06:44 ` Takashi Menjo <takashi.menjo@gmail.com>
2021-06-30 04:52   ` Takashi Menjo <takashi.menjo@gmail.com>
2021-10-07 22:46     ` Matthias van de Meent <boekewurm+postgres@gmail.com>
2021-10-08 08:07       ` Takashi Menjo <takashi.menjo@gmail.com>
2021-10-28 06:09         ` Takashi Menjo <takashi.menjo@gmail.com>
2021-11-03 13:04           ` Daniel Gustafsson <daniel@yesql.se>
2021-11-04 08:46             ` Takashi Menjo <takashi.menjo@gmail.com>
2021-11-05 06:47               ` Takashi Menjo <takashi.menjo@gmail.com>
2022-01-06 01:32                 ` Takashi Menjo <takashi.menjo@gmail.com>
2022-01-06 04:00                   ` Justin Pryzby <pryzby@telsasoft.com>
2022-01-06 04:52                     ` Thomas Munro <thomas.munro@gmail.com>
2022-01-06 05:19                       ` Justin Pryzby <pryzby@telsasoft.com>
2022-01-07 03:50                         ` Takashi Menjo <takashi.menjo@gmail.com>
2022-01-07 04:43                           ` Justin Pryzby <pryzby@telsasoft.com>
2022-01-17 07:34                             ` Justin Pryzby <pryzby@telsasoft.com>
2022-01-18 10:58                               ` Takashi Menjo <takashi.menjo@gmail.com>
2022-01-19 04:41                                 ` Takashi Menjo <takashi.menjo@gmail.com>
2022-01-20 05:55                                   ` Takashi Menjo <takashi.menjo@gmail.com>
2022-03-22 00:44                                     ` Andres Freund <andres@anarazel.de>
2022-03-23 08:58                                       ` Takashi Menjo <takashi.menjo@gmail.com>
2022-08-01 20:40                                         ` Jacob Champion <jchampion@timescale.com>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox