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

Commitebfc56d

Browse files
committed
Handle impending sinval queue overflow by means of a separate signal
(SIGUSR1, which we have not been using recently) instead of piggybackingon SIGUSR2-driven NOTIFY processing. This has several good results:the processing needed to drain the sinval queue is a lot less than theprocessing needed to answer a NOTIFY; there's less contention since wedon't have a bunch of backends all trying to acquire exclusive lock onpg_listener; backends that are sitting inside a transaction block canstill drain the queue, whereas NOTIFY processing can't run if there'san open transaction block. (This last is a fairly serious issue thatI don't think we ever recognized before --- with clients like JDBC thattend to sit with open transaction blocks, the sinval queue drainingmechanism never really worked as intended, probably resulting in a lotof useless cache-reset overhead.) This is the last of several proposedchanges in response to Philip Warner's recent report of sinval-inducedperformance problems.
1 parent4d86ae4 commitebfc56d

File tree

8 files changed

+289
-37
lines changed

8 files changed

+289
-37
lines changed

‎src/backend/commands/async.c

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.110 2004/05/22 21:58:24 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.111 2004/05/23 03:50:45 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -86,6 +86,7 @@
8686
#include"libpq/pqformat.h"
8787
#include"miscadmin.h"
8888
#include"storage/ipc.h"
89+
#include"storage/sinval.h"
8990
#include"tcop/tcopprot.h"
9091
#include"utils/fmgroids.h"
9192
#include"utils/ps_status.h"
@@ -607,7 +608,7 @@ AtAbort_Notify(void)
607608

608609
/*
609610
*--------------------------------------------------------------
610-
*Async_NotifyHandler
611+
*NotifyInterruptHandler
611612
*
612613
*This is the signal handler for SIGUSR2.
613614
*
@@ -623,7 +624,7 @@ AtAbort_Notify(void)
623624
*--------------------------------------------------------------
624625
*/
625626
void
626-
Async_NotifyHandler(SIGNAL_ARGS)
627+
NotifyInterruptHandler(SIGNAL_ARGS)
627628
{
628629
intsave_errno=errno;
629630

@@ -669,12 +670,12 @@ Async_NotifyHandler(SIGNAL_ARGS)
669670
{
670671
/* Here, it is finally safe to do stuff. */
671672
if (Trace_notify)
672-
elog(DEBUG1,"Async_NotifyHandler: perform async notify");
673+
elog(DEBUG1,"NotifyInterruptHandler: perform async notify");
673674

674675
ProcessIncomingNotify();
675676

676677
if (Trace_notify)
677-
elog(DEBUG1,"Async_NotifyHandler: done");
678+
elog(DEBUG1,"NotifyInterruptHandler: done");
678679
}
679680
}
680681

@@ -766,12 +767,20 @@ EnableNotifyInterrupt(void)
766767
*This is called by the PostgresMain main loop just after receiving
767768
*a frontend command. Signal handler execution of inbound notifies
768769
*is disabled until the next EnableNotifyInterrupt call.
770+
*
771+
*The SIGUSR1 signal handler also needs to call this, so as to
772+
*prevent conflicts if one signal interrupts the other. So we
773+
*must return the previous state of the flag.
769774
* --------------------------------------------------------------
770775
*/
771-
void
776+
bool
772777
DisableNotifyInterrupt(void)
773778
{
779+
boolresult= (notifyInterruptEnabled!=0);
780+
774781
notifyInterruptEnabled=0;
782+
783+
returnresult;
775784
}
776785

777786
/*
@@ -785,10 +794,6 @@ DisableNotifyInterrupt(void)
785794
*and clear the notification field in pg_listener until next time.
786795
*
787796
*NOTE: since we are outside any transaction, we must create our own.
788-
*
789-
* Results:
790-
*XXX
791-
*
792797
* --------------------------------------------------------------
793798
*/
794799
staticvoid
@@ -803,11 +808,15 @@ ProcessIncomingNotify(void)
803808
Datumvalue[Natts_pg_listener];
804809
charrepl[Natts_pg_listener],
805810
nulls[Natts_pg_listener];
811+
boolcatchup_enabled;
812+
813+
/* Must prevent SIGUSR1 interrupt while I am running */
814+
catchup_enabled=DisableCatchupInterrupt();
806815

807816
if (Trace_notify)
808817
elog(DEBUG1,"ProcessIncomingNotify");
809818

810-
set_ps_display("async_notify");
819+
set_ps_display("notify interrupt");
811820

812821
notifyInterruptOccurred=0;
813822

@@ -883,6 +892,9 @@ ProcessIncomingNotify(void)
883892

884893
if (Trace_notify)
885894
elog(DEBUG1,"ProcessIncomingNotify: done");
895+
896+
if (catchup_enabled)
897+
EnableCatchupInterrupt();
886898
}
887899

888900
/*

‎src/backend/postmaster/postmaster.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
*
3838
*
3939
* IDENTIFICATION
40-
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.393 2004/05/21 05:07:57 tgl Exp $
40+
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.394 2004/05/23 03:50:45 tgl Exp $
4141
*
4242
* NOTES
4343
*
@@ -2861,11 +2861,11 @@ sigusr1_handler(SIGNAL_ARGS)
28612861
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_CHILDREN))
28622862
{
28632863
/*
2864-
* SendSIGUSR2 to all children (triggersAsyncNotifyHandler). See
2865-
* storage/ipc/sinvaladt.c for the use of this.
2864+
* SendSIGUSR1 to all children (triggersCatchupInterruptHandler).
2865+
*Seestorage/ipc/sinval[adt].c for the use of this.
28662866
*/
28672867
if (Shutdown==NoShutdown)
2868-
SignalChildren(SIGUSR2);
2868+
SignalChildren(SIGUSR1);
28692869
}
28702870

28712871
PG_SETMASK(&UnBlockSig);

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

Lines changed: 230 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,46 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.62 2003/11/29 19:51:56 pgsql Exp $
11+
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.63 2004/05/23 03:50:45 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
1515
#include"postgres.h"
1616

17+
#include<signal.h>
1718

19+
#include"commands/async.h"
20+
#include"storage/ipc.h"
1821
#include"storage/proc.h"
1922
#include"storage/sinval.h"
2023
#include"storage/sinvaladt.h"
24+
#include"utils/inval.h"
2125
#include"utils/tqual.h"
2226
#include"miscadmin.h"
2327

2428

29+
/*
30+
* Because backends sitting idle will not be reading sinval events, we
31+
* need a way to give an idle backend a swift kick in the rear and make
32+
* it catch up before the sinval queue overflows and forces everyone
33+
* through a cache reset exercise. This is done by broadcasting SIGUSR1
34+
* to all backends when the queue is threatening to become full.
35+
*
36+
* State for catchup events consists of two flags: one saying whether
37+
* the signal handler is currently allowed to call ProcessCatchupEvent
38+
* directly, and one saying whether the signal has occurred but the handler
39+
* was not allowed to call ProcessCatchupEvent at the time.
40+
*
41+
* NB: the "volatile" on these declarations is critical! If your compiler
42+
* does not grok "volatile", you'd be best advised to compile this file
43+
* with all optimization turned off.
44+
*/
45+
staticvolatileintcatchupInterruptEnabled=0;
46+
staticvolatileintcatchupInterruptOccurred=0;
47+
48+
staticvoidProcessCatchupEvent(void);
49+
50+
2551
/****************************************************************************/
2652
/*CreateSharedInvalidationState() Initialize SI buffer*/
2753
/**/
@@ -91,6 +117,12 @@ ReceiveSharedInvalidMessages(
91117

92118
for (;;)
93119
{
120+
/*
121+
* We can discard any pending catchup event, since we will not exit
122+
* this loop until we're fully caught up.
123+
*/
124+
catchupInterruptOccurred=0;
125+
94126
/*
95127
* We can run SIGetDataEntry in parallel with other backends
96128
* running SIGetDataEntry for themselves, since each instance will
@@ -137,6 +169,203 @@ ReceiveSharedInvalidMessages(
137169
}
138170

139171

172+
/*
173+
* CatchupInterruptHandler
174+
*
175+
* This is the signal handler for SIGUSR1.
176+
*
177+
* If we are idle (catchupInterruptEnabled is set), we can safely
178+
* invoke ProcessCatchupEvent directly. Otherwise, just set a flag
179+
* to do it later. (Note that it's quite possible for normal processing
180+
* of the current transaction to cause ReceiveSharedInvalidMessages()
181+
* to be run later on; in that case the flag will get cleared again,
182+
* since there's no longer any reason to do anything.)
183+
*/
184+
void
185+
CatchupInterruptHandler(SIGNAL_ARGS)
186+
{
187+
intsave_errno=errno;
188+
189+
/*
190+
* Note: this is a SIGNAL HANDLER.You must be very wary what you do
191+
* here.
192+
*/
193+
194+
/* Don't joggle the elbow of proc_exit */
195+
if (proc_exit_inprogress)
196+
return;
197+
198+
if (catchupInterruptEnabled)
199+
{
200+
boolsave_ImmediateInterruptOK=ImmediateInterruptOK;
201+
202+
/*
203+
* We may be called while ImmediateInterruptOK is true; turn it
204+
* off while messing with the catchup state. (We would have to
205+
* save and restore it anyway, because PGSemaphore operations
206+
* inside ProcessCatchupEvent() might reset it.)
207+
*/
208+
ImmediateInterruptOK= false;
209+
210+
/*
211+
* I'm not sure whether some flavors of Unix might allow another
212+
* SIGUSR1 occurrence to recursively interrupt this routine. To
213+
* cope with the possibility, we do the same sort of dance that
214+
* EnableCatchupInterrupt must do --- see that routine for
215+
* comments.
216+
*/
217+
catchupInterruptEnabled=0;/* disable any recursive signal */
218+
catchupInterruptOccurred=1;/* do at least one iteration */
219+
for (;;)
220+
{
221+
catchupInterruptEnabled=1;
222+
if (!catchupInterruptOccurred)
223+
break;
224+
catchupInterruptEnabled=0;
225+
if (catchupInterruptOccurred)
226+
{
227+
/* Here, it is finally safe to do stuff. */
228+
ProcessCatchupEvent();
229+
}
230+
}
231+
232+
/*
233+
* Restore ImmediateInterruptOK, and check for interrupts if
234+
* needed.
235+
*/
236+
ImmediateInterruptOK=save_ImmediateInterruptOK;
237+
if (save_ImmediateInterruptOK)
238+
CHECK_FOR_INTERRUPTS();
239+
}
240+
else
241+
{
242+
/*
243+
* In this path it is NOT SAFE to do much of anything, except
244+
* this:
245+
*/
246+
catchupInterruptOccurred=1;
247+
}
248+
249+
errno=save_errno;
250+
}
251+
252+
/*
253+
* EnableCatchupInterrupt
254+
*
255+
* This is called by the PostgresMain main loop just before waiting
256+
* for a frontend command. We process any pending catchup events,
257+
* and enable the signal handler to process future events directly.
258+
*
259+
* NOTE: the signal handler starts out disabled, and stays so until
260+
* PostgresMain calls this the first time.
261+
*/
262+
void
263+
EnableCatchupInterrupt(void)
264+
{
265+
/*
266+
* This code is tricky because we are communicating with a signal
267+
* handler that could interrupt us at any point. If we just checked
268+
* catchupInterruptOccurred and then set catchupInterruptEnabled, we
269+
* could fail to respond promptly to a signal that happens in between
270+
* those two steps. (A very small time window, perhaps, but Murphy's
271+
* Law says you can hit it...)Instead, we first set the enable flag,
272+
* then test the occurred flag. If we see an unserviced interrupt has
273+
* occurred, we re-clear the enable flag before going off to do the
274+
* service work. (That prevents re-entrant invocation of
275+
* ProcessCatchupEvent() if another interrupt occurs.) If an
276+
* interrupt comes in between the setting and clearing of
277+
* catchupInterruptEnabled, then it will have done the service work and
278+
* left catchupInterruptOccurred zero, so we have to check again after
279+
* clearing enable. The whole thing has to be in a loop in case
280+
* another interrupt occurs while we're servicing the first. Once we
281+
* get out of the loop, enable is set and we know there is no
282+
* unserviced interrupt.
283+
*
284+
* NB: an overenthusiastic optimizing compiler could easily break this
285+
* code. Hopefully, they all understand what "volatile" means these
286+
* days.
287+
*/
288+
for (;;)
289+
{
290+
catchupInterruptEnabled=1;
291+
if (!catchupInterruptOccurred)
292+
break;
293+
catchupInterruptEnabled=0;
294+
if (catchupInterruptOccurred)
295+
{
296+
ProcessCatchupEvent();
297+
}
298+
}
299+
}
300+
301+
/*
302+
* DisableCatchupInterrupt
303+
*
304+
* This is called by the PostgresMain main loop just after receiving
305+
* a frontend command. Signal handler execution of catchup events
306+
* is disabled until the next EnableCatchupInterrupt call.
307+
*
308+
* The SIGUSR2 signal handler also needs to call this, so as to
309+
* prevent conflicts if one signal interrupts the other. So we
310+
* must return the previous state of the flag.
311+
*/
312+
bool
313+
DisableCatchupInterrupt(void)
314+
{
315+
boolresult= (catchupInterruptEnabled!=0);
316+
317+
catchupInterruptEnabled=0;
318+
319+
returnresult;
320+
}
321+
322+
/*
323+
* ProcessCatchupEvent
324+
*
325+
* Respond to a catchup event (SIGUSR1) from another backend.
326+
*
327+
* This is called either directly from the SIGUSR1 signal handler,
328+
* or the next time control reaches the outer idle loop (assuming
329+
* there's still anything to do by then).
330+
*/
331+
staticvoid
332+
ProcessCatchupEvent(void)
333+
{
334+
boolnotify_enabled;
335+
336+
/* Must prevent SIGUSR2 interrupt while I am running */
337+
notify_enabled=DisableNotifyInterrupt();
338+
339+
/*
340+
* What we need to do here is cause ReceiveSharedInvalidMessages()
341+
* to run, which will do the necessary work and also reset the
342+
* catchupInterruptOccurred flag. If we are inside a transaction
343+
* we can just call AcceptInvalidationMessages() to do this. If we
344+
* aren't, we start and immediately end a transaction; the call to
345+
* AcceptInvalidationMessages() happens down inside transaction start.
346+
*
347+
* It is awfully tempting to just call AcceptInvalidationMessages()
348+
* without the rest of the xact start/stop overhead, and I think that
349+
* would actually work in the normal case; but I am not sure that things
350+
* would clean up nicely if we got an error partway through.
351+
*/
352+
if (IsTransactionOrTransactionBlock())
353+
{
354+
elog(DEBUG4,"ProcessCatchupEvent inside transaction");
355+
AcceptInvalidationMessages();
356+
}
357+
else
358+
{
359+
elog(DEBUG4,"ProcessCatchupEvent outside transaction");
360+
StartTransactionCommand();
361+
CommitTransactionCommand();
362+
}
363+
364+
if (notify_enabled)
365+
EnableNotifyInterrupt();
366+
}
367+
368+
140369
/****************************************************************************/
141370
/* Functions that need to scan the PGPROC structures of all running backends. */
142371
/* It's a bit strange to keep these in sinval.c, since they don't have any*/

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp