maxstandbydelay-1.patch

text/x-patch

Filename: maxstandbydelay-1.patch
Type: text/x-patch
Part: 0
Message: Re: Keepalive for max_standby_delay

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: context
File+
src/backend/access/transam/xlog.c 82 11
src/backend/replication/walreceiver.c 1 0
src/backend/replication/walreceiverfuncs.c 10 2
src/backend/storage/ipc/standby.c 64 45
src/backend/utils/misc/guc.c 21 11
src/include/access/xlog.h 1 2
src/include/replication/walreceiver.h 21 11
src/include/storage/standby.h 3 0
Index: src/backend/access/transam/xlog.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v
retrieving revision 1.427
diff -c -r1.427 xlog.c
*** src/backend/access/transam/xlog.c	28 Jun 2010 19:46:19 -0000	1.427
--- src/backend/access/transam/xlog.c	2 Jul 2010 20:01:24 -0000
***************
*** 72,78 ****
  bool		XLogArchiveMode = false;
  char	   *XLogArchiveCommand = NULL;
  bool		EnableHotStandby = false;
- int			MaxStandbyDelay = 30 * 1000;
  bool		fullPageWrites = true;
  bool		log_checkpoints = false;
  int			sync_method = DEFAULT_SYNC_METHOD;
--- 72,77 ----
***************
*** 487,493 ****
   * Keeps track of which sources we've tried to read the current WAL
   * record from and failed.
   */
! static int failedSources = 0;
  
  /* Buffer for currently read page (XLOG_BLCKSZ bytes) */
  static char *readBuf = NULL;
--- 487,502 ----
   * Keeps track of which sources we've tried to read the current WAL
   * record from and failed.
   */
! static int failedSources = 0;	/* OR of XLOG_FROM_* codes */
! 
! /*
!  * These variables track when we last obtained some WAL data to process,
!  * and where we got it from.  (XLogReceiptSource is initially the same as
!  * readSource, but readSource gets reset to zero when we don't have data
!  * to process right now.)
!  */
! static TimestampTz XLogReceiptTime = 0;
! static int XLogReceiptSource = 0;	/* XLOG_FROM_* code */
  
  /* Buffer for currently read page (XLOG_BLCKSZ bytes) */
  static char *readBuf = NULL;
***************
*** 2626,2632 ****
   * Open a logfile segment for reading (during recovery).
   *
   * If source = XLOG_FROM_ARCHIVE, the segment is retrieved from archive.
!  * If source = XLOG_FROM_PG_XLOG, it's read from pg_xlog.
   */
  static int
  XLogFileRead(uint32 log, uint32 seg, int emode, TimeLineID tli,
--- 2635,2641 ----
   * Open a logfile segment for reading (during recovery).
   *
   * If source = XLOG_FROM_ARCHIVE, the segment is retrieved from archive.
!  * Otherwise, it's assumed to be already available in pg_xlog.
   */
  static int
  XLogFileRead(uint32 log, uint32 seg, int emode, TimeLineID tli,
***************
*** 2655,2660 ****
--- 2664,2670 ----
  			break;
  
  		case XLOG_FROM_PG_XLOG:
+ 		case XLOG_FROM_STREAM:
  			XLogFilePath(path, tli, log, seg);
  			restoredFromArchive = false;
  			break;
***************
*** 2674,2680 ****
--- 2684,2696 ----
  				 xlogfname);
  		set_ps_display(activitymsg, false);
  
+ 		/* Track source of data in assorted state variables */
  		readSource = source;
+ 		XLogReceiptSource = source;
+ 		/* In FROM_STREAM case, caller tracks receipt time, not me */
+ 		if (source != XLOG_FROM_STREAM)
+ 			XLogReceiptTime = GetCurrentTimestamp();
+ 
  		return fd;
  	}
  	if (errno != ENOENT || !notfoundOk) /* unexpected failure? */
***************
*** 5568,5574 ****
  /*
   * Returns timestamp of last recovered commit/abort record.
   */
! TimestampTz
  GetLatestXLogTime(void)
  {
  	/* use volatile pointer to prevent code rearrangement */
--- 5584,5590 ----
  /*
   * Returns timestamp of last recovered commit/abort record.
   */
! static TimestampTz
  GetLatestXLogTime(void)
  {
  	/* use volatile pointer to prevent code rearrangement */
***************
*** 5582,5587 ****
--- 5598,5620 ----
  }
  
  /*
+  * Returns time of receipt of current chunk of XLOG data, as well as
+  * whether it was received from streaming replication or from archives.
+  */
+ void
+ GetXLogReceiptTime(TimestampTz *rtime, bool *fromStream)
+ {
+ 	/*
+ 	 * This must be executed in the startup process, since we don't export
+ 	 * the relevant state to shared memory.
+ 	 */
+ 	Assert(InRecovery);
+ 
+ 	*rtime = XLogReceiptTime;
+ 	*fromStream = (XLogReceiptSource == XLOG_FROM_STREAM);
+ }
+ 
+ /*
   * Note that text field supplied is a parameter name and does not require
   * translation
   */
***************
*** 6060,6065 ****
--- 6093,6101 ----
  		xlogctl->recoveryLastRecPtr = ReadRecPtr;
  		SpinLockRelease(&xlogctl->info_lck);
  
+ 		/* Also ensure XLogReceiptTime has a sane value */
+ 		XLogReceiptTime = GetCurrentTimestamp();
+ 
  		/*
  		 * Let postmaster know we've started redo now, so that it can
  		 * launch bgwriter to perform restartpoints.  We don't bother
***************
*** 7647,7653 ****
  		XLogRecPtr	endptr;
  
  		/* Get the current (or recent) end of xlog */
! 		endptr = GetWalRcvWriteRecPtr();
  
  		PrevLogSeg(_logId, _logSeg);
  		RemoveOldXlogFiles(_logId, _logSeg, endptr);
--- 7683,7689 ----
  		XLogRecPtr	endptr;
  
  		/* Get the current (or recent) end of xlog */
! 		endptr = GetWalRcvWriteRecPtr(NULL);
  
  		PrevLogSeg(_logId, _logSeg);
  		RemoveOldXlogFiles(_logId, _logSeg, endptr);
***************
*** 8757,8763 ****
  	XLogRecPtr	recptr;
  	char		location[MAXFNAMELEN];
  
! 	recptr = GetWalRcvWriteRecPtr();
  
  	if (recptr.xlogid == 0 && recptr.xrecoff == 0)
  		PG_RETURN_NULL();
--- 8793,8799 ----
  	XLogRecPtr	recptr;
  	char		location[MAXFNAMELEN];
  
! 	recptr = GetWalRcvWriteRecPtr(NULL);
  
  	if (recptr.xlogid == 0 && recptr.xrecoff == 0)
  		PG_RETURN_NULL();
***************
*** 9272,9277 ****
--- 9308,9315 ----
  			{
  				if (WalRcvInProgress())
  				{
+ 					bool	havedata;
+ 
  					/*
  					 * If we find an invalid record in the WAL streamed from
  					 * master, something is seriously wrong. There's little
***************
*** 9289,9316 ****
  					}
  
  					/*
! 					 * While walreceiver is active, wait for new WAL to arrive
! 					 * from primary.
  					 */
- 					receivedUpto = GetWalRcvWriteRecPtr();
  					if (XLByteLT(*RecPtr, receivedUpto))
  					{
  						/*
  						 * Great, streamed far enough. Open the file if it's
! 						 * not open already.
  						 */
  						if (readFile < 0)
  						{
  							readFile =
  								XLogFileRead(readId, readSeg, PANIC,
  											 recoveryTargetTLI,
! 											 XLOG_FROM_PG_XLOG, false);
  							switched_segment = true;
  							readSource = XLOG_FROM_STREAM;
  						}
  						break;
  					}
  
  					if (CheckForStandbyTrigger())
  						goto triggered;
  
--- 9327,9388 ----
  					}
  
  					/*
! 					 * Walreceiver is active, so see if new data has arrived.
! 					 *
! 					 * We only advance XLogReceiptTime when we obtain fresh
! 					 * WAL from walreceiver and observe that we had already
! 					 * processed everything before the most recent "chunk"
! 					 * that it flushed to disk.  In steady state where we are
! 					 * keeping up with the incoming data, XLogReceiptTime
! 					 * will be updated on each cycle.  When we are behind,
! 					 * XLogReceiptTime will not advance, so the grace time
! 					 * alloted to conflicting queries will decrease.
  					 */
  					if (XLByteLT(*RecPtr, receivedUpto))
+ 						havedata = true;
+ 					else
+ 					{
+ 						XLogRecPtr	latestChunkStart;
+ 
+ 						receivedUpto = GetWalRcvWriteRecPtr(&latestChunkStart);
+ 						if (XLByteLT(*RecPtr, receivedUpto))
+ 						{
+ 							havedata = true;
+ 							if (!XLByteLT(*RecPtr, latestChunkStart))
+ 								XLogReceiptTime = GetCurrentTimestamp();
+ 						}
+ 						else
+ 							havedata = false;
+ 					}
+ 					if (havedata)
  					{
  						/*
  						 * Great, streamed far enough. Open the file if it's
! 						 * not open already.  Use XLOG_FROM_STREAM so that
! 						 * source info is set correctly and XLogReceiptTime
! 						 * isn't changed.
  						 */
  						if (readFile < 0)
  						{
  							readFile =
  								XLogFileRead(readId, readSeg, PANIC,
  											 recoveryTargetTLI,
! 											 XLOG_FROM_STREAM, false);
! 							Assert(readFile >= 0);
  							switched_segment = true;
+ 						}
+ 						else
+ 						{
+ 							/* just make sure source info is correct... */
  							readSource = XLOG_FROM_STREAM;
+ 							XLogReceiptSource = XLOG_FROM_STREAM;
  						}
  						break;
  					}
  
+ 					/*
+ 					 * Data not here yet, so check for trigger then sleep.
+ 					 */
  					if (CheckForStandbyTrigger())
  						goto triggered;
  
Index: src/backend/replication/walreceiver.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/replication/walreceiver.c,v
retrieving revision 1.14
diff -c -r1.14 walreceiver.c
*** src/backend/replication/walreceiver.c	9 Jun 2010 15:04:07 -0000	1.14
--- src/backend/replication/walreceiver.c	2 Jul 2010 20:01:24 -0000
***************
*** 524,529 ****
--- 524,530 ----
  
  		/* Update shared-memory status */
  		SpinLockAcquire(&walrcv->mutex);
+ 		walrcv->latestChunkStart = walrcv->receivedUpto;
  		walrcv->receivedUpto = LogstreamResult.Flush;
  		SpinLockRelease(&walrcv->mutex);
  
Index: src/backend/replication/walreceiverfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/replication/walreceiverfuncs.c,v
retrieving revision 1.5
diff -c -r1.5 walreceiverfuncs.c
*** src/backend/replication/walreceiverfuncs.c	28 Apr 2010 16:54:15 -0000	1.5
--- src/backend/replication/walreceiverfuncs.c	2 Jul 2010 20:01:24 -0000
***************
*** 199,214 ****
  	walrcv->startTime = now;
  
  	walrcv->receivedUpto = recptr;
  	SpinLockRelease(&walrcv->mutex);
  
  	SendPostmasterSignal(PMSIGNAL_START_WALRECEIVER);
  }
  
  /*
!  * Returns the byte position that walreceiver has written
   */
  XLogRecPtr
! GetWalRcvWriteRecPtr(void)
  {
  	/* use volatile pointer to prevent code rearrangement */
  	volatile WalRcvData *walrcv = WalRcv;
--- 200,221 ----
  	walrcv->startTime = now;
  
  	walrcv->receivedUpto = recptr;
+ 	walrcv->latestChunkStart = recptr;
+ 
  	SpinLockRelease(&walrcv->mutex);
  
  	SendPostmasterSignal(PMSIGNAL_START_WALRECEIVER);
  }
  
  /*
!  * Returns the last+1 byte position that walreceiver has written.
!  *
!  * Optionally, returns the previous chunk start, that is the first byte
!  * written in the most recent walreceiver flush cycle.  Callers not
!  * interested in that value may pass NULL for latestChunkStart.
   */
  XLogRecPtr
! GetWalRcvWriteRecPtr(XLogRecPtr *latestChunkStart)
  {
  	/* use volatile pointer to prevent code rearrangement */
  	volatile WalRcvData *walrcv = WalRcv;
***************
*** 216,221 ****
--- 223,230 ----
  
  	SpinLockAcquire(&walrcv->mutex);
  	recptr = walrcv->receivedUpto;
+ 	if (latestChunkStart)
+ 		*latestChunkStart = walrcv->latestChunkStart;
  	SpinLockRelease(&walrcv->mutex);
  
  	return recptr;
Index: src/backend/storage/ipc/standby.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/storage/ipc/standby.c,v
retrieving revision 1.25
diff -c -r1.25 standby.c
*** src/backend/storage/ipc/standby.c	14 Jun 2010 00:49:24 -0000	1.25
--- src/backend/storage/ipc/standby.c	2 Jul 2010 20:01:24 -0000
***************
*** 30,36 ****
--- 30,39 ----
  #include "storage/standby.h"
  #include "utils/ps_status.h"
  
+ /* User-settable GUC parameters */
  int			vacuum_defer_cleanup_age;
+ int			max_standby_archive_delay = 30 * 1000;
+ int			max_standby_streaming_delay = 30 * 1000;
  
  static List *RecoveryLockList;
  
***************
*** 113,118 ****
--- 117,152 ----
   * -----------------------------------------------------
   */
  
+ /*
+  * Determine the cutoff time at which we want to start canceling conflicting
+  * transactions.  Returns zero (a time safely in the past) if we are willing
+  * to wait forever.
+  */
+ static TimestampTz
+ GetStandbyLimitTime(void)
+ {
+ 	TimestampTz	rtime;
+ 	bool		fromStream;
+ 
+ 	/*
+ 	 * The cutoff time is the last WAL data receipt time plus the appropriate
+ 	 * delay variable.  Delay of -1 means wait forever.
+ 	 */
+ 	GetXLogReceiptTime(&rtime, &fromStream);
+ 	if (fromStream)
+ 	{
+ 		if (max_standby_streaming_delay < 0)
+ 			return 0;			/* wait forever */
+ 		return TimestampTzPlusMilliseconds(rtime, max_standby_streaming_delay);
+ 	}
+ 	else
+ 	{
+ 		if (max_standby_archive_delay < 0)
+ 			return 0;			/* wait forever */
+ 		return TimestampTzPlusMilliseconds(rtime, max_standby_archive_delay);
+ 	}
+ }
+ 
  #define STANDBY_INITIAL_WAIT_US  1000
  static int	standbyWait_us = STANDBY_INITIAL_WAIT_US;
  
***************
*** 124,133 ****
  static bool
  WaitExceedsMaxStandbyDelay(void)
  {
! 	/* Are we past max_standby_delay? */
! 	if (MaxStandbyDelay >= 0 &&
! 		TimestampDifferenceExceeds(GetLatestXLogTime(), GetCurrentTimestamp(),
! 								   MaxStandbyDelay))
  		return true;
  
  	/*
--- 158,168 ----
  static bool
  WaitExceedsMaxStandbyDelay(void)
  {
! 	TimestampTz	ltime;
! 
! 	/* Are we past the limit time? */
! 	ltime = GetStandbyLimitTime();
! 	if (ltime && GetCurrentTimestamp() >= ltime)
  		return true;
  
  	/*
***************
*** 368,433 ****
   * Startup is sleeping and the query waits on a lock. We protect against
   * only the former sequence here, the latter sequence is checked prior to
   * the query sleeping, in CheckRecoveryConflictDeadlock().
   */
  void
  ResolveRecoveryConflictWithBufferPin(void)
  {
  	bool		sig_alarm_enabled = false;
  
  	Assert(InHotStandby);
  
! 	if (MaxStandbyDelay == 0)
! 	{
! 		/*
! 		 * We don't want to wait, so just tell everybody holding the pin to
! 		 * get out of town.
! 		 */
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
! 	}
! 	else if (MaxStandbyDelay < 0)
! 	{
! 		TimestampTz now = GetCurrentTimestamp();
  
  		/*
! 		 * Set timeout for deadlock check (only)
  		 */
  		if (enable_standby_sig_alarm(now, now, true))
  			sig_alarm_enabled = true;
  		else
  			elog(FATAL, "could not set timer for process wakeup");
  	}
  	else
  	{
! 		TimestampTz then = GetLatestXLogTime();
! 		TimestampTz now = GetCurrentTimestamp();
! 
! 		/* Are we past max_standby_delay? */
! 		if (TimestampDifferenceExceeds(then, now, MaxStandbyDelay))
! 		{
! 			/*
! 			 * We're already behind, so clear a path as quickly as possible.
! 			 */
! 			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
! 		}
  		else
! 		{
! 			TimestampTz max_standby_time;
! 
! 			/*
! 			 * At what point in the future do we hit MaxStandbyDelay?
! 			 */
! 			max_standby_time = TimestampTzPlusMilliseconds(then, MaxStandbyDelay);
! 			Assert(max_standby_time > now);
! 
! 			/*
! 			 * Wake up at MaxStandby delay, and check for deadlocks as well
! 			 * if we will be waiting longer than deadlock_timeout
! 			 */
! 			if (enable_standby_sig_alarm(now, max_standby_time, false))
! 				sig_alarm_enabled = true;
! 			else
! 				elog(FATAL, "could not set timer for process wakeup");
! 		}
  	}
  
  	/* Wait to be signaled by UnpinBuffer() */
--- 402,452 ----
   * Startup is sleeping and the query waits on a lock. We protect against
   * only the former sequence here, the latter sequence is checked prior to
   * the query sleeping, in CheckRecoveryConflictDeadlock().
+  *
+  * Deadlocks are extremely rare, and relatively expensive to check for,
+  * so we don't do a deadlock check right away ... only if we have had to wait
+  * at least deadlock_timeout.  Most of the logic about that is in proc.c.
   */
  void
  ResolveRecoveryConflictWithBufferPin(void)
  {
  	bool		sig_alarm_enabled = false;
+ 	TimestampTz	ltime;
+ 	TimestampTz	now;
  
  	Assert(InHotStandby);
  
! 	ltime = GetStandbyLimitTime();
! 	now = GetCurrentTimestamp();
  
+ 	if (!ltime)
+ 	{
  		/*
! 		 * We're willing to wait forever for conflicts, so set timeout for
! 		 * deadlock check (only)
  		 */
  		if (enable_standby_sig_alarm(now, now, true))
  			sig_alarm_enabled = true;
  		else
  			elog(FATAL, "could not set timer for process wakeup");
  	}
+ 	else if (now >= ltime)
+ 	{
+ 		/*
+ 		 * We're already behind, so clear a path as quickly as possible.
+ 		 */
+ 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+ 	}
  	else
  	{
! 		/*
! 		 * Wake up at ltime, and check for deadlocks as well if we will be
! 		 * waiting longer than deadlock_timeout
! 		 */
! 		if (enable_standby_sig_alarm(now, ltime, false))
! 			sig_alarm_enabled = true;
  		else
! 			elog(FATAL, "could not set timer for process wakeup");
  	}
  
  	/* Wait to be signaled by UnpinBuffer() */
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.557
diff -c -r1.557 guc.c
*** src/backend/utils/misc/guc.c	25 Jun 2010 13:11:25 -0000	1.557
--- src/backend/utils/misc/guc.c	2 Jul 2010 20:01:25 -0000
***************
*** 57,62 ****
--- 57,63 ----
  #include "postmaster/walwriter.h"
  #include "replication/walsender.h"
  #include "storage/bufmgr.h"
+ #include "storage/standby.h"
  #include "storage/fd.h"
  #include "tcop/tcopprot.h"
  #include "tsearch/ts_cache.h"
***************
*** 116,122 ****
  extern char *temp_tablespaces;
  extern bool synchronize_seqscans;
  extern bool fullPageWrites;
- extern int	vacuum_defer_cleanup_age;
  extern int	ssl_renegotiation_limit;
  
  #ifdef TRACE_SORT
--- 117,122 ----
***************
*** 1373,1378 ****
--- 1373,1398 ----
  		1000, 1, INT_MAX / 1000, NULL, NULL
  	},
  
+ 	{
+ 		{"max_standby_archive_delay", PGC_SIGHUP, WAL_STANDBY_SERVERS,
+ 			gettext_noop("Sets the maximum delay before canceling queries when a hot standby server is processing archived WAL data."),
+ 			NULL,
+ 			GUC_UNIT_MS
+ 		},
+ 		&max_standby_archive_delay,
+ 		30 * 1000, -1, INT_MAX / 1000, NULL, NULL
+ 	},
+ 
+ 	{
+ 		{"max_standby_streaming_delay", PGC_SIGHUP, WAL_STANDBY_SERVERS,
+ 			gettext_noop("Sets the maximum delay before canceling queries when a hot standby server is processing streamed WAL data."),
+ 			NULL,
+ 			GUC_UNIT_MS
+ 		},
+ 		&max_standby_streaming_delay,
+ 		30 * 1000, -1, INT_MAX / 1000, NULL, NULL
+ 	},
+ 
  	/*
  	 * Note: MaxBackends is limited to INT_MAX/4 because some places compute
  	 * 4*MaxBackends without any overflow check.  This check is made in
***************
*** 1393,1408 ****
  	},
  
  	{
- 		{"max_standby_delay", PGC_SIGHUP, WAL_STANDBY_SERVERS,
- 			gettext_noop("Sets the maximum delay to avoid conflict processing on hot standby servers."),
- 			NULL,
- 			GUC_UNIT_MS
- 		},
- 		&MaxStandbyDelay,
- 		30 * 1000, -1, INT_MAX / 1000, NULL, NULL
- 	},
- 
- 	{
  		{"superuser_reserved_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
  			gettext_noop("Sets the number of connection slots reserved for superusers."),
  			NULL
--- 1413,1418 ----
Index: src/include/access/xlog.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/access/xlog.h,v
retrieving revision 1.113
diff -c -r1.113 xlog.h
*** src/include/access/xlog.h	17 Jun 2010 16:41:25 -0000	1.113
--- src/include/access/xlog.h	2 Jul 2010 20:01:25 -0000
***************
*** 193,199 ****
  extern bool XLogArchiveMode;
  extern char *XLogArchiveCommand;
  extern bool EnableHotStandby;
- extern int	MaxStandbyDelay;
  extern bool log_checkpoints;
  
  /* WAL levels */
--- 197,202 ----
***************
*** 279,285 ****
  
  extern bool RecoveryInProgress(void);
  extern bool XLogInsertAllowed(void);
! extern TimestampTz GetLatestXLogTime(void);
  
  extern void UpdateControlFile(void);
  extern uint64 GetSystemIdentifier(void);
--- 282,288 ----
  
  extern bool RecoveryInProgress(void);
  extern bool XLogInsertAllowed(void);
! extern void GetXLogReceiptTime(TimestampTz *rtime, bool *fromStream);
  
  extern void UpdateControlFile(void);
  extern uint64 GetSystemIdentifier(void);
Index: src/include/replication/walreceiver.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/replication/walreceiver.h,v
retrieving revision 1.9
diff -c -r1.9 walreceiver.h
*** src/include/replication/walreceiver.h	3 Jun 2010 22:17:32 -0000	1.9
--- src/include/replication/walreceiver.h	2 Jul 2010 20:01:25 -0000
***************
*** 41,65 ****
  typedef struct
  {
  	/*
! 	 * connection string; is used for walreceiver to connect with the primary.
! 	 */
! 	char		conninfo[MAXCONNINFO];
! 
! 	/*
! 	 * PID of currently active walreceiver process, and the current state.
  	 */
  	pid_t		pid;
  	WalRcvState walRcvState;
  	pg_time_t	startTime;
  
  	/*
! 	 * receivedUpto-1 is the last byte position that has been already
! 	 * received. When startup process starts the walreceiver, it sets this to
! 	 * the point where it wants the streaming to begin. After that,
! 	 * walreceiver updates this whenever it flushes the received WAL.
  	 */
  	XLogRecPtr	receivedUpto;
  
  	slock_t		mutex;			/* locks shared variables shown above */
  } WalRcvData;
  
--- 41,75 ----
  typedef struct
  {
  	/*
! 	 * PID of currently active walreceiver process, its current state and
! 	 * start time (actually, the time at which it was requested to be started).
  	 */
  	pid_t		pid;
  	WalRcvState walRcvState;
  	pg_time_t	startTime;
  
  	/*
! 	 * receivedUpto-1 is the last byte position that has already been
! 	 * received.  When startup process starts the walreceiver, it sets
! 	 * receivedUpto to the point where it wants the streaming to begin.
! 	 * After that, walreceiver updates this whenever it flushes the received
! 	 * WAL to disk.
  	 */
  	XLogRecPtr	receivedUpto;
  
+ 	/*
+ 	 * latestChunkStart is the starting byte position of the current "batch"
+ 	 * of received WAL.  It's actually the same as the previous value of
+ 	 * receivedUpto before the last flush to disk.  Startup process can use
+ 	 * this to detect whether it's keeping up or not.
+ 	 */
+ 	XLogRecPtr	latestChunkStart;
+ 
+ 	/*
+ 	 * connection string; is used for walreceiver to connect with the primary.
+ 	 */
+ 	char		conninfo[MAXCONNINFO];
+ 
  	slock_t		mutex;			/* locks shared variables shown above */
  } WalRcvData;
  
***************
*** 83,88 ****
  extern bool WalRcvInProgress(void);
  extern XLogRecPtr WaitNextXLogAvailable(XLogRecPtr recptr, bool *finished);
  extern void RequestXLogStreaming(XLogRecPtr recptr, const char *conninfo);
! extern XLogRecPtr GetWalRcvWriteRecPtr(void);
  
  #endif   /* _WALRECEIVER_H */
--- 93,98 ----
  extern bool WalRcvInProgress(void);
  extern XLogRecPtr WaitNextXLogAvailable(XLogRecPtr recptr, bool *finished);
  extern void RequestXLogStreaming(XLogRecPtr recptr, const char *conninfo);
! extern XLogRecPtr GetWalRcvWriteRecPtr(XLogRecPtr *latestChunkStart);
  
  #endif   /* _WALRECEIVER_H */
Index: src/include/storage/standby.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/storage/standby.h,v
retrieving revision 1.10
diff -c -r1.10 standby.h
*** src/include/storage/standby.h	13 May 2010 11:15:38 -0000	1.10
--- src/include/storage/standby.h	2 Jul 2010 20:01:25 -0000
***************
*** 19,25 ****
--- 19,28 ----
  #include "storage/procsignal.h"
  #include "storage/relfilenode.h"
  
+ /* User-settable GUC parameters */
  extern int	vacuum_defer_cleanup_age;
+ extern int	max_standby_archive_delay;
+ extern int	max_standby_streaming_delay;
  
  extern void InitRecoveryTransactionEnvironment(void);
  extern void ShutdownRecoveryTransactionEnvironment(void);