1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-01-09 14:45:47 +02:00
pg_probackup/parsexlog.c
Michael Paquier 04834e73c7 Page-level backup using block tracking in WAL records
This commit improves the performance of page-level, or differential
backup, by not having to scan anymore all the pages of a relation file,
something that can be very long on large data sets, but by scanning the
list of blocks changed by WAL records since the last full or differential
backup.

As a restriction and to avoid potential data corruption should hint-bit
updates occur on a page, backups can only be taken from a server that has
wal_log_hints or data checksums enabled.

Base patch by Yury Zhuravlev, heavily modified by me.
2016-01-15 23:47:38 +09:00

245 lines
6.1 KiB
C

/*-------------------------------------------------------------------------
*
* parsexlog.c
* Functions for reading Write-Ahead-Log
*
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "pg_arman.h"
#include <unistd.h>
#include "commands/dbcommands_xlog.h"
#include "catalog/storage_xlog.h"
/*
* RmgrNames is an array of resource manager names, to make error messages
* a bit nicer.
*/
#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup) \
name,
static const char *RmgrNames[RM_MAX_ID + 1] = {
#include "access/rmgrlist.h"
};
static void extractPageInfo(XLogReaderState *record);
static int xlogreadfd = -1;
static XLogSegNo xlogreadsegno = -1;
static char xlogfpath[MAXPGPATH];
typedef struct XLogPageReadPrivate
{
const char *archivedir;
TimeLineID tli;
} XLogPageReadPrivate;
static int SimpleXLogPageRead(XLogReaderState *xlogreader,
XLogRecPtr targetPagePtr,
int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
TimeLineID *pageTLI);
/*
* Read WAL from the archive directory, starting from 'startpoint' on the
* given timeline, until 'endpoint'. Make note of the data blocks touched
* by the WAL records, and return them in a page map.
*/
void
extractPageMap(const char *archivedir, XLogRecPtr startpoint, TimeLineID tli,
XLogRecPtr endpoint)
{
XLogRecord *record;
XLogReaderState *xlogreader;
char *errormsg;
XLogPageReadPrivate private;
private.archivedir = archivedir;
private.tli = tli;
xlogreader = XLogReaderAllocate(&SimpleXLogPageRead, &private);
if (xlogreader == NULL)
elog(ERROR, "out of memory");
do
{
record = XLogReadRecord(xlogreader, startpoint, &errormsg);
if (record == NULL)
{
XLogRecPtr errptr;
errptr = startpoint ? startpoint : xlogreader->EndRecPtr;
if (errormsg)
elog(ERROR, "could not read WAL record at %X/%X: %s",
(uint32) (errptr >> 32), (uint32) (errptr),
errormsg);
else
elog(ERROR, "could not read WAL record at %X/%X",
(uint32) (startpoint >> 32),
(uint32) (startpoint));
}
extractPageInfo(xlogreader);
startpoint = InvalidXLogRecPtr; /* continue reading at next record */
} while (xlogreader->ReadRecPtr != endpoint);
XLogReaderFree(xlogreader);
if (xlogreadfd != -1)
{
close(xlogreadfd);
xlogreadfd = -1;
}
}
/* XLogreader callback function, to read a WAL page */
static int
SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
int reqLen, XLogRecPtr targetRecPtr, char *readBuf,
TimeLineID *pageTLI)
{
XLogPageReadPrivate *private = (XLogPageReadPrivate *) xlogreader->private_data;
uint32 targetPageOff;
XLogRecPtr targetSegEnd;
XLogSegNo targetSegNo;
XLByteToSeg(targetPagePtr, targetSegNo);
XLogSegNoOffsetToRecPtr(targetSegNo + 1, 0, targetSegEnd);
targetPageOff = targetPagePtr % XLogSegSize;
/*
* See if we need to switch to a new segment because the requested record
* is not in the currently open one.
*/
if (xlogreadfd >= 0 && !XLByteInSeg(targetPagePtr, xlogreadsegno))
{
close(xlogreadfd);
xlogreadfd = -1;
}
XLByteToSeg(targetPagePtr, xlogreadsegno);
if (xlogreadfd < 0)
{
char xlogfname[MAXFNAMELEN];
XLogFileName(xlogfname, private->tli, xlogreadsegno);
snprintf(xlogfpath, MAXPGPATH, "%s/%s", private->archivedir,
xlogfname);
elog(LOG, "opening WAL segment \"%s\"", xlogfpath);
xlogreadfd = open(xlogfpath, O_RDONLY | PG_BINARY, 0);
if (xlogreadfd < 0)
{
elog(WARNING, "could not open WAL segment \"%s\": %s",
xlogfpath, strerror(errno));
return -1;
}
}
/*
* At this point, we have the right segment open.
*/
Assert(xlogreadfd != -1);
/* Read the requested page */
if (lseek(xlogreadfd, (off_t) targetPageOff, SEEK_SET) < 0)
{
elog(WARNING, "could not seek in file \"%s\": %s", xlogfpath,
strerror(errno));
return -1;
}
if (read(xlogreadfd, readBuf, XLOG_BLCKSZ) != XLOG_BLCKSZ)
{
elog(WARNING, "could not read from file \"%s\": %s",
xlogfpath, strerror(errno));
return -1;
}
Assert(targetSegNo == xlogreadsegno);
*pageTLI = private->tli;
return XLOG_BLCKSZ;
}
/*
* Extract information on which blocks the current record modifies.
*/
static void
extractPageInfo(XLogReaderState *record)
{
int block_id;
RmgrId rmid = XLogRecGetRmid(record);
uint8 info = XLogRecGetInfo(record);
uint8 rminfo = info & ~XLR_INFO_MASK;
/* Is this a special record type that I recognize? */
if (rmid == RM_DBASE_ID && rminfo == XLOG_DBASE_CREATE)
{
/*
* New databases can be safely ignored. They would be completely
* copied if found.
*/
}
else if (rmid == RM_DBASE_ID && rminfo == XLOG_DBASE_DROP)
{
/*
* An existing database was dropped. It is fine to ignore that
* they will be removed appropriately.
*/
}
else if (rmid == RM_SMGR_ID && rminfo == XLOG_SMGR_CREATE)
{
/*
* We can safely ignore these. The file will be removed when
* combining the backups in the case of differential on.
*/
}
else if (rmid == RM_SMGR_ID && rminfo == XLOG_SMGR_TRUNCATE)
{
/*
* We can safely ignore these. When we compare the sizes later on,
* we'll notice that they differ, and copy the missing tail from
* source system.
*/
}
else if (info & XLR_SPECIAL_REL_UPDATE)
{
/*
* This record type modifies a relation file in some special way, but
* we don't recognize the type. That's bad - we don't know how to
* track that change.
*/
elog(ERROR, "WAL record modifies a relation, but record type is not recognized\n"
"lsn: %X/%X, rmgr: %s, info: %02X",
(uint32) (record->ReadRecPtr >> 32), (uint32) (record->ReadRecPtr),
RmgrNames[rmid], info);
}
for (block_id = 0; block_id <= record->max_block_id; block_id++)
{
RelFileNode rnode;
ForkNumber forknum;
BlockNumber blkno;
if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
continue;
/* We only care about the main fork; others are copied in toto */
if (forknum != MAIN_FORKNUM)
continue;
process_block_change(forknum, rnode, blkno);
}
}