v2-0003-Add-support-for-the-incremental-backup.patch

text/x-patch

Filename: v2-0003-Add-support-for-the-incremental-backup.patch
Type: text/x-patch
Part: 2
Message: Re: block-level incremental backup

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v2-0003
Subject: Add support for the incremental backup.
File+
doc/src/sgml/protocol.sgml 49 1
doc/src/sgml/ref/pg_basebackup.sgml 23 0
src/backend/access/transam/xlog.c 18 1
src/backend/access/transam/xlogfuncs.c 4 2
src/backend/replication/basebackup.c 298 13
src/backend/storage/file/fd.c 29 0
src/bin/pg_basebackup/t/010_pg_basebackup.pl 1 1
src/include/access/xlog.h 2 1
src/include/replication/basebackup.h 13 0
src/include/storage/fd.h 1 0
From 031d3fecd3ceb70de173bd63214fd083a547ae32 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 9 Sep 2019 11:05:50 +0530
Subject: [PATCH v2 3/4] Add support for the incremental backup.

If file is modified 90% or more, we send a whole file else we send
only those blocks which are modified. The file is named .partial and
has following header details:

 - magic number, set to 0x494E4352 (4 bytes)
 - checksum, of whole file except actual blocks (4 bytes)
 - number of blocks in this .partial file (4 bytes)
 - all modified block numbers (4 bytes each)
 - modified blocks
---
 doc/src/sgml/protocol.sgml                   |  50 ++++-
 doc/src/sgml/ref/pg_basebackup.sgml          |  23 ++
 src/backend/access/transam/xlog.c            |  19 +-
 src/backend/access/transam/xlogfuncs.c       |   6 +-
 src/backend/replication/basebackup.c         | 311 +++++++++++++++++++++++++--
 src/backend/storage/file/fd.c                |  29 +++
 src/bin/pg_basebackup/t/010_pg_basebackup.pl |   2 +-
 src/include/access/xlog.h                    |   3 +-
 src/include/replication/basebackup.h         |  13 ++
 src/include/storage/fd.h                     |   1 +
 10 files changed, 438 insertions(+), 19 deletions(-)
 mode change 100644 => 100755 src/bin/pg_basebackup/t/010_pg_basebackup.pl

diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 8027521..21be571 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -2466,7 +2466,7 @@ The commands accepted in replication mode are:
   </varlistentry>
 
   <varlistentry>
-    <term><literal>BASE_BACKUP</literal> [ <literal>LABEL</literal> <replaceable>'label'</replaceable> ] [ <literal>PROGRESS</literal> ] [ <literal>FAST</literal> ] [ <literal>WAL</literal> ] [ <literal>NOWAIT</literal> ] [ <literal>MAX_RATE</literal> <replaceable>rate</replaceable> ] [ <literal>TABLESPACE_MAP</literal> ] [ <literal>NOVERIFY_CHECKSUMS</literal> ]
+    <term><literal>BASE_BACKUP</literal> [ <literal>LABEL</literal> <replaceable>'label'</replaceable> ] [ <literal>PROGRESS</literal> ] [ <literal>FAST</literal> ] [ <literal>WAL</literal> ] [ <literal>NOWAIT</literal> ] [ <literal>MAX_RATE</literal> <replaceable>rate</replaceable> ] [ <literal>TABLESPACE_MAP</literal> ] [ <literal>NOVERIFY_CHECKSUMS</literal> ] [ <literal>LSN</literal> <replaceable>'lsn'</replaceable> ]
      <indexterm><primary>BASE_BACKUP</primary></indexterm>
     </term>
     <listitem>
@@ -2576,6 +2576,22 @@ The commands accepted in replication mode are:
          </para>
         </listitem>
        </varlistentry>
+
+       <varlistentry>
+        <term><literal>LSN</literal> <replaceable>'lsn'</replaceable></term>
+        <listitem>
+         <para>
+          Includes only those data blocks in backup which has LSN greater than
+          or equal to the given lsn. However, if 90% or more data blocks are
+          modified in the file, then sends the entire file. Otherwise, creates
+          a <filename>.partial</filename> file containing only the blocks which
+          are modified and sends that instead. The <filename>.partial</filename>
+          file has its own header followed by the actual data blocks. Note that
+          only relation files are considered here, all other files are sent as
+          is.
+         </para>
+        </listitem>
+       </varlistentry>
       </variablelist>
      </para>
      <para>
@@ -2698,6 +2714,38 @@ The commands accepted in replication mode are:
       Owner, group, and file mode are set if the underlying file system on
       the server supports it.
      </para>
+     <para>
+      An incremental backup's <filename>.partial</filename> file has the
+      following format:
+      <itemizedlist>
+       <listitem>
+        <para>
+         Starts with a 4-byte magic number
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         Followed by a 4-byte CRC of the header (containing a magic number,
+         count of the number of blocks, and all block numbers)
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         Then a 4-byte count of the number of blocks included in the file
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         Then the block numbers, each as a 4-byte quantity
+        </para>
+       </listitem>
+       <listitem>
+        <para>
+         Followed by the actual data blocks in order with the block numbers
+        </para>
+       </listitem>
+      </itemizedlist>
+     </para>
     </listitem>
   </varlistentry>
 </variablelist>
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index fc9e222..9e2b9b8 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -408,6 +408,21 @@ PostgreSQL documentation
      </varlistentry>
 
      <varlistentry>
+      <term><option>--lsn=<replaceable class="parameter">LSN</replaceable></option></term>
+      <listitem>
+       <para>
+        Takes an incremental backup, using LSN as a threshold. Only the blocks
+        which are modified after this given LSN will be backed up. The file
+        which has these partial blocks has .partial as an extension. Backup
+        taken in this manner has to be combined with the full backup with the
+        <command>pg_combinebackup</command> utility. The value of LSN should
+        match the <literal>START WAL LOCATION</literal> of previously taken
+        full or incremental backup from <literal>backup_label</literal> file.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-n</option></term>
       <term><option>--no-clean</option></term>
       <listitem>
@@ -792,6 +807,14 @@ PostgreSQL documentation
 <prompt>$</prompt> <userinput>pg_basebackup -D backup/data -T /opt/ts=$(pwd)/backup/ts</userinput>
 </screen>
   </para>
+
+  <para>
+   To create an incremental backup having LSN greater than or equal to
+   <literal>5/19000060</literal>:
+<screen>
+<prompt>$</prompt> <userinput>pg_basebackup -D incbackup --lsn='5/19000060'</userinput>
+</screen>
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 6876537..406c13b 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -10178,7 +10178,8 @@ XLogRecPtr
 do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
 				   StringInfo labelfile, List **tablespaces,
 				   StringInfo tblspcmapfile, bool infotbssize,
-				   bool needtblspcmapfile)
+				   bool needtblspcmapfile,
+				   XLogRecPtr incremental_reference_lsn)
 {
 	bool		exclusive = (labelfile == NULL);
 	bool		backup_started_in_recovery = false;
@@ -10391,6 +10392,18 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
 		XLogFileName(xlogfilename, starttli, _logSegNo, wal_segment_size);
 
 		/*
+		 * If we are doing an incremental backup, then passed in reference LSN
+		 * must be from past i.e. it should be less than the startpoint.
+		 */
+		if (!XLogRecPtrIsInvalid(incremental_reference_lsn) &&
+			incremental_reference_lsn >= startpoint)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("incremental backup reference lsn %X/%X is in the future",
+							(uint32) (incremental_reference_lsn >> 32),
+							(uint32) incremental_reference_lsn)));
+
+		/*
 		 * Construct tablespace_map file
 		 */
 		if (exclusive)
@@ -10506,6 +10519,10 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
 		appendStringInfo(labelfile, "START TIME: %s\n", strfbuf);
 		appendStringInfo(labelfile, "LABEL: %s\n", backupidstr);
 		appendStringInfo(labelfile, "START TIMELINE: %u\n", starttli);
+		if (!XLogRecPtrIsInvalid(incremental_reference_lsn))
+			appendStringInfo(labelfile, "INCREMENTAL BACKUP REFERENCE WAL LOCATION: %X/%X\n",
+							 (uint32) (incremental_reference_lsn >> 32),
+							 (uint32) incremental_reference_lsn);
 
 		/*
 		 * Okay, write the file, or return its contents to caller.
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 8a70503..61598fb 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -89,7 +89,8 @@ pg_start_backup(PG_FUNCTION_ARGS)
 	if (exclusive)
 	{
 		startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL,
-										NULL, NULL, false, true);
+										NULL, NULL, false, true,
+										InvalidXLogRecPtr);
 	}
 	else
 	{
@@ -105,7 +106,8 @@ pg_start_backup(PG_FUNCTION_ARGS)
 		MemoryContextSwitchTo(oldcontext);
 
 		startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file,
-										NULL, tblspc_map_file, false, true);
+										NULL, tblspc_map_file, false, true,
+										InvalidXLogRecPtr);
 
 		before_shmem_exit(nonexclusive_base_backup_cleanup, (Datum) 0);
 	}
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index bf15262..90ab3ea 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -83,6 +83,11 @@ static pgoff_t sendCompleteFile(const char *readfilename,
 								const char *tarfilename, FILE *fp,
 								struct stat *statbuf, int segmentno,
 								bool verify_checksum, int *checksum_failures);
+static pgoff_t sendPartialFile(const char *readfilename,
+							   const char *tarfilename, FILE *fp,
+							   XLogRecPtr incremental_reference_lsn,
+							   struct stat *statbuf, int segmentno,
+							   bool verify_checksum, int *checksum_failures);
 
 /* Was the backup currently in-progress initiated in recovery mode? */
 static bool backup_started_in_recovery = false;
@@ -112,6 +117,11 @@ do { \
 				(errmsg("could not read from file \"%s\"", filename))); \
 } while (0)
 
+/*
+ * When to send the whole file, % blocks modified (90%)
+ */
+#define WHOLE_FILE_THRESHOLD	0.9
+
 /* The actual number of bytes, transfer of which may cause sleep. */
 static uint64 throttling_sample;
 
@@ -127,6 +137,9 @@ static TimestampTz throttled_last;
 /* The starting XLOG position of the base backup. */
 static XLogRecPtr startptr;
 
+/* The reference XLOG position for the incremental backup. */
+static XLogRecPtr incremental_reference_lsn;
+
 /* Total number of checksum failures during base backup. */
 static long long int total_checksum_failures;
 
@@ -267,7 +280,9 @@ perform_base_backup(basebackup_options *opt)
 	startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &starttli,
 								  labelfile, &tablespaces,
 								  tblspc_map_file,
-								  opt->progress, opt->sendtblspcmapfile);
+								  opt->progress, opt->sendtblspcmapfile,
+								  opt->lsn);
+	incremental_reference_lsn = opt->lsn;
 
 	/*
 	 * Once do_pg_start_backup has been called, ensure that any failure causes
@@ -665,6 +680,7 @@ parse_basebackup_options(List *options, basebackup_options *opt)
 	bool		o_lsn = false;
 
 	MemSet(opt, 0, sizeof(*opt));
+	opt->lsn = InvalidXLogRecPtr;
 	foreach(lopt, options)
 	{
 		DefElem    *defel = (DefElem *) lfirst(lopt);
@@ -1407,6 +1423,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 	int			segmentno = 0;
 	char	   *segmentpath;
 	bool		verify_checksum = false;
+	char	   *filename;
 
 	fp = AllocateFile(readfilename, "rb");
 	if (fp == NULL)
@@ -1418,17 +1435,15 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 				 errmsg("could not open file \"%s\": %m", readfilename)));
 	}
 
+	/*
+	 * Get the filename (excluding path).  As last_dir_separator() includes
+	 * the last directory separator, we chop that off by incrementing the
+	 * pointer.
+	 */
+	filename = last_dir_separator(readfilename) + 1;
+
 	if (!noverify_checksums && DataChecksumsEnabled())
 	{
-		char	   *filename;
-
-		/*
-		 * Get the filename (excluding path).  As last_dir_separator()
-		 * includes the last directory separator, we chop that off by
-		 * incrementing the pointer.
-		 */
-		filename = last_dir_separator(readfilename) + 1;
-
 		if (is_checksummed_file(readfilename, filename))
 		{
 			verify_checksum = true;
@@ -1449,9 +1464,25 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
 		}
 	}
 
-	/* Send complete file to the client. */
-	len = sendCompleteFile(readfilename, tarfilename, fp, statbuf, segmentno,
-						   verify_checksum, &checksum_failures);
+	/*
+	 * If incremental backup, see whether the filename is a non-temporary
+	 * relation filename or not and can be sent partially.  All other files are
+	 * sent completely.
+	 */
+	if (!XLogRecPtrIsInvalid(incremental_reference_lsn) &&
+		OidIsValid(dboid) && looks_like_non_temp_rel_name(filename))
+	{
+		/* Send partial file to the client. */
+		len = sendPartialFile(readfilename, tarfilename, fp,
+							  incremental_reference_lsn, statbuf, segmentno,
+							  verify_checksum, &checksum_failures);
+	}
+	else
+	{
+		/* Send complete file to the client. */
+		len = sendCompleteFile(readfilename, tarfilename, fp, statbuf,
+							   segmentno, verify_checksum, &checksum_failures);
+	}
 
 	/* If the file was truncated while we were sending it, pad it with zeros */
 	if (len < statbuf->st_size)
@@ -1813,3 +1844,257 @@ sendCompleteFile(const char *readfilename, const char *tarfilename, FILE *fp,
 
 	return len;
 }
+
+/*
+ * sendPartialFile
+ *
+ * Sends a partial file containing only the blocks which are modified after
+ * given LSN.  However, if the file is heavily modified, then we send complete
+ * file instead.
+ */
+static pgoff_t
+sendPartialFile(const char *readfilename, const char *tarfilename, FILE *fp,
+				XLogRecPtr incremental_reference_lsn, struct stat *statbuf,
+				int segmentno, bool verify_checksum, int *checksum_failures)
+{
+	char	   *buf;
+	off_t		cnt;
+	pgoff_t		len = 0;
+	bool		sendwholefile = false;
+
+	/*
+	 * Relation file is segmented at size RELSEG_SIZE * BLCKSZ, so we will
+	 * never have size more than that.
+	 */
+	Assert(statbuf->st_size <= (RELSEG_SIZE * BLCKSZ));
+
+	/*
+	 * We want to read the whole file in memory to see how many blocks were
+	 * actually changed.  We don't want to do that incrementally as it will
+	 * need to reread the file while sending the blocks.  palloc() reads
+	 * maximum 1GB - 1, and current max relation segment will be of 1GB, thus
+	 * we use malloc() here.
+	 */
+	buf = (char *) malloc(statbuf->st_size);
+	if (buf == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
+
+	if ((cnt = fread(buf, 1, statbuf->st_size, fp)) > 0)
+	{
+		Bitmapset  *mod_blocks = NULL;
+		int			nmodblocks = 0;
+		int			part_size = 0;
+		int			part_header_size;
+		int			blknum;
+		int			blknocnt;
+		partial_file_header *pfh;
+		char	   *partialtarfilename = NULL;
+
+		/*
+		 * A valid relation size is multiple of BLCKSZ.  However, if we read
+		 * some arbitrary size data, then instead of throwing an error, we
+		 * chose to send that file as-is.  Inform the same to the client by
+		 * emitting a warning.  Also, we cannot verify the checksum, if
+		 * enabled, emit a warning for that too.
+		 */
+		if (cnt % BLCKSZ != 0)
+		{
+			if (verify_checksum)
+			{
+				ereport(WARNING,
+						(errmsg("cannot verify checksum in file \"%s\"",
+								readfilename)));
+				verify_checksum = false;
+			}
+
+			ereport(WARNING,
+					(errmsg("file size (%d) not in multiple of page size (%d), sending whole file",
+							(int) cnt, BLCKSZ)));
+
+			/* File size is not in multiple of BLCKSZ, send as is. */
+			sendwholefile = true;
+		}
+
+		/*
+		 * Check each page LSN and see if it is modified after the given LSN or
+		 * not.  Create a bitmap of all such modified blocks and then decide
+		 * whether we want to send a whole file or a partial file.  Skip this
+		 * check if we decided to send whole file already.
+		 */
+		if (!sendwholefile)
+		{
+			XLogRecPtr	pglsn;
+			int 		i;
+			int			nblocks = (cnt / BLCKSZ);
+
+			for (i = 0; i < nblocks; i++)
+			{
+				int			page_index_in_buf = (BLCKSZ * i);
+				char	   *page = buf + page_index_in_buf;
+
+				pglsn = PageGetLSN(page);
+
+				if (pglsn >= incremental_reference_lsn)
+				{
+					/*
+					 * Verify checksum, if requested, for the modified blocks.
+					 */
+					if (verify_checksum)
+					{
+						verify_page_checksum(readfilename, fp, page,
+											 (cnt - page_index_in_buf), i,
+											 segmentno, checksum_failures);
+
+						/*
+						 * If we hit end-of-file, a concurrent truncation must
+						 * have occurred, so break out of this loop just as if
+						 * the initial fread() returned 0. We'll drop through
+						 * to the same code that handles that case. (We must
+						 * fix up cnt first, though.)
+						 */
+						if (feof(fp))
+						{
+							cnt = page_index_in_buf;
+							break;
+						}
+					}
+
+					mod_blocks = bms_add_member(mod_blocks, i);
+				}
+			}
+
+			nmodblocks = bms_num_members(mod_blocks);
+
+			/*
+			 * We need to send whole file if the modified block count is equal
+			 * to or greater than the WHOLE_FILE_THRESHOLD.  Check that.
+			 */
+			if (i > 0 && (nmodblocks / (double) i) >= WHOLE_FILE_THRESHOLD)
+				sendwholefile = true;
+		}
+
+		/*
+		 * If sendwholefile is true then we need to send the whole file as is.
+		 * Otherwise send a partial file.  Instead of sending entire file at a
+		 * time, we send data in a series of chunks of size CHUNK_SIZE.
+		 * CHUNK_SIZE is arbitrary chosen to 1MB assuming BLCKSZ is of 8K.
+		 */
+		if (sendwholefile)
+		{
+#define CHUNK_SIZE	(BLCKSZ * 128)
+			int			i;
+			int			nchunks = cnt / CHUNK_SIZE;
+			off_t		sent = 0;
+
+			_tarWriteHeader(tarfilename, NULL, statbuf, false);
+
+			/* Send data in chunks of size CHUNK_SIZE each. */
+			for (i = 0; i < nchunks; i++)
+			{
+				/* Send the chunk as a CopyData message */
+				if (pq_putmessage('d', buf + sent, CHUNK_SIZE))
+					ereport(ERROR,
+							(errmsg("base backup could not send data, aborting backup")));
+
+				throttle(CHUNK_SIZE);
+				sent += CHUNK_SIZE;
+			}
+
+			/* Send remaining data, if present. */
+			if (sent < cnt)
+			{
+				off_t		remaining = cnt - sent;
+
+				/* Send the chunk as a CopyData message */
+				if (pq_putmessage('d', buf + sent, remaining))
+					ereport(ERROR,
+							(errmsg("base backup could not send data, aborting backup")));
+
+				throttle(remaining);
+				sent += remaining;
+			}
+
+			free(buf);
+			Assert(sent = cnt);
+
+			return cnt;
+		}
+
+		/* Create a partial file */
+
+		/* Calculate partial file size. */
+		part_header_size = offsetof(partial_file_header, blocknumbers) +
+			(sizeof(uint32) * nmodblocks);
+		part_size = part_header_size + (BLCKSZ * nmodblocks);
+
+		/* Add .partial to filename */
+		partialtarfilename = (char *) palloc(strlen(tarfilename) + 9);
+		snprintf(partialtarfilename, strlen(tarfilename) + 9, "%s.partial", tarfilename);
+
+		statbuf->st_size = part_size;
+		_tarWriteHeader(partialtarfilename, NULL, statbuf, false);
+		pfree(partialtarfilename);
+
+		pfh = (partial_file_header *) palloc(part_header_size);
+		pfh->magic = INCREMENTAL_BACKUP_MAGIC;
+		pfh->nblocks = nmodblocks;
+
+		blknum = -1;
+		blknocnt = 0;
+		while ((blknum = bms_next_member(mod_blocks, blknum)) >= 0)
+		{
+			pfh->blocknumbers[blknocnt] = blknum;
+			/* Calculate CRC for each block to be transferred. */
+			blknocnt++;
+		}
+
+		Assert(blknocnt == nmodblocks);
+
+		/* Now calculate CRC for the header */
+		INIT_CRC32C(pfh->checksum);
+		COMP_CRC32C(pfh->checksum, pfh, part_header_size);
+
+		/* Send header */
+		if (pq_putmessage('d', (char *) pfh, part_header_size))
+			ereport(ERROR,
+					(errmsg("base backup could not send data, aborting backup")));
+		throttle(part_header_size);
+
+		/* Send data blocks */
+		for (blknocnt = 0; blknocnt < nmodblocks; blknocnt++)
+		{
+			int			offset = BLCKSZ * pfh->blocknumbers[blknocnt];
+
+			if (pq_putmessage('d', buf + offset, BLCKSZ))
+				ereport(ERROR,
+						(errmsg("base backup could not send data, aborting backup")));
+			throttle(BLCKSZ);
+		}
+
+		Assert(blknocnt == nmodblocks && statbuf->st_size == part_size);
+
+		len = part_size;
+		pfree(pfh);
+	}
+	else
+	{
+		/* Check for fread() error. */
+		if (ferror(fp))
+		{
+			free(buf);
+			ereport(ERROR,
+					(errmsg("could not read from file \"%s\"", readfilename)));
+		}
+
+		/* Send empty file as is */
+		_tarWriteHeader(tarfilename, NULL, statbuf, false);
+		len = cnt;
+	}
+
+	/* free buffer allocated */
+	free(buf);
+
+	return len;
+}
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 2de2105..3e07d52 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3111,6 +3111,35 @@ looks_like_temp_rel_name(const char *name)
 	return true;
 }
 
+/* <digits>, or <digits>.<digits> */
+bool
+looks_like_non_temp_rel_name(const char *name)
+{
+	int			pos;
+
+	/* Look for a non-empty string of digits (that isn't too long). */
+	for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
+		;
+	if (pos == 0 || pos > OIDCHARS)
+		return false;
+
+	if (name[pos] == '.')
+	{
+		int			segchar;
+
+		for (segchar = 1; isdigit((unsigned char) name[pos + segchar]); ++segchar)
+			;
+		if (segchar <= 1)
+			return false;
+		pos += segchar;
+	}
+
+	/* Now we should be at the end. */
+	if (name[pos] != '\0')
+		return false;
+	return true;
+}
+
 
 /*
  * Issue fsync recursively on PGDATA and all its contents.
diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
old mode 100644
new mode 100755
index fd8e187..a425f14
--- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl
+++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
@@ -561,7 +561,7 @@ $node->command_fails(
 	[ 'pg_basebackup', '-D', "$tempdir/lsn_test", '--lsn', "0/INVALID" ],
 	'pg_basebackup with invalid LSN fails');
 $node->command_ok(
-	[ 'pg_basebackup', '-D', "$tempdir/lsn_test", '--lsn', "0/ABCDEF01", '--no-verify-checksums' ],
+	[ 'pg_basebackup', '-D', "$tempdir/lsn_test", '--lsn', "0/00000001", '--no-verify-checksums' ],
 	'pg_basebackup with valid LSN');
 rmtree("$tempdir/lsn_test");
 
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d519252..155385d 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -347,7 +347,8 @@ typedef enum SessionBackupState
 extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast,
 									 TimeLineID *starttli_p, StringInfo labelfile,
 									 List **tablespaces, StringInfo tblspcmapfile, bool infotbssize,
-									 bool needtblspcmapfile);
+									 bool needtblspcmapfile,
+									 XLogRecPtr ref_lsn);
 extern XLogRecPtr do_pg_stop_backup(char *labelfile, bool waitforarchive,
 									TimeLineID *stoptli_p);
 extern void do_pg_abort_backup(void);
diff --git a/src/include/replication/basebackup.h b/src/include/replication/basebackup.h
index 503a5b9..1b35b08 100644
--- a/src/include/replication/basebackup.h
+++ b/src/include/replication/basebackup.h
@@ -20,6 +20,9 @@
 #define MAX_RATE_LOWER	32
 #define MAX_RATE_UPPER	1048576
 
+/* magic number in incremental backup's .partial file */
+#define INCREMENTAL_BACKUP_MAGIC	0x494E4352
+
 
 typedef struct
 {
@@ -29,6 +32,16 @@ typedef struct
 	int64		size;
 } tablespaceinfo;
 
+/* Definition of the partial file header */
+typedef struct
+{
+	uint32		magic;
+	pg_crc32c	checksum;
+	uint32		nblocks;
+	uint32		blocknumbers[FLEXIBLE_ARRAY_MEMBER];
+} partial_file_header;
+
+
 extern void SendBaseBackup(BaseBackupCmd *cmd);
 
 extern int64 sendTablespace(char *path, bool sizeonly);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index d2a8c52..070faf1 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -136,6 +136,7 @@ extern void AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
 							  SubTransactionId parentSubid);
 extern void RemovePgTempFiles(void);
 extern bool looks_like_temp_rel_name(const char *name);
+extern bool looks_like_non_temp_rel_name(const char *name);
 
 extern int	pg_fsync(int fd);
 extern int	pg_fsync_no_writethrough(int fd);
-- 
1.8.3.1