*** a/doc/src/sgml/config.sgml --- b/doc/src/sgml/config.sgml *************** *** 1876,1881 **** include 'filename' --- 1876,1920 ---- + + wal_log_hintbits (boolean) + + wal_log_hintbits configuration parameter + + + + When this parameter is on, the PostgreSQL + server writes the full page image of disk page to WAL during first time + updating the hint bits for that page after a checkpoint. This is needed + to maintain file system level consistency between master and standby + server in terms of hint bits. + + + + This functionality is useful for differential backups as well as to speed + up failback operation by avoiding need of fresh backup in Streaming Replication. + + + + Turning on this parameter generates more WAL than normal operation but it + has some performance improvement on standby side. Standby receives readymade + WAL records for each changed hint bit and those can get directly replayed + like other WAL. + + + + The feature is little bit similar to checksum feature, the only difference + is that WAL gets generated only when hint bits get updated and checksum + calculation part gets skipped. PostgreSQL will skip this functionality + if you already have checksum feature enabled. + + + + This parameter can only be set at server start. The default value is off. + + + + wal_buffers (integer) *** a/src/backend/access/transam/xlog.c --- b/src/backend/access/transam/xlog.c *************** *** 79,84 **** bool XLogArchiveMode = false; --- 79,85 ---- 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; *************** *** 5265,5270 **** BootStrapXLOG(void) --- 5266,5272 ---- ControlFile->max_locks_per_xact = max_locks_per_xact; ControlFile->wal_level = wal_level; ControlFile->data_checksum_version = bootstrap_data_checksum_version; + ControlFile->wal_log_hintbits = walLogHintbits; /* some additional ControlFile fields are set in WriteControlFile() */ *************** *** 8921,8926 **** static void --- 8923,8929 ---- 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 || *************** *** 8943,8948 **** XLogReportParameters(void) --- 8946,8952 ---- 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; *************** *** 8957,8962 **** XLogReportParameters(void) --- 8961,8967 ---- 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(); } } *************** *** 9343,9348 **** xlog_redo(XLogRecPtr lsn, XLogRecord *record) --- 9348,9354 ---- 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 *** a/src/backend/postmaster/postmaster.c --- b/src/backend/postmaster/postmaster.c *************** *** 817,822 **** PostmasterMain(int argc, char *argv[]) --- 817,825 ---- if (XLogArchiveMode && wal_level == WAL_LEVEL_MINIMAL) ereport(ERROR, (errmsg("WAL archival (archive_mode=on) requires wal_level \"archive\" or \"hot_standby\""))); + if (walLogHintbits && wal_level == WAL_LEVEL_MINIMAL) + ereport(ERROR, + (errmsg("Logging of hint bits (wal_log_hintbits = on) requires wal_level \"archive\" or \"hot_standby\""))); if (max_wal_senders > 0 && wal_level == WAL_LEVEL_MINIMAL) ereport(ERROR, (errmsg("WAL streaming (max_wal_senders > 0) requires wal_level \"archive\" or \"hot_standby\""))); *** a/src/backend/storage/buffer/bufmgr.c --- b/src/backend/storage/buffer/bufmgr.c *************** *** 2626,2633 **** 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 the hint bit update is the first change to the page since the * last checkpoint. --- 2626,2633 ---- bool delayChkpt = false; /* ! * If checksums or logging of hint bits 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 the hint bit update is the first change to the page since the * last checkpoint. *************** *** 2635,2641 **** MarkBufferDirtyHint(Buffer buffer, bool buffer_std) * 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 we're in recovery we cannot dirty a page because of a hint. --- 2635,2641 ---- * We don't check full_page_writes here because that logic is included * when we call XLogInsert() since the value changes dynamically. */ ! if ((DataChecksumsEnabled() || walLogHintbits) && (bufHdr->flags & BM_PERMANENT)) { /* * If we're in recovery we cannot dirty a page because of a hint. *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 854,859 **** static struct config_bool ConfigureNamesBool[] = --- 854,870 ---- true, NULL, NULL, NULL }, + + { + {"wal_log_hintbits", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Writes hint bits log to WAL when first updated after a checkpoint"), + NULL + }, + &walLogHintbits, + false, + NULL, NULL, NULL + }, + { {"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs each checkpoint."), *** a/src/backend/utils/misc/postgresql.conf.sample --- b/src/backend/utils/misc/postgresql.conf.sample *************** *** 181,186 **** --- 181,187 ---- # fsync_writethrough # open_sync #full_page_writes = on # recover from partial page writes + #wal_log_hintbits = off # turns logging of WAL when updating hint bits #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers # (change requires restart) #wal_writer_delay = 200ms # 1-10000 milliseconds *** a/src/bin/pg_controldata/pg_controldata.c --- b/src/bin/pg_controldata/pg_controldata.c *************** *** 291,295 **** main(int argc, char *argv[]) --- 291,297 ---- (ControlFile.float8ByVal ? _("by value") : _("by reference"))); printf(_("Data page checksum version: %u\n"), ControlFile.data_checksum_version); + printf(_("Wal log hintbits: %u\n"), + ControlFile.wal_log_hintbits); return 0; } *** a/src/include/access/xlog.h --- b/src/include/access/xlog.h *************** *** 189,194 **** extern bool XLogArchiveMode; --- 189,195 ---- extern char *XLogArchiveCommand; extern bool EnableHotStandby; extern bool fullPageWrites; + extern bool walLogHintbits; extern bool log_checkpoints; extern int num_xloginsert_slots; *** a/src/include/access/xlog_internal.h --- b/src/include/access/xlog_internal.h *************** *** 209,214 **** typedef struct xl_parameter_change --- 209,215 ---- int max_prepared_xacts; int max_locks_per_xact; int wal_level; + bool wal_log_hintbits; } xl_parameter_change; /* logs restore point */ *** a/src/include/catalog/pg_control.h --- b/src/include/catalog/pg_control.h *************** *** 219,224 **** typedef struct ControlFileData --- 219,227 ---- /* CRC of all above ... MUST BE LAST! */ pg_crc32 crc; + + /* Enable logging WAL when updating hint bits */ + bool wal_log_hintbits; } ControlFileData; /*