Skip to content

Commit 9e85743

Browse files
committed
Don't include unused space in LOG_NEWPAGE records.
This is the same trick we use when taking a full page image of a buffer passed to XLogInsert.
1 parent 22122c8 commit 9e85743

File tree

11 files changed

+109
-62
lines changed

11 files changed

+109
-62
lines changed

src/backend/access/gin/gininsert.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,10 +435,10 @@ ginbuildempty(PG_FUNCTION_ARGS)
435435
START_CRIT_SECTION();
436436
GinInitMetabuffer(MetaBuffer);
437437
MarkBufferDirty(MetaBuffer);
438-
log_newpage_buffer(MetaBuffer);
438+
log_newpage_buffer(MetaBuffer, false);
439439
GinInitBuffer(RootBuffer, GIN_LEAF);
440440
MarkBufferDirty(RootBuffer);
441-
log_newpage_buffer(RootBuffer);
441+
log_newpage_buffer(RootBuffer, false);
442442
END_CRIT_SECTION();
443443

444444
/* Unlock and release the buffers. */

src/backend/access/gist/gist.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ gistbuildempty(PG_FUNCTION_ARGS)
8383
START_CRIT_SECTION();
8484
GISTInitBuffer(buffer, F_LEAF);
8585
MarkBufferDirty(buffer);
86-
log_newpage_buffer(buffer);
86+
log_newpage_buffer(buffer, true);
8787
END_CRIT_SECTION();
8888

8989
/* Unlock and release the buffer */

src/backend/access/heap/heapam.c

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6207,16 +6207,22 @@ log_heap_update(Relation reln, Buffer oldbuf,
62076207
* memory and writing them directly to smgr. If you're using buffers, call
62086208
* log_newpage_buffer instead.
62096209
*
6210-
* Note: the NEWPAGE log record is used for both heaps and indexes, so do
6211-
* not do anything that assumes we are touching a heap.
6210+
* If the page follows the standard page layout, with a PageHeader and unused
6211+
* space between pd_lower and pd_upper, set 'page_std' to TRUE. That allows
6212+
* the unused space to be left out from the WAL record, making it smaller.
62126213
*/
62136214
XLogRecPtr
62146215
log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno,
6215-
Page page)
6216+
Page page, bool page_std)
62166217
{
62176218
xl_heap_newpage xlrec;
62186219
XLogRecPtr recptr;
6219-
XLogRecData rdata[2];
6220+
XLogRecData rdata[3];
6221+
6222+
/*
6223+
* Note: the NEWPAGE log record is used for both heaps and indexes, so do
6224+
* not do anything that assumes we are touching a heap.
6225+
*/
62206226

62216227
/* NO ELOG(ERROR) from here till newpage op is logged */
62226228
START_CRIT_SECTION();
@@ -6225,15 +6231,58 @@ log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno,
62256231
xlrec.forknum = forkNum;
62266232
xlrec.blkno = blkno;
62276233

6234+
if (page_std)
6235+
{
6236+
/* Assume we can omit data between pd_lower and pd_upper */
6237+
uint16 lower = ((PageHeader) page)->pd_lower;
6238+
uint16 upper = ((PageHeader) page)->pd_upper;
6239+
6240+
if (lower >= SizeOfPageHeaderData &&
6241+
upper > lower &&
6242+
upper <= BLCKSZ)
6243+
{
6244+
xlrec.hole_offset = lower;
6245+
xlrec.hole_length = upper - lower;
6246+
}
6247+
else
6248+
{
6249+
/* No "hole" to compress out */
6250+
xlrec.hole_offset = 0;
6251+
xlrec.hole_length = 0;
6252+
}
6253+
}
6254+
else
6255+
{
6256+
/* Not a standard page header, don't try to eliminate "hole" */
6257+
xlrec.hole_offset = 0;
6258+
xlrec.hole_length = 0;
6259+
}
6260+
62286261
rdata[0].data = (char *) &xlrec;
62296262
rdata[0].len = SizeOfHeapNewpage;
62306263
rdata[0].buffer = InvalidBuffer;
62316264
rdata[0].next = &(rdata[1]);
62326265

6233-
rdata[1].data = (char *) page;
6234-
rdata[1].len = BLCKSZ;
6235-
rdata[1].buffer = InvalidBuffer;
6236-
rdata[1].next = NULL;
6266+
if (xlrec.hole_length == 0)
6267+
{
6268+
rdata[1].data = (char *) page;
6269+
rdata[1].len = BLCKSZ;
6270+
rdata[1].buffer = InvalidBuffer;
6271+
rdata[1].next = NULL;
6272+
}
6273+
else
6274+
{
6275+
/* must skip the hole */
6276+
rdata[1].data = (char *) page;
6277+
rdata[1].len = xlrec.hole_offset;
6278+
rdata[1].buffer = InvalidBuffer;
6279+
rdata[1].next = &rdata[2];
6280+
6281+
rdata[2].data = (char *) page + (xlrec.hole_offset + xlrec.hole_length);
6282+
rdata[2].len = BLCKSZ - (xlrec.hole_offset + xlrec.hole_length);
6283+
rdata[2].buffer = InvalidBuffer;
6284+
rdata[2].next = NULL;
6285+
}
62376286

62386287
recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_NEWPAGE, rdata);
62396288

@@ -6257,44 +6306,24 @@ log_newpage(RelFileNode *rnode, ForkNumber forkNum, BlockNumber blkno,
62576306
* Caller should initialize the buffer and mark it dirty before calling this
62586307
* function. This function will set the page LSN and TLI.
62596308
*
6260-
* Note: the NEWPAGE log record is used for both heaps and indexes, so do
6261-
* not do anything that assumes we are touching a heap.
6309+
* If the page follows the standard page layout, with a PageHeader and unused
6310+
* space between pd_lower and pd_upper, set 'page_std' to TRUE. That allows
6311+
* the unused space to be left out from the WAL record, making it smaller.
62626312
*/
62636313
XLogRecPtr
6264-
log_newpage_buffer(Buffer buffer)
6314+
log_newpage_buffer(Buffer buffer, bool page_std)
62656315
{
6266-
xl_heap_newpage xlrec;
6267-
XLogRecPtr recptr;
6268-
XLogRecData rdata[2];
62696316
Page page = BufferGetPage(buffer);
6317+
RelFileNode rnode;
6318+
ForkNumber forkNum;
6319+
BlockNumber blkno;
62706320

6271-
/* We should be in a critical section. */
6321+
/* Shared buffers should be modified in a critical section. */
62726322
Assert(CritSectionCount > 0);
62736323

6274-
BufferGetTag(buffer, &xlrec.node, &xlrec.forknum, &xlrec.blkno);
6275-
6276-
rdata[0].data = (char *) &xlrec;
6277-
rdata[0].len = SizeOfHeapNewpage;
6278-
rdata[0].buffer = InvalidBuffer;
6279-
rdata[0].next = &(rdata[1]);
6280-
6281-
rdata[1].data = page;
6282-
rdata[1].len = BLCKSZ;
6283-
rdata[1].buffer = InvalidBuffer;
6284-
rdata[1].next = NULL;
6285-
6286-
recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_NEWPAGE, rdata);
6287-
6288-
/*
6289-
* The page may be uninitialized. If so, we can't set the LSN and TLI
6290-
* because that would corrupt the page.
6291-
*/
6292-
if (!PageIsNew(page))
6293-
{
6294-
PageSetLSN(page, recptr);
6295-
}
6324+
BufferGetTag(buffer, &rnode, &forkNum, &blkno);
62966325

6297-
return recptr;
6326+
return log_newpage(&rnode, forkNum, blkno, page, page_std);
62986327
}
62996328

63006329
/*
@@ -6582,12 +6611,15 @@ static void
65826611
heap_xlog_newpage(XLogRecPtr lsn, XLogRecord *record)
65836612
{
65846613
xl_heap_newpage *xlrec = (xl_heap_newpage *) XLogRecGetData(record);
6614+
char *blk = ((char *) xlrec) + sizeof(xl_heap_newpage);
65856615
Buffer buffer;
65866616
Page page;
65876617

65886618
/* Backup blocks are not used in newpage records */
65896619
Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
65906620

6621+
Assert(record->xl_len == SizeOfHeapNewpage + BLCKSZ - xlrec->hole_length);
6622+
65916623
/*
65926624
* Note: the NEWPAGE log record is used for both heaps and indexes, so do
65936625
* not do anything that assumes we are touching a heap.
@@ -6598,8 +6630,19 @@ heap_xlog_newpage(XLogRecPtr lsn, XLogRecord *record)
65986630
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
65996631
page = (Page) BufferGetPage(buffer);
66006632

6601-
Assert(record->xl_len == SizeOfHeapNewpage + BLCKSZ);
6602-
memcpy(page, (char *) xlrec + SizeOfHeapNewpage, BLCKSZ);
6633+
if (xlrec->hole_length == 0)
6634+
{
6635+
memcpy((char *) page, blk, BLCKSZ);
6636+
}
6637+
else
6638+
{
6639+
memcpy((char *) page, blk, xlrec->hole_offset);
6640+
/* must zero-fill the hole */
6641+
MemSet((char *) page + xlrec->hole_offset, 0, xlrec->hole_length);
6642+
memcpy((char *) page + (xlrec->hole_offset + xlrec->hole_length),
6643+
blk + xlrec->hole_offset,
6644+
BLCKSZ - (xlrec->hole_offset + xlrec->hole_length));
6645+
}
66036646

66046647
/*
66056648
* The page may be uninitialized. If so, we can't set the LSN because that

src/backend/access/heap/rewriteheap.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@ end_heap_rewrite(RewriteState state)
277277
log_newpage(&state->rs_new_rel->rd_node,
278278
MAIN_FORKNUM,
279279
state->rs_blockno,
280-
state->rs_buffer);
280+
state->rs_buffer,
281+
true);
281282
RelationOpenSmgr(state->rs_new_rel);
282283

283284
PageSetChecksumInplace(state->rs_buffer, state->rs_blockno);
@@ -622,7 +623,8 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
622623
log_newpage(&state->rs_new_rel->rd_node,
623624
MAIN_FORKNUM,
624625
state->rs_blockno,
625-
page);
626+
page,
627+
true);
626628

627629
/*
628630
* Now write the page. We say isTemp = true even if it's not a

src/backend/access/nbtree/nbtree.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ btbuildempty(PG_FUNCTION_ARGS)
222222
(char *) metapage, true);
223223
if (XLogIsNeeded())
224224
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
225-
BTREE_METAPAGE, metapage);
225+
BTREE_METAPAGE, metapage, false);
226226

227227
/*
228228
* An immediate sync is require even if we xlog'd the page, because the

src/backend/access/nbtree/nbtsort.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ _bt_blwritepage(BTWriteState *wstate, Page page, BlockNumber blkno)
274274
if (wstate->btws_use_wal)
275275
{
276276
/* We use the heap NEWPAGE record type for this */
277-
log_newpage(&wstate->index->rd_node, MAIN_FORKNUM, blkno, page);
277+
log_newpage(&wstate->index->rd_node, MAIN_FORKNUM, blkno, page, true);
278278
}
279279

280280
/*

src/backend/access/spgist/spginsert.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ spgbuildempty(PG_FUNCTION_ARGS)
169169
(char *) page, true);
170170
if (XLogIsNeeded())
171171
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
172-
SPGIST_METAPAGE_BLKNO, page);
172+
SPGIST_METAPAGE_BLKNO, page, false);
173173

174174
/* Likewise for the root page. */
175175
SpGistInitPage(page, SPGIST_LEAF);
@@ -179,7 +179,7 @@ spgbuildempty(PG_FUNCTION_ARGS)
179179
(char *) page, true);
180180
if (XLogIsNeeded())
181181
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
182-
SPGIST_ROOT_BLKNO, page);
182+
SPGIST_ROOT_BLKNO, page, true);
183183

184184
/* Likewise for the null-tuples root page. */
185185
SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
@@ -189,7 +189,7 @@ spgbuildempty(PG_FUNCTION_ARGS)
189189
(char *) page, true);
190190
if (XLogIsNeeded())
191191
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
192-
SPGIST_NULL_BLKNO, page);
192+
SPGIST_NULL_BLKNO, page, true);
193193

194194
/*
195195
* An immediate sync is required even if we xlog'd the pages, because the

src/backend/commands/tablecmds.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9130,9 +9130,13 @@ copy_relation_data(SMgrRelation src, SMgrRelation dst,
91309130
src->smgr_rnode.backend,
91319131
forkNum))));
91329132

9133-
/* XLOG stuff */
9133+
/*
9134+
* WAL-log the copied page. Unfortunately we don't know what kind of
9135+
* a page this is, so we have to log the full page including any
9136+
* unused space.
9137+
*/
91349138
if (use_wal)
9135-
log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page);
9139+
log_newpage(&dst->smgr_rnode.node, forkNum, blkno, page, false);
91369140

91379141
PageSetChecksumInplace(page, blkno);
91389142

src/backend/commands/vacuumlazy.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -699,14 +699,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
699699
* which will cause a PANIC. To prevent that, check whether
700700
* the page has been previously WAL-logged, and if not, do that
701701
* now.
702-
*
703-
* XXX: It would be nice to use a logging method supporting
704-
* standard buffers here since log_newpage_buffer() will write
705-
* the full block instead of omitting the hole.
706702
*/
707703
if (RelationNeedsWAL(onerel) &&
708704
PageGetLSN(page) == InvalidXLogRecPtr)
709-
log_newpage_buffer(buf);
705+
log_newpage_buffer(buf, true);
710706

711707
PageSetAllVisible(page);
712708
visibilitymap_set(onerel, blkno, buf, InvalidXLogRecPtr,

src/include/access/heapam_xlog.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,12 @@ typedef struct xl_heap_newpage
198198
RelFileNode node;
199199
ForkNumber forknum;
200200
BlockNumber blkno; /* location of new page */
201-
/* entire page contents follow at end of record */
201+
uint16 hole_offset; /* number of bytes before "hole" */
202+
uint16 hole_length; /* number of bytes in "hole" */
203+
/* entire page contents (minus the hole) follow at end of record */
202204
} xl_heap_newpage;
203205

204-
#define SizeOfHeapNewpage (offsetof(xl_heap_newpage, blkno) + sizeof(BlockNumber))
206+
#define SizeOfHeapNewpage (offsetof(xl_heap_newpage, hole_length) + sizeof(uint16))
205207

206208
/* flags for infobits_set */
207209
#define XLHL_XMAX_IS_MULTI 0x01
@@ -282,7 +284,7 @@ extern XLogRecPtr log_heap_freeze(Relation reln, Buffer buffer,
282284
extern XLogRecPtr log_heap_visible(RelFileNode rnode, Buffer heap_buffer,
283285
Buffer vm_buffer, TransactionId cutoff_xid);
284286
extern XLogRecPtr log_newpage(RelFileNode *rnode, ForkNumber forkNum,
285-
BlockNumber blk, Page page);
286-
extern XLogRecPtr log_newpage_buffer(Buffer buffer);
287+
BlockNumber blk, Page page, bool page_std);
288+
extern XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std);
287289

288290
#endif /* HEAPAM_XLOG_H */

src/include/access/xlog_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ typedef struct BkpBlock
5555
/*
5656
* Each page of XLOG file has a header like this:
5757
*/
58-
#define XLOG_PAGE_MAGIC 0xD077 /* can be used as WAL version indicator */
58+
#define XLOG_PAGE_MAGIC 0xD078 /* can be used as WAL version indicator */
5959

6060
typedef struct XLogPageHeaderData
6161
{

0 commit comments

Comments
 (0)