Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf34c68f

Browse files
committed
Introduce timeout handling framework
Management of timeouts was getting a little cumbersome; what weoriginally had was more than enough back when we were only concernedabout deadlocks and query cancel; however, when we added timeouts forstandby processes, the code got considerably messier. Since there areplans to add more complex timeouts, this seems a good time to introducea central timeout handling module.External modules register their timeout handlers during processinitialization, and later enable and disable them as they see fit usinga simple API; timeout.c is in charge of keeping track of which timeoutsare in effect at any time, installing a common SIGALRM signal handler,and calling setitimer() as appropriate to ensure timely firing ofexternal handlers.timeout.c additionally supports pluggable modules to add their owntimeouts, though this capability isn't exercised anywhere yet.Additionally, as of this commit, walsender processes are aware oftimeouts; we had a preexisting bug there that made those ignore SIGALRM,thus being subject to unhandled deadlocks, particularly during theauthentication phase. This has already been fixed in back branches incommit0bf8eb2, which see for more details.Main author: Zoltán BöszörményiSome review and cleanup by Álvaro HerreraExtensive reworking by Tom Lane
1 parentdd16f94 commitf34c68f

File tree

13 files changed

+688
-498
lines changed

13 files changed

+688
-498
lines changed

‎src/backend/postmaster/autovacuum.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
#include"utils/rel.h"
9898
#include"utils/snapmgr.h"
9999
#include"utils/syscache.h"
100+
#include"utils/timeout.h"
100101
#include"utils/timestamp.h"
101102
#include"utils/tqual.h"
102103

@@ -432,7 +433,7 @@ AutoVacLauncherMain(int argc, char *argv[])
432433
pqsignal(SIGTERM,avl_sigterm_handler);
433434

434435
pqsignal(SIGQUIT,quickdie);
435-
pqsignal(SIGALRM,handle_sig_alarm);
436+
InitializeTimeouts();/* establishes SIGALRM handler */
436437

437438
pqsignal(SIGPIPE,SIG_IGN);
438439
pqsignal(SIGUSR1,procsignal_sigusr1_handler);
@@ -482,9 +483,9 @@ AutoVacLauncherMain(int argc, char *argv[])
482483
/* Prevents interrupts while cleaning up */
483484
HOLD_INTERRUPTS();
484485

485-
/* Forget any pending QueryCancel request */
486+
/* Forget any pending QueryCancelor timeoutrequest */
486487
QueryCancelPending= false;
487-
disable_sig_alarm(true);
488+
disable_all_timeouts(false);
488489
QueryCancelPending= false;/* again in case timeout occurred */
489490

490491
/* Report the error to the server log */
@@ -1492,7 +1493,7 @@ AutoVacWorkerMain(int argc, char *argv[])
14921493
pqsignal(SIGINT,StatementCancelHandler);
14931494
pqsignal(SIGTERM,die);
14941495
pqsignal(SIGQUIT,quickdie);
1495-
pqsignal(SIGALRM,handle_sig_alarm);
1496+
InitializeTimeouts();/* establishes SIGALRM handler */
14961497

14971498
pqsignal(SIGPIPE,SIG_IGN);
14981499
pqsignal(SIGUSR1,procsignal_sigusr1_handler);

‎src/backend/postmaster/postmaster.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,12 @@
112112
#include"storage/ipc.h"
113113
#include"storage/pg_shmem.h"
114114
#include"storage/pmsignal.h"
115-
#include"storage/proc.h"
116115
#include"tcop/tcopprot.h"
117116
#include"utils/builtins.h"
118117
#include"utils/datetime.h"
119118
#include"utils/memutils.h"
120119
#include"utils/ps_status.h"
120+
#include"utils/timeout.h"
121121

122122
#ifdefEXEC_BACKEND
123123
#include"storage/spin.h"
@@ -337,6 +337,7 @@ static void reaper(SIGNAL_ARGS);
337337
staticvoidsigusr1_handler(SIGNAL_ARGS);
338338
staticvoidstartup_die(SIGNAL_ARGS);
339339
staticvoiddummy_handler(SIGNAL_ARGS);
340+
staticvoidStartupPacketTimeoutHandler(void);
340341
staticvoidCleanupBackend(intpid,intexitstatus);
341342
staticvoidHandleChildCrash(intpid,intexitstatus,constchar*procname);
342343
staticvoidLogChildExit(intlev,constchar*procname,
@@ -3415,7 +3416,7 @@ BackendInitialize(Port *port)
34153416
*/
34163417
pqsignal(SIGTERM,startup_die);
34173418
pqsignal(SIGQUIT,startup_die);
3418-
pqsignal(SIGALRM,startup_die);
3419+
InitializeTimeouts();/* establishes SIGALRM handler */
34193420
PG_SETMASK(&StartupBlockSig);
34203421

34213422
/*
@@ -3469,9 +3470,18 @@ BackendInitialize(Port *port)
34693470
* time delay, so that a broken client can't hog a connection
34703471
* indefinitely. PreAuthDelay and any DNS interactions above don't count
34713472
* against the time limit.
3473+
*
3474+
* Note: AuthenticationTimeout is applied here while waiting for the
3475+
* startup packet, and then again in InitPostgres for the duration of any
3476+
* authentication operations. So a hostile client could tie up the
3477+
* process for nearly twice AuthenticationTimeout before we kick him off.
3478+
*
3479+
* Note: because PostgresMain will call InitializeTimeouts again, the
3480+
* registration of STARTUP_PACKET_TIMEOUT will be lost. This is okay
3481+
* since we never use it again after this function.
34723482
*/
3473-
if (!enable_sig_alarm(AuthenticationTimeout*1000, false))
3474-
elog(FATAL,"could not set timer for startup packet timeout");
3483+
RegisterTimeout(STARTUP_PACKET_TIMEOUT,StartupPacketTimeoutHandler);
3484+
enable_timeout_after(STARTUP_PACKET_TIMEOUT,AuthenticationTimeout*1000);
34753485

34763486
/*
34773487
* Receive the startup packet (which might turn out to be a cancel request
@@ -3508,8 +3518,7 @@ BackendInitialize(Port *port)
35083518
/*
35093519
* Disable the timeout, and prevent SIGTERM/SIGQUIT again.
35103520
*/
3511-
if (!disable_sig_alarm(false))
3512-
elog(FATAL,"could not disable timer for startup packet timeout");
3521+
disable_timeout(STARTUP_PACKET_TIMEOUT, false);
35133522
PG_SETMASK(&BlockSig);
35143523
}
35153524

@@ -4311,8 +4320,8 @@ sigusr1_handler(SIGNAL_ARGS)
43114320
}
43124321

43134322
/*
4314-
*Timeout orshutdown signal from postmaster while processing startup packet.
4315-
*Cleanup and exit(1).
4323+
*SIGTERM orSIGQUIT while processing startup packet.
4324+
*Clean up and exit(1).
43164325
*
43174326
* XXX: possible future improvement: try to send a message indicating
43184327
* why we are disconnecting. Problem is to be sure we don't block while
@@ -4339,6 +4348,17 @@ dummy_handler(SIGNAL_ARGS)
43394348
{
43404349
}
43414350

4351+
/*
4352+
* Timeout while processing startup packet.
4353+
* As for startup_die(), we clean up and exit(1).
4354+
*/
4355+
staticvoid
4356+
StartupPacketTimeoutHandler(void)
4357+
{
4358+
proc_exit(1);
4359+
}
4360+
4361+
43424362
/*
43434363
* RandomSalt
43444364
*/

‎src/backend/postmaster/startup.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@
2727
#include"storage/ipc.h"
2828
#include"storage/latch.h"
2929
#include"storage/pmsignal.h"
30-
#include"storage/proc.h"
30+
#include"storage/standby.h"
3131
#include"utils/guc.h"
32+
#include"utils/timeout.h"
3233

3334

3435
/*
@@ -185,20 +186,12 @@ StartupProcessMain(void)
185186

186187
/*
187188
* Properly accept or ignore signals the postmaster might send us.
188-
*
189-
* Note: ideally we'd not enable handle_standby_sig_alarm unless actually
190-
* doing hot standby, but we don't know that yet. Rely on it to not do
191-
* anything if it shouldn't.
192189
*/
193190
pqsignal(SIGHUP,StartupProcSigHupHandler);/* reload config file */
194191
pqsignal(SIGINT,SIG_IGN);/* ignore query cancel */
195192
pqsignal(SIGTERM,StartupProcShutdownHandler);/* request shutdown */
196193
pqsignal(SIGQUIT,startupproc_quickdie);/* hard crash time */
197-
if (EnableHotStandby)
198-
pqsignal(SIGALRM,handle_standby_sig_alarm);/* ignored unless
199-
* InHotStandby */
200-
else
201-
pqsignal(SIGALRM,SIG_IGN);
194+
InitializeTimeouts();/* establishes SIGALRM handler */
202195
pqsignal(SIGPIPE,SIG_IGN);
203196
pqsignal(SIGUSR1,StartupProcSigUsr1Handler);
204197
pqsignal(SIGUSR2,StartupProcTriggerHandler);
@@ -212,11 +205,20 @@ StartupProcessMain(void)
212205
pqsignal(SIGCONT,SIG_DFL);
213206
pqsignal(SIGWINCH,SIG_DFL);
214207

208+
/*
209+
* Register timeouts needed for standby mode
210+
*/
211+
RegisterTimeout(STANDBY_DEADLOCK_TIMEOUT,StandbyDeadLockHandler);
212+
RegisterTimeout(STANDBY_TIMEOUT,StandbyTimeoutHandler);
213+
215214
/*
216215
* Unblock signals (they were blocked when the postmaster forked us)
217216
*/
218217
PG_SETMASK(&UnBlockSig);
219218

219+
/*
220+
* Do what we came for.
221+
*/
220222
StartupXLOG();
221223

222224
/*

‎src/backend/replication/walsender.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include"utils/memutils.h"
6464
#include"utils/ps_status.h"
6565
#include"utils/resowner.h"
66+
#include"utils/timeout.h"
6667
#include"utils/timestamp.h"
6768

6869

@@ -1345,7 +1346,7 @@ WalSndSignals(void)
13451346
pqsignal(SIGINT,SIG_IGN);/* not used */
13461347
pqsignal(SIGTERM,WalSndShutdownHandler);/* request shutdown */
13471348
pqsignal(SIGQUIT,WalSndQuickDieHandler);/* hard crash time */
1348-
pqsignal(SIGALRM,SIG_IGN);
1349+
InitializeTimeouts();/* establishes SIGALRM handler */
13491350
pqsignal(SIGPIPE,SIG_IGN);
13501351
pqsignal(SIGUSR1,WalSndXLogSendHandler);/* request WAL sending */
13511352
pqsignal(SIGUSR2,WalSndLastCycleHandler);/* request a last cycle and

‎src/backend/storage/ipc/standby.c

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include"storage/sinvaladt.h"
2929
#include"storage/standby.h"
3030
#include"utils/ps_status.h"
31+
#include"utils/timeout.h"
3132
#include"utils/timestamp.h"
3233

3334
/* User-settable GUC parameters */
@@ -40,6 +41,7 @@ static List *RecoveryLockList;
4041
staticvoidResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId*waitlist,
4142
ProcSignalReasonreason);
4243
staticvoidResolveRecoveryConflictWithLock(OiddbOid,OidrelOid);
44+
staticvoidSendRecoveryConflictWithBufferPin(ProcSignalReasonreason);
4345
staticvoidLogCurrentRunningXacts(RunningTransactionsCurrRunningXacts);
4446
staticvoidLogAccessExclusiveLocks(intnlocks,xl_standby_lock*locks);
4547

@@ -370,13 +372,15 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
370372
* ResolveRecoveryConflictWithBufferPin is called from LockBufferForCleanup()
371373
* to resolve conflicts with other backends holding buffer pins.
372374
*
373-
* We either resolve conflicts immediately or set a SIGALRM to wake us at
374-
* the limit of our patience. The sleep in LockBufferForCleanup() is
375-
* performed here, for code clarity.
375+
* The ProcWaitForSignal() sleep normally done in LockBufferForCleanup()
376+
* (when not InHotStandby) is performed here, for code clarity.
377+
*
378+
* We either resolve conflicts immediately or set a timeout to wake us at
379+
* the limit of our patience.
376380
*
377381
* Resolve conflicts by sending a PROCSIG signal to all backends to check if
378382
* they hold one of the buffer pins that is blocking Startup process. If so,
379-
* backends will take an appropriate error action, ERROR or FATAL.
383+
*thosebackends will take an appropriate error action, ERROR or FATAL.
380384
*
381385
* We also must check for deadlocks. Deadlocks occur because if queries
382386
* wait on a lock, that must be behind an AccessExclusiveLock, which can only
@@ -389,32 +393,26 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
389393
*
390394
* Deadlocks are extremely rare, and relatively expensive to check for,
391395
* so we don't do a deadlock check right away ... only if we have had to wait
392-
* at least deadlock_timeout. Most of the logic about that is in proc.c.
396+
* at least deadlock_timeout.
393397
*/
394398
void
395399
ResolveRecoveryConflictWithBufferPin(void)
396400
{
397-
boolsig_alarm_enabled= false;
398401
TimestampTzltime;
399-
TimestampTznow;
400402

401403
Assert(InHotStandby);
402404

403405
ltime=GetStandbyLimitTime();
404-
now=GetCurrentTimestamp();
405406

406-
if (!ltime)
407+
if (ltime==0)
407408
{
408409
/*
409410
* We're willing to wait forever for conflicts, so set timeout for
410-
* deadlock check(only)
411+
* deadlock check only
411412
*/
412-
if (enable_standby_sig_alarm(now,now, true))
413-
sig_alarm_enabled= true;
414-
else
415-
elog(FATAL,"could not set timer for process wakeup");
413+
enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT,DeadlockTimeout);
416414
}
417-
elseif (now >=ltime)
415+
elseif (GetCurrentTimestamp() >=ltime)
418416
{
419417
/*
420418
* We're already behind, so clear a path as quickly as possible.
@@ -427,23 +425,23 @@ ResolveRecoveryConflictWithBufferPin(void)
427425
* Wake up at ltime, and check for deadlocks as well if we will be
428426
* waiting longer than deadlock_timeout
429427
*/
430-
if (enable_standby_sig_alarm(now,ltime, false))
431-
sig_alarm_enabled= true;
432-
else
433-
elog(FATAL,"could not set timer for process wakeup");
428+
enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT,DeadlockTimeout);
429+
enable_timeout_at(STANDBY_TIMEOUT,ltime);
434430
}
435431

436432
/* Wait to be signaled by UnpinBuffer() */
437433
ProcWaitForSignal();
438434

439-
if (sig_alarm_enabled)
440-
{
441-
if (!disable_standby_sig_alarm())
442-
elog(FATAL,"could not disable timer for process wakeup");
443-
}
435+
/*
436+
* Clear any timeout requests established above. We assume here that
437+
* the Startup process doesn't have any other timeouts than what this
438+
* function uses. If that stops being true, we could cancel the
439+
* timeouts individually, but that'd be slower.
440+
*/
441+
disable_all_timeouts(false);
444442
}
445443

446-
void
444+
staticvoid
447445
SendRecoveryConflictWithBufferPin(ProcSignalReasonreason)
448446
{
449447
Assert(reason==PROCSIG_RECOVERY_CONFLICT_BUFFERPIN||
@@ -492,6 +490,38 @@ CheckRecoveryConflictDeadlock(void)
492490
errdetail("User transaction caused buffer deadlock with recovery.")));
493491
}
494492

493+
494+
/* --------------------------------
495+
*timeout handler routines
496+
* --------------------------------
497+
*/
498+
499+
/*
500+
* StandbyDeadLockHandler() will be called if STANDBY_DEADLOCK_TIMEOUT
501+
* occurs before STANDBY_TIMEOUT. Send out a request for hot-standby
502+
* backends to check themselves for deadlocks.
503+
*/
504+
void
505+
StandbyDeadLockHandler(void)
506+
{
507+
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
508+
}
509+
510+
/*
511+
* StandbyTimeoutHandler() will be called if STANDBY_TIMEOUT is exceeded.
512+
* Send out a request to release conflicting buffer pins unconditionally,
513+
* so we can press ahead with applying changes in recovery.
514+
*/
515+
void
516+
StandbyTimeoutHandler(void)
517+
{
518+
/* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
519+
disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
520+
521+
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
522+
}
523+
524+
495525
/*
496526
* -----------------------------------------------------
497527
* Locking in Recovery Mode

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp