hs_deadlock_timeout.patch

text/x-patch

Filename: hs_deadlock_timeout.patch
Type: text/x-patch
Part: 0
Message: Hot Standby performance and deadlocking

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/storage/ipc/standby.c 14 0
src/backend/storage/lmgr/proc.c 88 0
src/include/storage/proc.h 2 0
*** a/src/backend/storage/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
***************
*** 388,399 **** ResolveRecoveryConflictWithBufferPin(void)
  	}
  	else if (MaxStandbyDelay < 0)
  	{
  		/*
! 		 * Send out a request to check for buffer pin deadlocks before we
! 		 * wait. This is fairly cheap, so no need to wait for deadlock timeout
! 		 * before trying to send it out.
  		 */
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
  	}
  	else
  	{
--- 388,402 ----
  	}
  	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
  	{
***************
*** 410,443 **** ResolveRecoveryConflictWithBufferPin(void)
  		}
  		else
  		{
! 			TimestampTz fin_time;		/* Expected wake-up time by timer */
! 			long		timer_delay_secs;		/* Amount of time we set timer
! 												 * for */
! 			int			timer_delay_usecs;
! 
! 			/*
! 			 * Send out a request to check for buffer pin deadlocks before we
! 			 * wait. This is fairly cheap, so no need to wait for deadlock
! 			 * timeout before trying to send it out.
! 			 */
! 			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
  
  			/*
! 			 * How much longer we should wait?
  			 */
! 			fin_time = TimestampTzPlusMilliseconds(then, MaxStandbyDelay);
! 
! 			TimestampDifference(now, fin_time,
! 								&timer_delay_secs, &timer_delay_usecs);
  
  			/*
! 			 * It's possible that the difference is less than a microsecond;
! 			 * ensure we don't cancel, rather than set, the interrupt.
  			 */
! 			if (timer_delay_secs == 0 && timer_delay_usecs == 0)
! 				timer_delay_usecs = 1;
! 
! 			if (enable_standby_sig_alarm(timer_delay_secs, timer_delay_usecs, fin_time))
  				sig_alarm_enabled = true;
  			else
  				elog(FATAL, "could not set timer for process wakeup");
--- 413,431 ----
  		}
  		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");
*** a/src/backend/storage/lmgr/proc.c
--- b/src/backend/storage/lmgr/proc.c
***************
*** 85,90 **** static TimestampTz timeout_start_time;
--- 85,91 ----
  
  /* statement_fin_time is valid only if statement_timeout_active is true */
  static TimestampTz statement_fin_time;
+ static TimestampTz statement_fin_time2; /* valid only in recovery */
  
  
  static void RemoveProcFromArray(int code, Datum arg);
***************
*** 1619,1641 **** handle_sig_alarm(SIGNAL_ARGS)
   * To avoid various edge cases, we must be careful to do nothing
   * when there is nothing to be done.  We also need to be able to
   * reschedule the timer interrupt if called before end of statement.
   */
  bool
! enable_standby_sig_alarm(long delay_s, int delay_us, TimestampTz fin_time)
  {
! 	struct itimerval timeval;
! 
! 	Assert(delay_s >= 0 && delay_us >= 0);
  
! 	statement_fin_time = fin_time;
  
! 	standby_timeout_active = true;
  
- 	MemSet(&timeval, 0, sizeof(struct itimerval));
- 	timeval.it_value.tv_sec = delay_s;
- 	timeval.it_value.tv_usec = delay_us;
- 	if (setitimer(ITIMER_REAL, &timeval, NULL))
- 		return false;
  	return true;
  }
  
--- 1620,1680 ----
   * To avoid various edge cases, we must be careful to do nothing
   * when there is nothing to be done.  We also need to be able to
   * reschedule the timer interrupt if called before end of statement.
+  *
+  * We set either deadlock_timeout_active or statement_timeout_active
+  * or both. Interrupts are enabled if standby_timeout_active.
   */
  bool
! enable_standby_sig_alarm(TimestampTz now, TimestampTz fin_time, bool deadlock_only)
  {
! 	TimestampTz deadlock_time = TimestampTzPlusMilliseconds(now, DeadlockTimeout);
  
! 	if (deadlock_only)
! 	{
! 		/*
! 		 * Wake up at DeadlockTimeout only, then wait forever
! 		 */
! 		statement_fin_time = deadlock_time;
! 		deadlock_timeout_active = true;
! 		statement_timeout_active = false;
! 	}
! 	else if (fin_time > deadlock_time)
! 	{
! 		/*
! 		 * Wake up at DeadlockTimeout, then again at MaxStandbyDelay
! 		 */
! 		statement_fin_time = deadlock_time;
! 		statement_fin_time2 = fin_time;
! 		deadlock_timeout_active = true;
! 		statement_timeout_active = true;
! 	}
! 	else
! 	{
! 		/*
! 		 * Wake only at MaxStandbyDelay because its fairly soon
! 		 */
! 		statement_fin_time = fin_time;
! 		deadlock_timeout_active = false;
! 		statement_timeout_active = true;
! 	}
  
! 	if (deadlock_timeout_active || statement_timeout_active)
! 	{
! 		long		secs;
! 		int			usecs;
! 		struct itimerval timeval;
! 		TimestampDifference(now, statement_fin_time,
! 							&secs, &usecs);
! 		if (secs == 0 && usecs == 0)
! 			usecs = 1;
! 		MemSet(&timeval, 0, sizeof(struct itimerval));
! 		timeval.it_value.tv_sec = secs;
! 		timeval.it_value.tv_usec = usecs;
! 		if (setitimer(ITIMER_REAL, &timeval, NULL))
! 			return false;
! 		standby_timeout_active = true;
! 	}
  
  	return true;
  }
  
***************
*** 1675,1711 **** static bool
  CheckStandbyTimeout(void)
  {
  	TimestampTz now;
  
  	standby_timeout_active = false;
  
  	now = GetCurrentTimestamp();
  
  	if (now >= statement_fin_time)
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
  	else
  	{
- 		/* Not time yet, so (re)schedule the interrupt */
  		long		secs;
  		int			usecs;
  		struct itimerval timeval;
- 
  		TimestampDifference(now, statement_fin_time,
  							&secs, &usecs);
- 
- 		/*
- 		 * It's possible that the difference is less than a microsecond;
- 		 * ensure we don't cancel, rather than set, the interrupt.
- 		 */
  		if (secs == 0 && usecs == 0)
  			usecs = 1;
- 
- 		standby_timeout_active = true;
- 
  		MemSet(&timeval, 0, sizeof(struct itimerval));
  		timeval.it_value.tv_sec = secs;
  		timeval.it_value.tv_usec = usecs;
  		if (setitimer(ITIMER_REAL, &timeval, NULL))
  			return false;
  	}
  
  	return true;
--- 1714,1777 ----
  CheckStandbyTimeout(void)
  {
  	TimestampTz now;
+ 	bool reschedule = false;
  
  	standby_timeout_active = false;
  
  	now = GetCurrentTimestamp();
  
+ 	/*
+ 	 * Reschedule the timer if its not time to wake yet, or if we
+ 	 * have both timers set and the first one has just been reached.
+ 	 */
  	if (now >= statement_fin_time)
! 	{
! 		if (deadlock_timeout_active)
! 		{
! 			/*
! 			 * We're still waiting when we reach DeadlockTimeout, so send out a request
! 			 * to have other backends check themselves for deadlock. Then continue
! 			 * waiting until MaxStandbyDelay.
! 			 */
! 			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
! 			deadlock_timeout_active = false;
! 
! 			/*
! 			 * Begin second waiting period to MaxStandbyDelay if required.
! 			 */
! 			if (statement_timeout_active)
! 			{
! 				reschedule = true;
! 				statement_fin_time = statement_fin_time2;
! 			}
! 		}
! 		else
! 		{
! 			/*
! 			 * We've now reached MaxStandbyDelay, so ask all conflicts to leave, cos
! 			 * its time for us to press ahead with applying changes in recovery.
! 			 */
! 			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
! 		}
! 	}
  	else
+ 		reschedule = true;
+ 
+ 	if (reschedule)
  	{
  		long		secs;
  		int			usecs;
  		struct itimerval timeval;
  		TimestampDifference(now, statement_fin_time,
  							&secs, &usecs);
  		if (secs == 0 && usecs == 0)
  			usecs = 1;
  		MemSet(&timeval, 0, sizeof(struct itimerval));
  		timeval.it_value.tv_sec = secs;
  		timeval.it_value.tv_usec = usecs;
  		if (setitimer(ITIMER_REAL, &timeval, NULL))
  			return false;
+ 		standby_timeout_active = true;
  	}
  
  	return true;
*** a/src/include/storage/proc.h
--- b/src/include/storage/proc.h
***************
*** 199,205 **** extern bool enable_sig_alarm(int delayms, bool is_statement_timeout);
  extern bool disable_sig_alarm(bool is_statement_timeout);
  extern void handle_sig_alarm(SIGNAL_ARGS);
  
! extern bool enable_standby_sig_alarm(long delay_s, int delay_us, TimestampTz fin_time);
  extern bool disable_standby_sig_alarm(void);
  extern void handle_standby_sig_alarm(SIGNAL_ARGS);
  
--- 199,206 ----
  extern bool disable_sig_alarm(bool is_statement_timeout);
  extern void handle_sig_alarm(SIGNAL_ARGS);
  
! extern bool enable_standby_sig_alarm(TimestampTz now,
! 									 TimestampTz fin_time, bool deadlock_only);
  extern bool disable_standby_sig_alarm(void);
  extern void handle_standby_sig_alarm(SIGNAL_ARGS);