diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 8896988e5aa9a8f53e6b81190bb13f6e1a08b4ee..f0794467ba47c748717cfb99b65061818d9038ff 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1944,6 +1944,32 @@ include 'filename' </listitem> </varlistentry> + <varlistentry id="guc-wal-log-hintbits" xreflabel="wal_log_hintbits"> + <term><varname>wal_log_hintbits</varname> (<type>boolean</type>)</term> + <indexterm> + <primary><varname>wal_log_hintbits</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + When this parameter is <literal>on</>, the <productname>PostgreSQL</> + server writes the entire content of each disk page to WAL during the + first modification of that page after a checkpoint, even for + non-critical modifications of so-called hint bits. + </para> + + <para> + If data checksums are enabled, hint bit updates are always WAL-logged + and this setting is ignored. You can use this setting to test how much + extra WAL-logging would occur if your database had data checksums + enabled. + </para> + + <para> + This parameter can only be set at server start. The default value is <literal>off</>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-buffers" xreflabel="wal_buffers"> <term><varname>wal_buffers</varname> (<type>integer</type>)</term> <indexterm> diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 09174b472b06a00b6aa74db9b7f1690e7a34eafb..91cfae1603d674632b2376a5d4bbb895e20fb89e 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -6271,7 +6271,7 @@ log_heap_visible(RelFileNode rnode, Buffer heap_buffer, Buffer vm_buffer, rdata[1].buffer_std = false; rdata[1].next = NULL; - if (DataChecksumsEnabled()) + if (XLogHintBitIsNeeded()) { rdata[1].next = &(rdata[2]); diff --git a/src/backend/access/heap/visibilitymap.c b/src/backend/access/heap/visibilitymap.c index 7f40d89b9f1ac722772ec52325333cd4c0f6ae95..60ee76a208133f55b419f8e0993836bcf89e0540 100644 --- a/src/backend/access/heap/visibilitymap.c +++ b/src/backend/access/heap/visibilitymap.c @@ -287,10 +287,10 @@ visibilitymap_set(Relation rel, BlockNumber heapBlk, Buffer heapBuf, cutoff_xid); /* - * If data checksums are enabled, we need to protect the heap - * page from being torn. + * If data checksums are enabled (or wal_log_hintbits=on), we + * need to protect the heap page from being torn. */ - if (DataChecksumsEnabled()) + if (XLogHintBitIsNeeded()) { Page heapPage = BufferGetPage(heapBuf); diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a76aef37f3d2f6066153fa6080d0183efe224390..0efb50b17ef9669a977d9dd4a4592a476bbb75c8 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -79,6 +79,7 @@ bool XLogArchiveMode = false; char *XLogArchiveCommand = NULL; bool EnableHotStandby = false; bool fullPageWrites = true; +bool walLogHintbits = false; bool log_checkpoints = false; int sync_method = DEFAULT_SYNC_METHOD; int wal_level = WAL_LEVEL_MINIMAL; @@ -5270,6 +5271,7 @@ BootStrapXLOG(void) ControlFile->max_prepared_xacts = max_prepared_xacts; ControlFile->max_locks_per_xact = max_locks_per_xact; ControlFile->wal_level = wal_level; + ControlFile->wal_log_hintbits = walLogHintbits; ControlFile->data_checksum_version = bootstrap_data_checksum_version; /* some additional ControlFile fields are set in WriteControlFile() */ @@ -9058,6 +9060,7 @@ static void XLogReportParameters(void) { if (wal_level != ControlFile->wal_level || + walLogHintbits != ControlFile->wal_log_hintbits || MaxConnections != ControlFile->MaxConnections || max_worker_processes != ControlFile->max_worker_processes || max_prepared_xacts != ControlFile->max_prepared_xacts || @@ -9080,6 +9083,7 @@ XLogReportParameters(void) xlrec.max_prepared_xacts = max_prepared_xacts; xlrec.max_locks_per_xact = max_locks_per_xact; xlrec.wal_level = wal_level; + xlrec.wal_log_hintbits = walLogHintbits; rdata.buffer = InvalidBuffer; rdata.data = (char *) &xlrec; @@ -9094,6 +9098,7 @@ XLogReportParameters(void) ControlFile->max_prepared_xacts = max_prepared_xacts; ControlFile->max_locks_per_xact = max_locks_per_xact; ControlFile->wal_level = wal_level; + ControlFile->wal_log_hintbits = walLogHintbits; UpdateControlFile(); } } @@ -9480,6 +9485,7 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record) ControlFile->max_prepared_xacts = xlrec.max_prepared_xacts; ControlFile->max_locks_per_xact = xlrec.max_locks_per_xact; ControlFile->wal_level = xlrec.wal_level; + ControlFile->wal_log_hintbits = walLogHintbits; /* * Update minRecoveryPoint to ensure that if recovery is aborted, we diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index f84839181161e2aaedf3ef6a806d0647116ab5c0..081165faef09484f607562e0ec6fb8650859389e 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -2626,16 +2626,15 @@ MarkBufferDirtyHint(Buffer buffer, bool buffer_std) bool delayChkpt = false; /* - * If checksums are enabled, and the buffer is permanent, then a full - * page image may be required even for some hint bit updates to - * protect against torn pages. This full page image is only necessary + * If we need to protect hint bit updates from torn writes, WAL-log a + * full page image of the page. This full page image is only necessary * if the hint bit update is the first change to the page since the * last checkpoint. * * We don't check full_page_writes here because that logic is included * when we call XLogInsert() since the value changes dynamically. */ - if (DataChecksumsEnabled() && (bufHdr->flags & BM_PERMANENT)) + if (XLogHintBitIsNeeded() && (bufHdr->flags & BM_PERMANENT)) { /* * If we're in recovery we cannot dirty a page because of a hint. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index e69e132f056288d5bcb0fe3f6d230a7e6256b3a4..b0c14a2dfce41b1631e15ce3b26c6b5feaeb49e9 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -866,6 +866,17 @@ static struct config_bool ConfigureNamesBool[] = true, NULL, NULL, NULL }, + + { + {"wal_log_hintbits", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Writes full pages to WAL when first modified after a checkpoint, even for a non-critical modifications"), + NULL + }, + &walLogHintbits, + false, + NULL, NULL, NULL + }, + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index f8bdce34d3307aba3cbadf152525b95286835a11..d049159444f481815be4ac180e649ea5eae31696 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -184,6 +184,7 @@ # fsync_writethrough # open_sync #full_page_writes = on # recover from partial page writes +#wal_log_hintbits = off # also do full pages writes of non-critical updates #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers # (change requires restart) #wal_writer_delay = 200ms # 1-10000 milliseconds diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 8c6cf24d23755217d400536a5c26ac21efea6d36..da48e98ff99282236f83b36ed0a7ecbff19f32fe 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -260,6 +260,8 @@ main(int argc, char *argv[]) ControlFile.backupEndRequired ? _("yes") : _("no")); printf(_("Current wal_level setting: %s\n"), wal_level_str(ControlFile.wal_level)); + printf(_("Current wal_log_hintbits setting: %s\n"), + ControlFile.wal_log_hintbits ? _("on") : _("off")); printf(_("Current max_connections setting: %d\n"), ControlFile.MaxConnections); printf(_("Current max_worker_processes setting: %d\n"), diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c index ed0f945679649d3bd21d5c2b65e3660b87824e50..da13ff4d134b7c185b3c62af6cc6eeec9f2ab31e 100644 --- a/src/bin/pg_resetxlog/pg_resetxlog.c +++ b/src/bin/pg_resetxlog/pg_resetxlog.c @@ -525,6 +525,7 @@ GuessControlValues(void) /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */ ControlFile.wal_level = WAL_LEVEL_MINIMAL; + ControlFile.wal_log_hintbits = false; ControlFile.MaxConnections = 100; ControlFile.max_worker_processes = 8; ControlFile.max_prepared_xacts = 0; @@ -721,6 +722,7 @@ RewriteControlFile(void) * anyway at startup. */ ControlFile.wal_level = WAL_LEVEL_MINIMAL; + ControlFile.wal_log_hintbits = false; ControlFile.MaxConnections = 100; ControlFile.max_worker_processes = 8; ControlFile.max_prepared_xacts = 0; diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 7415a261bbdfe0c673a0c6f5985cbce4489c160c..e69accd4b2e010fce8376fe31729d13d8b0f04ee 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -189,6 +189,7 @@ extern bool XLogArchiveMode; extern char *XLogArchiveCommand; extern bool EnableHotStandby; extern bool fullPageWrites; +extern bool walLogHintbits; extern bool log_checkpoints; extern int num_xloginsert_slots; @@ -211,6 +212,17 @@ extern int wal_level; */ #define XLogIsNeeded() (wal_level >= WAL_LEVEL_ARCHIVE) +/* + * Is a full-page image needed for hint bit updates? + * + * Normally, we don't WAL-log hint bit updates, but if checksums are enabled, + * we have to protect them against torn page writes. When you only set + * individual bits on a page, it's still consistent no matter what combination + * of the bits make it to disk, but the checksum wouldn't match. Also WAL-log + * them if forced by wal_log_hintbits=on. + */ +#define XLogHintBitIsNeeded() (DataChecksumsEnabled() || walLogHintbits) + /* Do we need to WAL-log information required only for Hot Standby and logical replication? */ #define XLogStandbyInfoActive() (wal_level >= WAL_LEVEL_HOT_STANDBY) diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 64ba55355b9857184a1679fca4850bb700df2ca8..c78a2fbfae8f0668bb14555402de22bc5863e8a5 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -209,6 +209,7 @@ typedef struct xl_parameter_change int max_prepared_xacts; int max_locks_per_xact; int wal_level; + bool wal_log_hintbits; } xl_parameter_change; /* logs restore point */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 759395a24b942be8480456b0bf85f37f6025d847..b7610e8c3f86a12bad4923bc09efd4703f4e365c 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201311261 +#define CATALOG_VERSION_NO 201312131 #endif diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index 637221e6347c1e673a24675176dce751caa9d872..8f23508ad0f16a6dd2bc337cbe18eef2b0836efc 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -171,6 +171,7 @@ typedef struct ControlFileData * or hot standby. */ int wal_level; + bool wal_log_hintbits; int MaxConnections; int max_worker_processes; int max_prepared_xacts;