Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * walsummarizer.c
4 : : *
5 : : * Background process to perform WAL summarization, if it is enabled.
6 : : * It continuously scans the write-ahead log and periodically emits a
7 : : * summary file which indicates which blocks in which relation forks
8 : : * were modified by WAL records in the LSN range covered by the summary
9 : : * file. See walsummary.c and blkreftable.c for more details on the
10 : : * naming and contents of WAL summary files.
11 : : *
12 : : * If configured to do, this background process will also remove WAL
13 : : * summary files when the file timestamp is older than a configurable
14 : : * threshold (but only if the WAL has been removed first).
15 : : *
16 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
17 : : *
18 : : * IDENTIFICATION
19 : : * src/backend/postmaster/walsummarizer.c
20 : : *
21 : : *-------------------------------------------------------------------------
22 : : */
23 : : #include "postgres.h"
24 : :
25 : : #include "access/timeline.h"
26 : : #include "access/visibilitymap.h"
27 : : #include "access/xlog.h"
28 : : #include "access/xlog_internal.h"
29 : : #include "access/xlogrecovery.h"
30 : : #include "access/xlogutils.h"
31 : : #include "backup/walsummary.h"
32 : : #include "catalog/storage_xlog.h"
33 : : #include "commands/dbcommands_xlog.h"
34 : : #include "common/blkreftable.h"
35 : : #include "libpq/pqsignal.h"
36 : : #include "miscadmin.h"
37 : : #include "pgstat.h"
38 : : #include "postmaster/auxprocess.h"
39 : : #include "postmaster/interrupt.h"
40 : : #include "postmaster/walsummarizer.h"
41 : : #include "replication/walreceiver.h"
42 : : #include "storage/aio_subsys.h"
43 : : #include "storage/fd.h"
44 : : #include "storage/ipc.h"
45 : : #include "storage/latch.h"
46 : : #include "storage/lwlock.h"
47 : : #include "storage/proc.h"
48 : : #include "storage/procsignal.h"
49 : : #include "storage/shmem.h"
50 : : #include "utils/guc.h"
51 : : #include "utils/memutils.h"
52 : : #include "utils/wait_event.h"
53 : :
54 : : /*
55 : : * Data in shared memory related to WAL summarization.
56 : : */
57 : : typedef struct
58 : : {
59 : : /*
60 : : * These fields are protected by WALSummarizerLock.
61 : : *
62 : : * Until we've discovered what summary files already exist on disk and
63 : : * stored that information in shared memory, initialized is false and the
64 : : * other fields here contain no meaningful information. After that has
65 : : * been done, initialized is true.
66 : : *
67 : : * summarized_tli and summarized_lsn indicate the last LSN and TLI at
68 : : * which the next summary file will start. Normally, these are the LSN and
69 : : * TLI at which the last file ended; in such case, lsn_is_exact is true.
70 : : * If, however, the LSN is just an approximation, then lsn_is_exact is
71 : : * false. This can happen if, for example, there are no existing WAL
72 : : * summary files at startup. In that case, we have to derive the position
73 : : * at which to start summarizing from the WAL files that exist on disk,
74 : : * and so the LSN might point to the start of the next file even though
75 : : * that might happen to be in the middle of a WAL record.
76 : : *
77 : : * summarizer_pgprocno is the proc number of the summarizer process, if
78 : : * one is running, or else INVALID_PROC_NUMBER.
79 : : *
80 : : * pending_lsn is used by the summarizer to advertise the ending LSN of a
81 : : * record it has recently read. It shouldn't ever be less than
82 : : * summarized_lsn, but might be greater, because the summarizer buffers
83 : : * data for a range of LSNs in memory before writing out a new file.
84 : : */
85 : : bool initialized;
86 : : TimeLineID summarized_tli;
87 : : XLogRecPtr summarized_lsn;
88 : : bool lsn_is_exact;
89 : : ProcNumber summarizer_pgprocno;
90 : : XLogRecPtr pending_lsn;
91 : :
92 : : /*
93 : : * This field handles its own synchronization.
94 : : */
95 : : ConditionVariable summary_file_cv;
96 : : } WalSummarizerData;
97 : :
98 : : /*
99 : : * Private data for our xlogreader's page read callback.
100 : : */
101 : : typedef struct
102 : : {
103 : : TimeLineID tli;
104 : : bool historic;
105 : : XLogRecPtr read_upto;
106 : : bool end_of_wal;
107 : : } SummarizerReadLocalXLogPrivate;
108 : :
109 : : /* Pointer to shared memory state. */
110 : : static WalSummarizerData *WalSummarizerCtl;
111 : :
112 : : /*
113 : : * When we reach end of WAL and need to read more, we sleep for a number of
114 : : * milliseconds that is an integer multiple of MS_PER_SLEEP_QUANTUM. This is
115 : : * the multiplier. It should vary between 1 and MAX_SLEEP_QUANTA, depending
116 : : * on system activity. See summarizer_wait_for_wal() for how we adjust this.
117 : : */
118 : : static long sleep_quanta = 1;
119 : :
120 : : /*
121 : : * The sleep time will always be a multiple of 200ms and will not exceed
122 : : * thirty seconds (150 * 200 = 30 * 1000). Note that the timeout here needs
123 : : * to be substantially less than the maximum amount of time for which an
124 : : * incremental backup will wait for this process to catch up. Otherwise, an
125 : : * incremental backup might time out on an idle system just because we sleep
126 : : * for too long.
127 : : */
128 : : #define MAX_SLEEP_QUANTA 150
129 : : #define MS_PER_SLEEP_QUANTUM 200
130 : :
131 : : /*
132 : : * This is a count of the number of pages of WAL that we've read since the
133 : : * last time we waited for more WAL to appear.
134 : : */
135 : : static long pages_read_since_last_sleep = 0;
136 : :
137 : : /*
138 : : * Most recent RedoRecPtr value observed by MaybeRemoveOldWalSummaries.
139 : : */
140 : : static XLogRecPtr redo_pointer_at_last_summary_removal = InvalidXLogRecPtr;
141 : :
142 : : /*
143 : : * GUC parameters
144 : : */
145 : : bool summarize_wal = false;
146 : : int wal_summary_keep_time = 10 * HOURS_PER_DAY * MINS_PER_HOUR;
147 : :
148 : : static void WalSummarizerShutdown(int code, Datum arg);
149 : : static XLogRecPtr GetLatestLSN(TimeLineID *tli);
150 : : static void ProcessWalSummarizerInterrupts(void);
151 : : static XLogRecPtr SummarizeWAL(TimeLineID tli, XLogRecPtr start_lsn,
152 : : bool exact, XLogRecPtr switch_lsn,
153 : : XLogRecPtr maximum_lsn);
154 : : static void SummarizeDbaseRecord(XLogReaderState *xlogreader,
155 : : BlockRefTable *brtab);
156 : : static void SummarizeSmgrRecord(XLogReaderState *xlogreader,
157 : : BlockRefTable *brtab);
158 : : static void SummarizeXactRecord(XLogReaderState *xlogreader,
159 : : BlockRefTable *brtab);
160 : : static bool SummarizeXlogRecord(XLogReaderState *xlogreader,
161 : : bool *new_fast_forward);
162 : : static int summarizer_read_local_xlog_page(XLogReaderState *state,
163 : : XLogRecPtr targetPagePtr,
164 : : int reqLen,
165 : : XLogRecPtr targetRecPtr,
166 : : char *cur_page);
167 : : static void summarizer_wait_for_wal(void);
168 : : static void MaybeRemoveOldWalSummaries(void);
169 : :
170 : : /*
171 : : * Amount of shared memory required for this module.
172 : : */
173 : : Size
816 rhaas@postgresql.org 174 :CBC 3297 : WalSummarizerShmemSize(void)
175 : : {
176 : 3297 : return sizeof(WalSummarizerData);
177 : : }
178 : :
179 : : /*
180 : : * Create or attach to shared memory segment for this module.
181 : : */
182 : : void
183 : 1150 : WalSummarizerShmemInit(void)
184 : : {
185 : : bool found;
186 : :
187 : 1150 : WalSummarizerCtl = (WalSummarizerData *)
188 : 1150 : ShmemInitStruct("Wal Summarizer Ctl", WalSummarizerShmemSize(),
189 : : &found);
190 : :
191 [ + - ]: 1150 : if (!found)
192 : : {
193 : : /*
194 : : * First time through, so initialize.
195 : : *
196 : : * We're just filling in dummy values here -- the real initialization
197 : : * will happen when GetOldestUnsummarizedLSN() is called for the first
198 : : * time.
199 : : */
200 : 1150 : WalSummarizerCtl->initialized = false;
201 : 1150 : WalSummarizerCtl->summarized_tli = 0;
202 : 1150 : WalSummarizerCtl->summarized_lsn = InvalidXLogRecPtr;
203 : 1150 : WalSummarizerCtl->lsn_is_exact = false;
742 heikki.linnakangas@i 204 : 1150 : WalSummarizerCtl->summarizer_pgprocno = INVALID_PROC_NUMBER;
816 rhaas@postgresql.org 205 : 1150 : WalSummarizerCtl->pending_lsn = InvalidXLogRecPtr;
206 : 1150 : ConditionVariableInit(&WalSummarizerCtl->summary_file_cv);
207 : : }
208 : 1150 : }
209 : :
210 : : /*
211 : : * Entry point for walsummarizer process.
212 : : */
213 : : void
387 peter@eisentraut.org 214 : 3 : WalSummarizerMain(const void *startup_data, size_t startup_data_len)
215 : : {
216 : : sigjmp_buf local_sigjmp_buf;
217 : : MemoryContext context;
218 : :
219 : : /*
220 : : * Within this function, 'current_lsn' and 'current_tli' refer to the
221 : : * point from which the next WAL summary file should start. 'exact' is
222 : : * true if 'current_lsn' is known to be the start of a WAL record or WAL
223 : : * segment, and false if it might be in the middle of a record someplace.
224 : : *
225 : : * 'switch_lsn' and 'switch_tli', if set, are the LSN at which we need to
226 : : * switch to a new timeline and the timeline to which we need to switch.
227 : : * If not set, we either haven't figured out the answers yet or we're
228 : : * already on the latest timeline.
229 : : */
230 : : XLogRecPtr current_lsn;
231 : : TimeLineID current_tli;
232 : : bool exact;
816 rhaas@postgresql.org 233 : 3 : XLogRecPtr switch_lsn = InvalidXLogRecPtr;
234 : 3 : TimeLineID switch_tli = 0;
235 : :
727 heikki.linnakangas@i 236 [ - + ]: 3 : Assert(startup_data_len == 0);
237 : :
238 : 3 : AuxiliaryProcessMainCommon();
239 : :
816 rhaas@postgresql.org 240 [ - + ]: 3 : ereport(DEBUG1,
241 : : (errmsg_internal("WAL summarizer started")));
242 : :
243 : : /*
244 : : * Properly accept or ignore signals the postmaster might send us
245 : : */
246 : 3 : pqsignal(SIGHUP, SignalHandlerForConfigReload);
26 heikki.linnakangas@i 247 :GNC 3 : pqsignal(SIGINT, SIG_IGN); /* no query to cancel */
816 rhaas@postgresql.org 248 :CBC 3 : pqsignal(SIGTERM, SignalHandlerForShutdownRequest);
249 : : /* SIGQUIT handler was already set up by InitPostmasterChild */
250 : 3 : pqsignal(SIGALRM, SIG_IGN);
251 : 3 : pqsignal(SIGPIPE, SIG_IGN);
252 : 3 : pqsignal(SIGUSR1, procsignal_sigusr1_handler);
253 : 3 : pqsignal(SIGUSR2, SIG_IGN); /* not used */
254 : :
255 : : /* Advertise ourselves. */
794 256 : 3 : on_shmem_exit(WalSummarizerShutdown, (Datum) 0);
816 257 : 3 : LWLockAcquire(WALSummarizerLock, LW_EXCLUSIVE);
752 heikki.linnakangas@i 258 : 3 : WalSummarizerCtl->summarizer_pgprocno = MyProcNumber;
816 rhaas@postgresql.org 259 : 3 : LWLockRelease(WALSummarizerLock);
260 : :
261 : : /* Create and switch to a memory context that we can reset on error. */
262 : 3 : context = AllocSetContextCreate(TopMemoryContext,
263 : : "Wal Summarizer",
264 : : ALLOCSET_DEFAULT_SIZES);
265 : 3 : MemoryContextSwitchTo(context);
266 : :
267 : : /*
268 : : * Reset some signals that are accepted by postmaster but not here
269 : : */
270 : 3 : pqsignal(SIGCHLD, SIG_DFL);
271 : :
272 : : /*
273 : : * If an exception is encountered, processing resumes here.
274 : : */
275 [ - + ]: 3 : if (sigsetjmp(local_sigjmp_buf, 1) != 0)
276 : : {
277 : : /* Since not using PG_TRY, must reset error stack by hand */
816 rhaas@postgresql.org 278 :UBC 0 : error_context_stack = NULL;
279 : :
280 : : /* Prevent interrupts while cleaning up */
281 : 0 : HOLD_INTERRUPTS();
282 : :
283 : : /* Report the error to the server log */
284 : 0 : EmitErrorReport();
285 : :
286 : : /* Release resources we might have acquired. */
287 : 0 : LWLockReleaseAll();
288 : 0 : ConditionVariableCancelSleep();
289 : 0 : pgstat_report_wait_end();
363 andres@anarazel.de 290 : 0 : pgaio_error_cleanup();
816 rhaas@postgresql.org 291 : 0 : ReleaseAuxProcessResources(false);
292 : 0 : AtEOXact_Files(false);
293 : 0 : AtEOXact_HashTables(false);
294 : :
295 : : /*
296 : : * Now return to normal top-level context and clear ErrorContext for
297 : : * next time.
298 : : */
299 : 0 : MemoryContextSwitchTo(context);
300 : 0 : FlushErrorState();
301 : :
302 : : /* Flush any leaked data in the top-level context */
303 : 0 : MemoryContextReset(context);
304 : :
305 : : /* Now we can allow interrupts again */
306 [ # # ]: 0 : RESUME_INTERRUPTS();
307 : :
308 : : /*
309 : : * Sleep for 10 seconds before attempting to resume operations in
310 : : * order to avoid excessive logging.
311 : : *
312 : : * Many of the likely error conditions are things that will repeat
313 : : * every time. For example, if the WAL can't be read or the summary
314 : : * can't be written, only administrator action will cure the problem.
315 : : * So a really fast retry time doesn't seem to be especially
316 : : * beneficial, and it will clutter the logs.
317 : : */
526 heikki.linnakangas@i 318 : 0 : (void) WaitLatch(NULL,
319 : : WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
320 : : 10000,
321 : : WAIT_EVENT_WAL_SUMMARIZER_ERROR);
322 : : }
323 : :
324 : : /* We can now handle ereport(ERROR) */
816 rhaas@postgresql.org 325 :CBC 3 : PG_exception_stack = &local_sigjmp_buf;
326 : :
327 : : /*
328 : : * Unblock signals (they were blocked when the postmaster forked us)
329 : : */
330 : 3 : sigprocmask(SIG_SETMASK, &UnBlockSig, NULL);
331 : :
332 : : /*
333 : : * Fetch information about previous progress from shared memory, and ask
334 : : * GetOldestUnsummarizedLSN to reset pending_lsn to summarized_lsn. We
335 : : * might be recovering from an error, and if so, pending_lsn might have
336 : : * advanced past summarized_lsn, but any WAL we read previously has been
337 : : * lost and will need to be reread.
338 : : *
339 : : * If we discover that WAL summarization is not enabled, just exit.
340 : : */
628 341 : 3 : current_lsn = GetOldestUnsummarizedLSN(¤t_tli, &exact);
129 alvherre@kurilemu.de 342 [ + - ]:GNC 3 : if (!XLogRecPtrIsValid(current_lsn))
816 rhaas@postgresql.org 343 :UBC 0 : proc_exit(0);
344 : :
345 : : /*
346 : : * Loop forever
347 : : */
348 : : for (;;)
816 rhaas@postgresql.org 349 :CBC 18 : {
350 : : XLogRecPtr latest_lsn;
351 : : TimeLineID latest_tli;
352 : : XLogRecPtr end_of_summary_lsn;
353 : :
354 : : /* Flush any leaked data in the top-level context */
355 : 21 : MemoryContextReset(context);
356 : :
357 : : /* Process any signals received recently. */
375 heikki.linnakangas@i 358 : 21 : ProcessWalSummarizerInterrupts();
359 : :
360 : : /* If it's time to remove any old WAL summaries, do that now. */
816 rhaas@postgresql.org 361 : 21 : MaybeRemoveOldWalSummaries();
362 : :
363 : : /* Find the LSN and TLI up to which we can safely summarize. */
364 : 21 : latest_lsn = GetLatestLSN(&latest_tli);
365 : :
366 : : /*
367 : : * If we're summarizing a historic timeline and we haven't yet
368 : : * computed the point at which to switch to the next timeline, do that
369 : : * now.
370 : : *
371 : : * Note that if this is a standby, what was previously the current
372 : : * timeline could become historic at any time.
373 : : *
374 : : * We could try to make this more efficient by caching the results of
375 : : * readTimeLineHistory when latest_tli has not changed, but since we
376 : : * only have to do this once per timeline switch, we probably wouldn't
377 : : * save any significant amount of work in practice.
378 : : */
129 alvherre@kurilemu.de 379 [ - + - - ]:GNC 21 : if (current_tli != latest_tli && !XLogRecPtrIsValid(switch_lsn))
380 : : {
816 rhaas@postgresql.org 381 :UBC 0 : List *tles = readTimeLineHistory(latest_tli);
382 : :
383 : 0 : switch_lsn = tliSwitchPoint(current_tli, tles, &switch_tli);
384 [ # # ]: 0 : ereport(DEBUG1,
385 : : errmsg_internal("switch point from TLI %u to TLI %u is at %X/%08X",
386 : : current_tli, switch_tli, LSN_FORMAT_ARGS(switch_lsn)));
387 : : }
388 : :
389 : : /*
390 : : * If we've reached the switch LSN, we can't summarize anything else
391 : : * on this timeline. Switch to the next timeline and go around again,
392 : : * backing up to the exact switch point if we passed it.
393 : : */
129 alvherre@kurilemu.de 394 [ - + - - ]:GNC 21 : if (XLogRecPtrIsValid(switch_lsn) && current_lsn >= switch_lsn)
395 : : {
396 : : /* Restart summarization from switch point. */
816 rhaas@postgresql.org 397 :UBC 0 : current_tli = switch_tli;
597 398 : 0 : current_lsn = switch_lsn;
399 : :
400 : : /* Next timeline and switch point, if any, not yet known. */
816 401 : 0 : switch_lsn = InvalidXLogRecPtr;
402 : 0 : switch_tli = 0;
403 : :
404 : : /* Update (really, rewind, if needed) state in shared memory. */
597 405 : 0 : LWLockAcquire(WALSummarizerLock, LW_EXCLUSIVE);
406 : 0 : WalSummarizerCtl->summarized_lsn = current_lsn;
407 : 0 : WalSummarizerCtl->summarized_tli = current_tli;
408 : 0 : WalSummarizerCtl->lsn_is_exact = true;
409 : 0 : WalSummarizerCtl->pending_lsn = current_lsn;
410 : 0 : LWLockRelease(WALSummarizerLock);
411 : :
816 412 : 0 : continue;
413 : : }
414 : :
415 : : /* Summarize WAL. */
816 rhaas@postgresql.org 416 :CBC 21 : end_of_summary_lsn = SummarizeWAL(current_tli,
417 : : current_lsn, exact,
418 : : switch_lsn, latest_lsn);
129 alvherre@kurilemu.de 419 [ - + ]:GNC 18 : Assert(XLogRecPtrIsValid(end_of_summary_lsn));
816 rhaas@postgresql.org 420 [ - + ]:CBC 18 : Assert(end_of_summary_lsn >= current_lsn);
421 : :
422 : : /*
423 : : * Update state for next loop iteration.
424 : : *
425 : : * Next summary file should start from exactly where this one ended.
426 : : */
427 : 18 : current_lsn = end_of_summary_lsn;
428 : 18 : exact = true;
429 : :
430 : : /* Update state in shared memory. */
431 : 18 : LWLockAcquire(WALSummarizerLock, LW_EXCLUSIVE);
432 : 18 : WalSummarizerCtl->summarized_lsn = end_of_summary_lsn;
433 : 18 : WalSummarizerCtl->summarized_tli = current_tli;
434 : 18 : WalSummarizerCtl->lsn_is_exact = true;
435 : 18 : WalSummarizerCtl->pending_lsn = end_of_summary_lsn;
436 : 18 : LWLockRelease(WALSummarizerLock);
437 : :
438 : : /* Wake up anyone waiting for more summary files to be written. */
439 : 18 : ConditionVariableBroadcast(&WalSummarizerCtl->summary_file_cv);
440 : : }
441 : : }
442 : :
443 : : /*
444 : : * Get information about the state of the WAL summarizer.
445 : : */
446 : : void
794 rhaas@postgresql.org 447 :UBC 0 : GetWalSummarizerState(TimeLineID *summarized_tli, XLogRecPtr *summarized_lsn,
448 : : XLogRecPtr *pending_lsn, int *summarizer_pid)
449 : : {
450 : 0 : LWLockAcquire(WALSummarizerLock, LW_SHARED);
451 [ # # ]: 0 : if (!WalSummarizerCtl->initialized)
452 : : {
453 : : /*
454 : : * If initialized is false, the rest of the structure contents are
455 : : * undefined.
456 : : */
457 : 0 : *summarized_tli = 0;
458 : 0 : *summarized_lsn = InvalidXLogRecPtr;
459 : 0 : *pending_lsn = InvalidXLogRecPtr;
460 : 0 : *summarizer_pid = -1;
461 : : }
462 : : else
463 : : {
464 : 0 : int summarizer_pgprocno = WalSummarizerCtl->summarizer_pgprocno;
465 : :
466 : 0 : *summarized_tli = WalSummarizerCtl->summarized_tli;
467 : 0 : *summarized_lsn = WalSummarizerCtl->summarized_lsn;
742 heikki.linnakangas@i 468 [ # # ]: 0 : if (summarizer_pgprocno == INVALID_PROC_NUMBER)
469 : : {
470 : : /*
471 : : * If the summarizer has exited, the fact that it had processed
472 : : * beyond summarized_lsn is irrelevant now.
473 : : */
794 rhaas@postgresql.org 474 : 0 : *pending_lsn = WalSummarizerCtl->summarized_lsn;
475 : 0 : *summarizer_pid = -1;
476 : : }
477 : : else
478 : : {
479 : 0 : *pending_lsn = WalSummarizerCtl->pending_lsn;
480 : :
481 : : /*
482 : : * We're not fussed about inexact answers here, since they could
483 : : * become stale instantly, so we don't bother taking the lock, but
484 : : * make sure that invalid PID values are normalized to -1.
485 : : */
486 : 0 : *summarizer_pid = GetPGProcByNumber(summarizer_pgprocno)->pid;
487 [ # # ]: 0 : if (*summarizer_pid <= 0)
488 : 0 : *summarizer_pid = -1;
489 : : }
490 : : }
491 : 0 : LWLockRelease(WALSummarizerLock);
492 : 0 : }
493 : :
494 : : /*
495 : : * Get the oldest LSN in this server's timeline history that has not yet been
496 : : * summarized, and update shared memory state as appropriate.
497 : : *
498 : : * If *tli != NULL, it will be set to the TLI for the LSN that is returned.
499 : : *
500 : : * If *lsn_is_exact != NULL, it will be set to true if the returned LSN is
501 : : * necessarily the start of a WAL record and false if it's just the beginning
502 : : * of a WAL segment.
503 : : */
504 : : XLogRecPtr
628 rhaas@postgresql.org 505 :CBC 2401 : GetOldestUnsummarizedLSN(TimeLineID *tli, bool *lsn_is_exact)
506 : : {
507 : : TimeLineID latest_tli;
508 : : int n;
509 : : List *tles;
809 510 : 2401 : XLogRecPtr unsummarized_lsn = InvalidXLogRecPtr;
816 511 : 2401 : TimeLineID unsummarized_tli = 0;
512 : 2401 : bool should_make_exact = false;
513 : : List *existing_summaries;
514 : : ListCell *lc;
628 515 : 2401 : bool am_wal_summarizer = AmWalSummarizerProcess();
516 : :
517 : : /* If not summarizing WAL, do nothing. */
816 518 [ + + ]: 2401 : if (!summarize_wal)
519 : 2391 : return InvalidXLogRecPtr;
520 : :
521 : : /*
522 : : * If we are not the WAL summarizer process, then we normally just want to
523 : : * read the values from shared memory. However, as an exception, if shared
524 : : * memory hasn't been initialized yet, then we need to do that so that we
525 : : * can read legal values and not remove any WAL too early.
526 : : */
628 527 [ + + ]: 10 : if (!am_wal_summarizer)
528 : : {
529 : 7 : LWLockAcquire(WALSummarizerLock, LW_SHARED);
530 : :
816 531 [ + + ]: 7 : if (WalSummarizerCtl->initialized)
532 : : {
533 : 6 : unsummarized_lsn = WalSummarizerCtl->summarized_lsn;
534 [ - + ]: 6 : if (tli != NULL)
816 rhaas@postgresql.org 535 :UBC 0 : *tli = WalSummarizerCtl->summarized_tli;
816 rhaas@postgresql.org 536 [ - + ]:CBC 6 : if (lsn_is_exact != NULL)
816 rhaas@postgresql.org 537 :UBC 0 : *lsn_is_exact = WalSummarizerCtl->lsn_is_exact;
816 rhaas@postgresql.org 538 :CBC 6 : LWLockRelease(WALSummarizerLock);
539 : 6 : return unsummarized_lsn;
540 : : }
541 : :
542 : 1 : LWLockRelease(WALSummarizerLock);
543 : : }
544 : :
545 : : /*
546 : : * Find the oldest timeline on which WAL still exists, and the earliest
547 : : * segment for which it exists.
548 : : *
549 : : * Note that we do this every time the WAL summarizer process restarts or
550 : : * recovers from an error, in case the contents of pg_wal have changed
551 : : * under us e.g. if some files were removed, either manually - which
552 : : * shouldn't really happen, but might - or by postgres itself, if
553 : : * summarize_wal was turned off and then back on again.
554 : : */
555 : 4 : (void) GetLatestLSN(&latest_tli);
556 : 4 : tles = readTimeLineHistory(latest_tli);
557 [ + - ]: 4 : for (n = list_length(tles) - 1; n >= 0; --n)
558 : : {
559 : 4 : TimeLineHistoryEntry *tle = list_nth(tles, n);
560 : : XLogSegNo oldest_segno;
561 : :
562 : 4 : oldest_segno = XLogGetOldestSegno(tle->tli);
563 [ + - ]: 4 : if (oldest_segno != 0)
564 : : {
565 : : /* Compute oldest LSN that still exists on disk. */
566 : 4 : XLogSegNoOffsetToRecPtr(oldest_segno, 0, wal_segment_size,
567 : : unsummarized_lsn);
568 : :
569 : 4 : unsummarized_tli = tle->tli;
570 : 4 : break;
571 : : }
572 : : }
573 : :
574 : : /*
575 : : * Don't try to summarize anything older than the end LSN of the newest
576 : : * summary file that exists for this timeline.
577 : : */
578 : : existing_summaries =
579 : 4 : GetWalSummaries(unsummarized_tli,
580 : : InvalidXLogRecPtr, InvalidXLogRecPtr);
581 [ - + - - : 4 : foreach(lc, existing_summaries)
- + ]
582 : : {
816 rhaas@postgresql.org 583 :UBC 0 : WalSummaryFile *ws = lfirst(lc);
584 : :
585 [ # # ]: 0 : if (ws->end_lsn > unsummarized_lsn)
586 : : {
587 : 0 : unsummarized_lsn = ws->end_lsn;
588 : 0 : should_make_exact = true;
589 : : }
590 : : }
591 : :
592 : : /* It really should not be possible for us to find no WAL. */
628 rhaas@postgresql.org 593 [ - + ]:CBC 4 : if (unsummarized_tli == 0)
628 rhaas@postgresql.org 594 [ # # ]:UBC 0 : ereport(ERROR,
595 : : errcode(ERRCODE_INTERNAL_ERROR),
596 : : errmsg_internal("no WAL found on timeline %u", latest_tli));
597 : :
598 : : /*
599 : : * If we're the WAL summarizer, we always want to store the values we just
600 : : * computed into shared memory, because those are the values we're going
601 : : * to use to drive our operation, and so they are the authoritative
602 : : * values. Otherwise, we only store values into shared memory if shared
603 : : * memory is uninitialized. Our values are not canonical in such a case,
604 : : * but it's better to have something than nothing, to guide WAL retention.
605 : : */
628 rhaas@postgresql.org 606 :CBC 4 : LWLockAcquire(WALSummarizerLock, LW_EXCLUSIVE);
625 607 [ + + + - ]: 4 : if (am_wal_summarizer || !WalSummarizerCtl->initialized)
608 : : {
628 609 : 4 : WalSummarizerCtl->initialized = true;
610 : 4 : WalSummarizerCtl->summarized_lsn = unsummarized_lsn;
611 : 4 : WalSummarizerCtl->summarized_tli = unsummarized_tli;
612 : 4 : WalSummarizerCtl->lsn_is_exact = should_make_exact;
613 : 4 : WalSummarizerCtl->pending_lsn = unsummarized_lsn;
614 : : }
615 : : else
628 rhaas@postgresql.org 616 :UBC 0 : unsummarized_lsn = WalSummarizerCtl->summarized_lsn;
617 : :
618 : : /* Also return the to the caller as required. */
816 rhaas@postgresql.org 619 [ + + ]:CBC 4 : if (tli != NULL)
620 : 3 : *tli = WalSummarizerCtl->summarized_tli;
621 [ + + ]: 4 : if (lsn_is_exact != NULL)
622 : 3 : *lsn_is_exact = WalSummarizerCtl->lsn_is_exact;
623 : 4 : LWLockRelease(WALSummarizerLock);
624 : :
625 : 4 : return unsummarized_lsn;
626 : : }
627 : :
628 : : /*
629 : : * Wake up the WAL summarizer process.
630 : : *
631 : : * This might not work, because there's no guarantee that the WAL summarizer
632 : : * process was successfully started, and it also might have started but
633 : : * subsequently terminated. So, under normal circumstances, this will get the
634 : : * latch set, but there's no guarantee.
635 : : */
636 : : void
499 heikki.linnakangas@i 637 : 1598 : WakeupWalSummarizer(void)
638 : : {
639 : : ProcNumber pgprocno;
640 : :
816 rhaas@postgresql.org 641 [ - + ]: 1598 : if (WalSummarizerCtl == NULL)
816 rhaas@postgresql.org 642 :UBC 0 : return;
643 : :
199 msawada@postgresql.o 644 :CBC 1598 : LWLockAcquire(WALSummarizerLock, LW_SHARED);
816 rhaas@postgresql.org 645 : 1598 : pgprocno = WalSummarizerCtl->summarizer_pgprocno;
646 : 1598 : LWLockRelease(WALSummarizerLock);
647 : :
742 heikki.linnakangas@i 648 [ + + ]: 1598 : if (pgprocno != INVALID_PROC_NUMBER)
69 drowley@postgresql.o 649 :GNC 3 : SetLatch(&GetPGProcByNumber(pgprocno)->procLatch);
650 : : }
651 : :
652 : : /*
653 : : * Wait until WAL summarization reaches the given LSN, but time out with an
654 : : * error if the summarizer seems to be stick.
655 : : *
656 : : * Returns immediately if summarize_wal is turned off while we wait. Caller
657 : : * is expected to handle this case, if necessary.
658 : : */
659 : : void
597 rhaas@postgresql.org 660 :CBC 12 : WaitForWalSummarization(XLogRecPtr lsn)
661 : : {
662 : : TimestampTz initial_time,
663 : : cycle_time,
664 : : current_time;
665 : 12 : XLogRecPtr prior_pending_lsn = InvalidXLogRecPtr;
666 : 12 : int deadcycles = 0;
667 : :
668 : 12 : initial_time = cycle_time = GetCurrentTimestamp();
669 : :
670 : : while (1)
816 671 : 8 : {
597 672 : 20 : long timeout_in_ms = 10000;
673 : : XLogRecPtr summarized_lsn;
674 : : XLogRecPtr pending_lsn;
675 : :
676 [ - + ]: 20 : CHECK_FOR_INTERRUPTS();
677 : :
678 : : /* If WAL summarization is disabled while we're waiting, give up. */
679 [ - + ]: 20 : if (!summarize_wal)
597 rhaas@postgresql.org 680 :UBC 0 : return;
681 : :
682 : : /*
683 : : * If the LSN summarized on disk has reached the target value, stop.
684 : : */
199 msawada@postgresql.o 685 :CBC 20 : LWLockAcquire(WALSummarizerLock, LW_SHARED);
816 rhaas@postgresql.org 686 : 20 : summarized_lsn = WalSummarizerCtl->summarized_lsn;
597 687 : 20 : pending_lsn = WalSummarizerCtl->pending_lsn;
816 688 : 20 : LWLockRelease(WALSummarizerLock);
689 : :
690 : : /* If WAL summarization has progressed sufficiently, stop waiting. */
691 [ + + ]: 20 : if (summarized_lsn >= lsn)
692 : 12 : break;
693 : :
694 : : /* Recheck current time. */
597 695 : 8 : current_time = GetCurrentTimestamp();
696 : :
697 : : /* Have we finished the current cycle of waiting? */
698 [ - + ]: 8 : if (TimestampDifferenceMilliseconds(cycle_time,
699 : : current_time) >= timeout_in_ms)
700 : : {
701 : : long elapsed_seconds;
702 : :
703 : : /* Begin new wait cycle. */
597 rhaas@postgresql.org 704 :UBC 0 : cycle_time = TimestampTzPlusMilliseconds(cycle_time,
705 : : timeout_in_ms);
706 : :
707 : : /*
708 : : * Keep track of the number of cycles during which there has been
709 : : * no progression of pending_lsn. If pending_lsn is not advancing,
710 : : * that means that not only are no new files appearing on disk,
711 : : * but we're not even incorporating new records into the in-memory
712 : : * state.
713 : : */
714 [ # # ]: 0 : if (pending_lsn > prior_pending_lsn)
715 : : {
716 : 0 : prior_pending_lsn = pending_lsn;
717 : 0 : deadcycles = 0;
718 : : }
719 : : else
720 : 0 : ++deadcycles;
721 : :
722 : : /*
723 : : * If we've managed to wait for an entire minute without the WAL
724 : : * summarizer absorbing a single WAL record, error out; probably
725 : : * something is wrong.
726 : : *
727 : : * We could consider also erroring out if the summarizer is taking
728 : : * too long to catch up, but it's not clear what rate of progress
729 : : * would be acceptable and what would be too slow. So instead, we
730 : : * just try to error out in the case where there's no progress at
731 : : * all. That seems likely to catch a reasonable number of the
732 : : * things that can go wrong in practice (e.g. the summarizer
733 : : * process is completely hung, say because somebody hooked up a
734 : : * debugger to it or something) without giving up too quickly when
735 : : * the system is just slow.
736 : : */
737 [ # # ]: 0 : if (deadcycles >= 6)
738 [ # # ]: 0 : ereport(ERROR,
739 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
740 : : errmsg("WAL summarization is not progressing"),
741 : : errdetail("Summarization is needed through %X/%08X, but is stuck at %X/%08X on disk and %X/%08X in memory.",
742 : : LSN_FORMAT_ARGS(lsn),
743 : : LSN_FORMAT_ARGS(summarized_lsn),
744 : : LSN_FORMAT_ARGS(pending_lsn))));
745 : :
746 : :
747 : : /*
748 : : * Otherwise, just let the user know what's happening.
749 : : */
750 : 0 : elapsed_seconds =
751 : 0 : TimestampDifferenceMilliseconds(initial_time,
752 : : current_time) / 1000;
753 [ # # ]: 0 : ereport(WARNING,
754 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
755 : : errmsg_plural("still waiting for WAL summarization through %X/%08X after %ld second",
756 : : "still waiting for WAL summarization through %X/%08X after %ld seconds",
757 : : elapsed_seconds,
758 : : LSN_FORMAT_ARGS(lsn),
759 : : elapsed_seconds),
760 : : errdetail("Summarization has reached %X/%08X on disk and %X/%08X in memory.",
761 : : LSN_FORMAT_ARGS(summarized_lsn),
762 : : LSN_FORMAT_ARGS(pending_lsn))));
763 : : }
764 : :
765 : : /*
766 : : * Align the wait time to prevent drift. This doesn't really matter,
767 : : * but we'd like the warnings about how long we've been waiting to say
768 : : * 10 seconds, 20 seconds, 30 seconds, 40 seconds ... without ever
769 : : * drifting to something that is not a multiple of ten.
770 : : */
597 rhaas@postgresql.org 771 :CBC 8 : timeout_in_ms -=
772 : 8 : TimestampDifferenceMilliseconds(cycle_time, current_time);
773 : :
774 : : /* Wait and see. */
816 775 : 8 : ConditionVariableTimedSleep(&WalSummarizerCtl->summary_file_cv,
776 : : timeout_in_ms,
777 : : WAIT_EVENT_WAL_SUMMARY_READY);
778 : : }
779 : :
606 780 : 12 : ConditionVariableCancelSleep();
781 : : }
782 : :
783 : : /*
784 : : * On exit, update shared memory to make it clear that we're no longer
785 : : * running.
786 : : */
787 : : static void
794 788 : 3 : WalSummarizerShutdown(int code, Datum arg)
789 : : {
790 : 3 : LWLockAcquire(WALSummarizerLock, LW_EXCLUSIVE);
742 heikki.linnakangas@i 791 : 3 : WalSummarizerCtl->summarizer_pgprocno = INVALID_PROC_NUMBER;
794 rhaas@postgresql.org 792 : 3 : LWLockRelease(WALSummarizerLock);
793 : 3 : }
794 : :
795 : : /*
796 : : * Get the latest LSN that is eligible to be summarized, and set *tli to the
797 : : * corresponding timeline.
798 : : */
799 : : static XLogRecPtr
816 800 : 39 : GetLatestLSN(TimeLineID *tli)
801 : : {
802 [ + + ]: 39 : if (!RecoveryInProgress())
803 : : {
804 : : /* Don't summarize WAL before it's flushed. */
805 : 38 : return GetFlushRecPtr(tli);
806 : : }
807 : : else
808 : : {
809 : : XLogRecPtr flush_lsn;
810 : : TimeLineID flush_tli;
811 : : XLogRecPtr replay_lsn;
812 : : TimeLineID replay_tli;
813 : : TimeLineID insert_tli;
814 : :
815 : : /*
816 : : * After the insert TLI has been set and before the control file has
817 : : * been updated to show the DB in production, RecoveryInProgress()
818 : : * will return true, because it's not yet safe for all backends to
819 : : * begin writing WAL. However, replay has already ceased, so from our
820 : : * point of view, recovery is already over. We should summarize up to
821 : : * where replay stopped and then prepare to resume at the start of the
822 : : * insert timeline.
823 : : */
597 824 [ + - ]: 1 : if ((insert_tli = GetWALInsertionTimeLineIfSet()) != 0)
825 : : {
826 : 1 : *tli = insert_tli;
827 : 1 : return GetXLogReplayRecPtr(NULL);
828 : : }
829 : :
830 : : /*
831 : : * What we really want to know is how much WAL has been flushed to
832 : : * disk, but the only flush position available is the one provided by
833 : : * the walreceiver, which may not be running, because this could be
834 : : * crash recovery or recovery via restore_command. So use either the
835 : : * WAL receiver's flush position or the replay position, whichever is
836 : : * further ahead, on the theory that if the WAL has been replayed then
837 : : * it must also have been flushed to disk.
838 : : */
816 rhaas@postgresql.org 839 :UBC 0 : flush_lsn = GetWalRcvFlushRecPtr(NULL, &flush_tli);
840 : 0 : replay_lsn = GetXLogReplayRecPtr(&replay_tli);
841 [ # # ]: 0 : if (flush_lsn > replay_lsn)
842 : : {
843 : 0 : *tli = flush_tli;
844 : 0 : return flush_lsn;
845 : : }
846 : : else
847 : : {
848 : 0 : *tli = replay_tli;
849 : 0 : return replay_lsn;
850 : : }
851 : : }
852 : : }
853 : :
854 : : /*
855 : : * Interrupt handler for main loop of WAL summarizer process.
856 : : */
857 : : static void
375 heikki.linnakangas@i 858 :CBC 55132 : ProcessWalSummarizerInterrupts(void)
859 : : {
816 rhaas@postgresql.org 860 [ - + ]: 55132 : if (ProcSignalBarrierPending)
816 rhaas@postgresql.org 861 :UBC 0 : ProcessProcSignalBarrier();
862 : :
816 rhaas@postgresql.org 863 [ - + ]:CBC 55132 : if (ConfigReloadPending)
864 : : {
816 rhaas@postgresql.org 865 :UBC 0 : ConfigReloadPending = false;
866 : 0 : ProcessConfigFile(PGC_SIGHUP);
867 : : }
868 : :
816 rhaas@postgresql.org 869 [ + + - + ]:CBC 55132 : if (ShutdownRequestPending || !summarize_wal)
870 : : {
871 [ - + ]: 3 : ereport(DEBUG1,
872 : : errmsg_internal("WAL summarizer shutting down"));
873 : 3 : proc_exit(0);
874 : : }
875 : :
876 : : /* Perform logging of memory contexts of this process */
877 [ - + ]: 55129 : if (LogMemoryContextPending)
816 rhaas@postgresql.org 878 :UBC 0 : ProcessLogMemoryContextInterrupt();
816 rhaas@postgresql.org 879 :CBC 55129 : }
880 : :
881 : : /*
882 : : * Summarize a range of WAL records on a single timeline.
883 : : *
884 : : * 'tli' is the timeline to be summarized.
885 : : *
886 : : * 'start_lsn' is the point at which we should start summarizing. If this
887 : : * value comes from the end LSN of the previous record as returned by the
888 : : * xlogreader machinery, 'exact' should be true; otherwise, 'exact' should
889 : : * be false, and this function will search forward for the start of a valid
890 : : * WAL record.
891 : : *
892 : : * 'switch_lsn' is the point at which we should switch to a later timeline,
893 : : * if we're summarizing a historic timeline.
894 : : *
895 : : * 'maximum_lsn' identifies the point beyond which we can't count on being
896 : : * able to read any more WAL. It should be the switch point when reading a
897 : : * historic timeline, or the most-recently-measured end of WAL when reading
898 : : * the current timeline.
899 : : *
900 : : * The return value is the LSN at which the WAL summary actually ends. Most
901 : : * often, a summary file ends because we notice that a checkpoint has
902 : : * occurred and reach the redo pointer of that checkpoint, but sometimes
903 : : * we stop for other reasons, such as a timeline switch.
904 : : */
905 : : static XLogRecPtr
906 : 21 : SummarizeWAL(TimeLineID tli, XLogRecPtr start_lsn, bool exact,
907 : : XLogRecPtr switch_lsn, XLogRecPtr maximum_lsn)
908 : : {
909 : : SummarizerReadLocalXLogPrivate *private_data;
910 : : XLogReaderState *xlogreader;
911 : : XLogRecPtr summary_start_lsn;
912 : 21 : XLogRecPtr summary_end_lsn = switch_lsn;
913 : : char temp_path[MAXPGPATH];
914 : : char final_path[MAXPGPATH];
915 : : WalSummaryIO io;
916 : 21 : BlockRefTable *brtab = CreateEmptyBlockRefTable();
605 917 : 21 : bool fast_forward = true;
918 : :
919 : : /* Initialize private data for xlogreader. */
95 michael@paquier.xyz 920 :GNC 21 : private_data = palloc0_object(SummarizerReadLocalXLogPrivate);
816 rhaas@postgresql.org 921 :CBC 21 : private_data->tli = tli;
129 alvherre@kurilemu.de 922 :GNC 21 : private_data->historic = XLogRecPtrIsValid(switch_lsn);
816 rhaas@postgresql.org 923 :CBC 21 : private_data->read_upto = maximum_lsn;
924 : :
925 : : /* Create xlogreader. */
926 : 21 : xlogreader = XLogReaderAllocate(wal_segment_size, NULL,
927 : 21 : XL_ROUTINE(.page_read = &summarizer_read_local_xlog_page,
928 : : .segment_open = &wal_segment_open,
929 : : .segment_close = &wal_segment_close),
930 : : private_data);
931 [ - + ]: 21 : if (xlogreader == NULL)
816 rhaas@postgresql.org 932 [ # # ]:UBC 0 : ereport(ERROR,
933 : : (errcode(ERRCODE_OUT_OF_MEMORY),
934 : : errmsg("out of memory"),
935 : : errdetail("Failed while allocating a WAL reading processor.")));
936 : :
937 : : /*
938 : : * When exact = false, we're starting from an arbitrary point in the WAL
939 : : * and must search forward for the start of the next record.
940 : : *
941 : : * When exact = true, start_lsn should be either the LSN where a record
942 : : * begins, or the LSN of a page where the page header is immediately
943 : : * followed by the start of a new record. XLogBeginRead should tolerate
944 : : * either case.
945 : : *
946 : : * We need to allow for both cases because the behavior of xlogreader
947 : : * varies. When a record spans two or more xlog pages, the ending LSN
948 : : * reported by xlogreader will be the starting LSN of the following
949 : : * record, but when an xlog page boundary falls between two records, the
950 : : * end LSN for the first will be reported as the first byte of the
951 : : * following page. We can't know until we read that page how large the
952 : : * header will be, but we'll have to skip over it to find the next record.
953 : : */
816 rhaas@postgresql.org 954 [ + + ]:CBC 21 : if (exact)
955 : : {
956 : : /*
957 : : * Even if start_lsn is the beginning of a page rather than the
958 : : * beginning of the first record on that page, we should still use it
959 : : * as the start LSN for the summary file. That's because we detect
960 : : * missing summary files by looking for cases where the end LSN of one
961 : : * file is less than the start LSN of the next file. When only a page
962 : : * header is skipped, nothing has been missed.
963 : : */
964 : 18 : XLogBeginRead(xlogreader, start_lsn);
965 : 18 : summary_start_lsn = start_lsn;
966 : : }
967 : : else
968 : : {
969 : 3 : summary_start_lsn = XLogFindNextRecord(xlogreader, start_lsn);
129 alvherre@kurilemu.de 970 [ - + ]:GNC 3 : if (!XLogRecPtrIsValid(summary_start_lsn))
971 : : {
972 : : /*
973 : : * If we hit end-of-WAL while trying to find the next valid
974 : : * record, we must be on a historic timeline that has no valid
975 : : * records that begin after start_lsn and before end of WAL.
976 : : */
816 rhaas@postgresql.org 977 [ # # ]:UBC 0 : if (private_data->end_of_wal)
978 : : {
979 [ # # ]: 0 : ereport(DEBUG1,
980 : : errmsg_internal("could not read WAL from timeline %u at %X/%08X: end of WAL at %X/%08X",
981 : : tli,
982 : : LSN_FORMAT_ARGS(start_lsn),
983 : : LSN_FORMAT_ARGS(private_data->read_upto)));
984 : :
985 : : /*
986 : : * The timeline ends at or after start_lsn, without containing
987 : : * any records. Thus, we must make sure the main loop does not
988 : : * iterate. If start_lsn is the end of the timeline, then we
989 : : * won't actually emit an empty summary file, but otherwise,
990 : : * we must, to capture the fact that the LSN range in question
991 : : * contains no interesting WAL records.
992 : : */
993 : 0 : summary_start_lsn = start_lsn;
994 : 0 : summary_end_lsn = private_data->read_upto;
995 : 0 : switch_lsn = xlogreader->EndRecPtr;
996 : : }
997 : : else
998 [ # # ]: 0 : ereport(ERROR,
999 : : errmsg("could not find a valid record after %X/%08X",
1000 : : LSN_FORMAT_ARGS(start_lsn)));
1001 : : }
1002 : :
1003 : : /* We shouldn't go backward. */
816 rhaas@postgresql.org 1004 [ + - ]:CBC 3 : Assert(summary_start_lsn >= start_lsn);
1005 : : }
1006 : :
1007 : : /*
1008 : : * Main loop: read xlog records one by one.
1009 : : */
1010 : : while (1)
1011 : 53028 : {
1012 : : int block_id;
1013 : : char *errormsg;
1014 : : XLogRecord *record;
1015 : : uint8 rmid;
1016 : :
375 heikki.linnakangas@i 1017 : 53049 : ProcessWalSummarizerInterrupts();
1018 : :
1019 : : /* We shouldn't go backward. */
816 rhaas@postgresql.org 1020 [ - + ]: 53049 : Assert(summary_start_lsn <= xlogreader->EndRecPtr);
1021 : :
1022 : : /* Now read the next record. */
1023 : 53049 : record = XLogReadRecord(xlogreader, &errormsg);
1024 [ - + ]: 53046 : if (record == NULL)
1025 : : {
816 rhaas@postgresql.org 1026 [ # # ]:UBC 0 : if (private_data->end_of_wal)
1027 : : {
1028 : : /*
1029 : : * This timeline must be historic and must end before we were
1030 : : * able to read a complete record.
1031 : : */
1032 [ # # ]: 0 : ereport(DEBUG1,
1033 : : errmsg_internal("could not read WAL from timeline %u at %X/%08X: end of WAL at %X/%08X",
1034 : : tli,
1035 : : LSN_FORMAT_ARGS(xlogreader->EndRecPtr),
1036 : : LSN_FORMAT_ARGS(private_data->read_upto)));
1037 : : /* Summary ends at end of WAL. */
1038 : 0 : summary_end_lsn = private_data->read_upto;
1039 : 0 : break;
1040 : : }
1041 [ # # ]: 0 : if (errormsg)
1042 [ # # ]: 0 : ereport(ERROR,
1043 : : (errcode_for_file_access(),
1044 : : errmsg("could not read WAL from timeline %u at %X/%08X: %s",
1045 : : tli, LSN_FORMAT_ARGS(xlogreader->EndRecPtr),
1046 : : errormsg)));
1047 : : else
1048 [ # # ]: 0 : ereport(ERROR,
1049 : : (errcode_for_file_access(),
1050 : : errmsg("could not read WAL from timeline %u at %X/%08X",
1051 : : tli, LSN_FORMAT_ARGS(xlogreader->EndRecPtr))));
1052 : : }
1053 : :
1054 : : /* We shouldn't go backward. */
816 rhaas@postgresql.org 1055 [ - + ]:CBC 53046 : Assert(summary_start_lsn <= xlogreader->EndRecPtr);
1056 : :
129 alvherre@kurilemu.de 1057 [ - + ]:GNC 53046 : if (XLogRecPtrIsValid(switch_lsn) &&
816 rhaas@postgresql.org 1058 [ # # ]:UBC 0 : xlogreader->ReadRecPtr >= switch_lsn)
1059 : : {
1060 : : /*
1061 : : * Whoops! We've read a record that *starts* after the switch LSN,
1062 : : * contrary to our goal of reading only until we hit the first
1063 : : * record that ends at or after the switch LSN. Pretend we didn't
1064 : : * read it after all by bailing out of this loop right here,
1065 : : * before we do anything with this record.
1066 : : *
1067 : : * This can happen because the last record before the switch LSN
1068 : : * might be continued across multiple pages, and then we might
1069 : : * come to a page with XLP_FIRST_IS_OVERWRITE_CONTRECORD set. In
1070 : : * that case, the record that was continued across multiple pages
1071 : : * is incomplete and will be disregarded, and the read will
1072 : : * restart from the beginning of the page that is flagged
1073 : : * XLP_FIRST_IS_OVERWRITE_CONTRECORD.
1074 : : *
1075 : : * If this case occurs, we can fairly say that the current summary
1076 : : * file ends at the switch LSN exactly. The first record on the
1077 : : * page marked XLP_FIRST_IS_OVERWRITE_CONTRECORD will be
1078 : : * discovered when generating the next summary file.
1079 : : */
1080 : 0 : summary_end_lsn = switch_lsn;
1081 : 0 : break;
1082 : : }
1083 : :
1084 : : /*
1085 : : * Certain types of records require special handling. Redo points and
1086 : : * shutdown checkpoints trigger creation of new summary files and can
1087 : : * also cause us to enter or exit "fast forward" mode. Other types of
1088 : : * records can require special updates to the block reference table.
1089 : : */
605 rhaas@postgresql.org 1090 :CBC 53046 : rmid = XLogRecGetRmid(xlogreader);
1091 [ + + ]: 53046 : if (rmid == RM_XLOG_ID)
1092 : : {
1093 : : bool new_fast_forward;
1094 : :
1095 : : /*
1096 : : * If we've already processed some WAL records when we hit a redo
1097 : : * point or shutdown checkpoint, then we stop summarization before
1098 : : * including this record in the current file, so that it will be
1099 : : * the first record in the next file.
1100 : : *
1101 : : * When we hit one of those record types as the first record in a
1102 : : * file, we adjust our notion of whether we're fast-forwarding.
1103 : : * Any WAL generated with wal_level=minimal must be skipped
1104 : : * without actually generating any summary file, because an
1105 : : * incremental backup that crosses such WAL would be unsafe.
1106 : : */
1107 [ + + ]: 695 : if (SummarizeXlogRecord(xlogreader, &new_fast_forward))
1108 : : {
1109 [ + + ]: 39 : if (xlogreader->ReadRecPtr > summary_start_lsn)
1110 : : {
1111 : 18 : summary_end_lsn = xlogreader->ReadRecPtr;
1112 : 18 : break;
1113 : : }
1114 : : else
1115 : 21 : fast_forward = new_fast_forward;
1116 : : }
1117 : : }
1118 [ + - ]: 52351 : else if (!fast_forward)
1119 : : {
1120 : : /*
1121 : : * This switch handles record types that require extra updates to
1122 : : * the contents of the block reference table.
1123 : : */
1124 [ + + + + ]: 52351 : switch (rmid)
1125 : : {
1126 : 4 : case RM_DBASE_ID:
1127 : 4 : SummarizeDbaseRecord(xlogreader, brtab);
1128 : 4 : break;
1129 : 29 : case RM_SMGR_ID:
1130 : 29 : SummarizeSmgrRecord(xlogreader, brtab);
1131 : 29 : break;
1132 : 1322 : case RM_XACT_ID:
1133 : 1322 : SummarizeXactRecord(xlogreader, brtab);
1134 : 1322 : break;
1135 : : }
1136 : : }
1137 : :
1138 : : /*
1139 : : * If we're in fast-forward mode, we don't really need to do anything.
1140 : : * Otherwise, feed block references from xlog record to block
1141 : : * reference table.
1142 : : */
1143 [ + - ]: 53028 : if (!fast_forward)
1144 : : {
1145 [ + + ]: 105792 : for (block_id = 0; block_id <= XLogRecMaxBlockId(xlogreader);
1146 : 52764 : block_id++)
1147 : : {
1148 : : RelFileLocator rlocator;
1149 : : ForkNumber forknum;
1150 : : BlockNumber blocknum;
1151 : :
1152 [ + + ]: 52764 : if (!XLogRecGetBlockTagExtended(xlogreader, block_id, &rlocator,
1153 : : &forknum, &blocknum, NULL))
1154 : 32 : continue;
1155 : :
1156 : : /*
1157 : : * As we do elsewhere, ignore the FSM fork, because it's not
1158 : : * fully WAL-logged.
1159 : : */
1160 [ + + ]: 52732 : if (forknum != FSM_FORKNUM)
1161 : 52442 : BlockRefTableMarkBlockModified(brtab, &rlocator, forknum,
1162 : : blocknum);
1163 : : }
1164 : : }
1165 : :
1166 : : /* Update our notion of where this summary file ends. */
816 1167 : 53028 : summary_end_lsn = xlogreader->EndRecPtr;
1168 : :
1169 : : /* Also update shared memory. */
1170 : 53028 : LWLockAcquire(WALSummarizerLock, LW_EXCLUSIVE);
1171 [ - + ]: 53028 : Assert(summary_end_lsn >= WalSummarizerCtl->summarized_lsn);
1172 : 53028 : WalSummarizerCtl->pending_lsn = summary_end_lsn;
1173 : 53028 : LWLockRelease(WALSummarizerLock);
1174 : :
1175 : : /*
1176 : : * If we have a switch LSN and have reached it, stop before reading
1177 : : * the next record.
1178 : : */
129 alvherre@kurilemu.de 1179 [ - + ]:GNC 53028 : if (XLogRecPtrIsValid(switch_lsn) &&
816 rhaas@postgresql.org 1180 [ # # ]:UBC 0 : xlogreader->EndRecPtr >= switch_lsn)
1181 : 0 : break;
1182 : : }
1183 : :
1184 : : /* Destroy xlogreader. */
816 rhaas@postgresql.org 1185 :CBC 18 : pfree(xlogreader->private_data);
1186 : 18 : XLogReaderFree(xlogreader);
1187 : :
1188 : : /*
1189 : : * If a timeline switch occurs, we may fail to make any progress at all
1190 : : * before exiting the loop above. If that happens, we don't write a WAL
1191 : : * summary file at all. We can also skip writing a file if we're in
1192 : : * fast-forward mode.
1193 : : */
605 1194 [ + - + - ]: 18 : if (summary_end_lsn > summary_start_lsn && !fast_forward)
1195 : : {
1196 : : /* Generate temporary and final path name. */
816 1197 : 18 : snprintf(temp_path, MAXPGPATH,
1198 : : XLOGDIR "/summaries/temp.summary");
1199 : 18 : snprintf(final_path, MAXPGPATH,
1200 : : XLOGDIR "/summaries/%08X%08X%08X%08X%08X.summary",
1201 : : tli,
1202 : 18 : LSN_FORMAT_ARGS(summary_start_lsn),
1203 : 18 : LSN_FORMAT_ARGS(summary_end_lsn));
1204 : :
1205 : : /* Open the temporary file for writing. */
1206 : 18 : io.filepos = 0;
1207 : 18 : io.file = PathNameOpenFile(temp_path, O_WRONLY | O_CREAT | O_TRUNC);
1208 [ - + ]: 18 : if (io.file < 0)
816 rhaas@postgresql.org 1209 [ # # ]:UBC 0 : ereport(ERROR,
1210 : : (errcode_for_file_access(),
1211 : : errmsg("could not create file \"%s\": %m", temp_path)));
1212 : :
1213 : : /* Write the data. */
816 rhaas@postgresql.org 1214 :CBC 18 : WriteBlockRefTable(brtab, WriteWalSummary, &io);
1215 : :
1216 : : /* Close temporary file and shut down xlogreader. */
1217 : 18 : FileClose(io.file);
1218 : :
1219 : : /* Tell the user what we did. */
1220 [ - + ]: 18 : ereport(DEBUG1,
1221 : : errmsg_internal("summarized WAL on TLI %u from %X/%08X to %X/%08X",
1222 : : tli,
1223 : : LSN_FORMAT_ARGS(summary_start_lsn),
1224 : : LSN_FORMAT_ARGS(summary_end_lsn)));
1225 : :
1226 : : /* Durably rename the new summary into place. */
1227 : 18 : durable_rename(temp_path, final_path, ERROR);
1228 : : }
1229 : :
1230 : : /* If we skipped a non-zero amount of WAL, log a debug message. */
605 1231 [ + - - + ]: 18 : if (summary_end_lsn > summary_start_lsn && fast_forward)
605 rhaas@postgresql.org 1232 [ # # ]:UBC 0 : ereport(DEBUG1,
1233 : : errmsg_internal("skipped summarizing WAL on TLI %u from %X/%08X to %X/%08X",
1234 : : tli,
1235 : : LSN_FORMAT_ARGS(summary_start_lsn),
1236 : : LSN_FORMAT_ARGS(summary_end_lsn)));
1237 : :
816 rhaas@postgresql.org 1238 :CBC 18 : return summary_end_lsn;
1239 : : }
1240 : :
1241 : : /*
1242 : : * Special handling for WAL records with RM_DBASE_ID.
1243 : : */
1244 : : static void
741 1245 : 4 : SummarizeDbaseRecord(XLogReaderState *xlogreader, BlockRefTable *brtab)
1246 : : {
1247 : 4 : uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
1248 : :
1249 : : /*
1250 : : * We use relfilenode zero for a given database OID and tablespace OID to
1251 : : * indicate that all relations with that pair of IDs have been recreated
1252 : : * if they exist at all. Effectively, we're setting a limit block of 0 for
1253 : : * all such relfilenodes.
1254 : : *
1255 : : * Technically, this special handling is only needed in the case of
1256 : : * XLOG_DBASE_CREATE_FILE_COPY, because that can create a whole bunch of
1257 : : * relation files in a directory without logging anything specific to each
1258 : : * one. If we didn't mark the whole DB OID/TS OID combination in some way,
1259 : : * then a tablespace that was dropped after the reference backup and
1260 : : * recreated using the FILE_COPY method prior to the incremental backup
1261 : : * would look just like one that was never touched at all, which would be
1262 : : * catastrophic.
1263 : : *
1264 : : * But it seems best to adopt this treatment for all records that drop or
1265 : : * create a DB OID/TS OID combination. That's similar to how we treat the
1266 : : * limit block for individual relations, and it's an extra layer of safety
1267 : : * here. We can never lose data by marking more stuff as needing to be
1268 : : * backed up in full.
1269 : : */
1270 [ + - ]: 4 : if (info == XLOG_DBASE_CREATE_FILE_COPY)
1271 : : {
1272 : : xl_dbase_create_file_copy_rec *xlrec;
1273 : : RelFileLocator rlocator;
1274 : :
1275 : 4 : xlrec =
1276 : 4 : (xl_dbase_create_file_copy_rec *) XLogRecGetData(xlogreader);
1277 : 4 : rlocator.spcOid = xlrec->tablespace_id;
1278 : 4 : rlocator.dbOid = xlrec->db_id;
1279 : 4 : rlocator.relNumber = 0;
1280 : 4 : BlockRefTableSetLimitBlock(brtab, &rlocator, MAIN_FORKNUM, 0);
1281 : : }
741 rhaas@postgresql.org 1282 [ # # ]:UBC 0 : else if (info == XLOG_DBASE_CREATE_WAL_LOG)
1283 : : {
1284 : : xl_dbase_create_wal_log_rec *xlrec;
1285 : : RelFileLocator rlocator;
1286 : :
1287 : 0 : xlrec = (xl_dbase_create_wal_log_rec *) XLogRecGetData(xlogreader);
1288 : 0 : rlocator.spcOid = xlrec->tablespace_id;
1289 : 0 : rlocator.dbOid = xlrec->db_id;
1290 : 0 : rlocator.relNumber = 0;
1291 : 0 : BlockRefTableSetLimitBlock(brtab, &rlocator, MAIN_FORKNUM, 0);
1292 : : }
1293 [ # # ]: 0 : else if (info == XLOG_DBASE_DROP)
1294 : : {
1295 : : xl_dbase_drop_rec *xlrec;
1296 : : RelFileLocator rlocator;
1297 : : int i;
1298 : :
1299 : 0 : xlrec = (xl_dbase_drop_rec *) XLogRecGetData(xlogreader);
1300 : 0 : rlocator.dbOid = xlrec->db_id;
1301 : 0 : rlocator.relNumber = 0;
1302 [ # # ]: 0 : for (i = 0; i < xlrec->ntablespaces; ++i)
1303 : : {
1304 : 0 : rlocator.spcOid = xlrec->tablespace_ids[i];
1305 : 0 : BlockRefTableSetLimitBlock(brtab, &rlocator, MAIN_FORKNUM, 0);
1306 : : }
1307 : : }
741 rhaas@postgresql.org 1308 :CBC 4 : }
1309 : :
1310 : : /*
1311 : : * Special handling for WAL records with RM_SMGR_ID.
1312 : : */
1313 : : static void
816 1314 : 29 : SummarizeSmgrRecord(XLogReaderState *xlogreader, BlockRefTable *brtab)
1315 : : {
1316 : 29 : uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
1317 : :
1318 [ + + ]: 29 : if (info == XLOG_SMGR_CREATE)
1319 : : {
1320 : : xl_smgr_create *xlrec;
1321 : :
1322 : : /*
1323 : : * If a new relation fork is created on disk, there is no point
1324 : : * tracking anything about which blocks have been modified, because
1325 : : * the whole thing will be new. Hence, set the limit block for this
1326 : : * fork to 0.
1327 : : *
1328 : : * Ignore the FSM fork, which is not fully WAL-logged.
1329 : : */
1330 : 28 : xlrec = (xl_smgr_create *) XLogRecGetData(xlogreader);
1331 : :
1332 [ + - ]: 28 : if (xlrec->forkNum != FSM_FORKNUM)
1333 : 28 : BlockRefTableSetLimitBlock(brtab, &xlrec->rlocator,
1334 : : xlrec->forkNum, 0);
1335 : : }
1336 [ + - ]: 1 : else if (info == XLOG_SMGR_TRUNCATE)
1337 : : {
1338 : : xl_smgr_truncate *xlrec;
1339 : :
1340 : 1 : xlrec = (xl_smgr_truncate *) XLogRecGetData(xlogreader);
1341 : :
1342 : : /*
1343 : : * If a relation fork is truncated on disk, there is no point in
1344 : : * tracking anything about block modifications beyond the truncation
1345 : : * point.
1346 : : *
1347 : : * We ignore SMGR_TRUNCATE_FSM here because the FSM isn't fully
1348 : : * WAL-logged and thus we can't track modified blocks for it anyway.
1349 : : */
1350 [ + - ]: 1 : if ((xlrec->flags & SMGR_TRUNCATE_HEAP) != 0)
1351 : 1 : BlockRefTableSetLimitBlock(brtab, &xlrec->rlocator,
1352 : : MAIN_FORKNUM, xlrec->blkno);
1353 [ + - ]: 1 : if ((xlrec->flags & SMGR_TRUNCATE_VM) != 0)
1354 : 1 : BlockRefTableSetLimitBlock(brtab, &xlrec->rlocator,
1355 : : VISIBILITYMAP_FORKNUM,
1356 : : visibilitymap_truncation_length(xlrec->blkno));
1357 : : }
1358 : 29 : }
1359 : :
1360 : : /*
1361 : : * Special handling for WAL records with RM_XACT_ID.
1362 : : */
1363 : : static void
1364 : 1322 : SummarizeXactRecord(XLogReaderState *xlogreader, BlockRefTable *brtab)
1365 : : {
1366 : 1322 : uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
1367 : 1322 : uint8 xact_info = info & XLOG_XACT_OPMASK;
1368 : :
1369 [ - + - - ]: 1322 : if (xact_info == XLOG_XACT_COMMIT ||
1370 : : xact_info == XLOG_XACT_COMMIT_PREPARED)
1371 : 1322 : {
1372 : 1322 : xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(xlogreader);
1373 : : xl_xact_parsed_commit parsed;
1374 : : int i;
1375 : :
1376 : : /*
1377 : : * Don't track modified blocks for any relations that were removed on
1378 : : * commit.
1379 : : */
1380 : 1322 : ParseCommitRecord(XLogRecGetInfo(xlogreader), xlrec, &parsed);
1381 [ - + ]: 1322 : for (i = 0; i < parsed.nrels; ++i)
1382 : : {
1383 : : ForkNumber forknum;
1384 : :
816 rhaas@postgresql.org 1385 [ # # ]:UBC 0 : for (forknum = 0; forknum <= MAX_FORKNUM; ++forknum)
1386 [ # # ]: 0 : if (forknum != FSM_FORKNUM)
1387 : 0 : BlockRefTableSetLimitBlock(brtab, &parsed.xlocators[i],
1388 : : forknum, 0);
1389 : : }
1390 : : }
1391 [ # # # # ]: 0 : else if (xact_info == XLOG_XACT_ABORT ||
1392 : : xact_info == XLOG_XACT_ABORT_PREPARED)
1393 : : {
1394 : 0 : xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(xlogreader);
1395 : : xl_xact_parsed_abort parsed;
1396 : : int i;
1397 : :
1398 : : /*
1399 : : * Don't track modified blocks for any relations that were removed on
1400 : : * abort.
1401 : : */
1402 : 0 : ParseAbortRecord(XLogRecGetInfo(xlogreader), xlrec, &parsed);
1403 [ # # ]: 0 : for (i = 0; i < parsed.nrels; ++i)
1404 : : {
1405 : : ForkNumber forknum;
1406 : :
1407 [ # # ]: 0 : for (forknum = 0; forknum <= MAX_FORKNUM; ++forknum)
1408 [ # # ]: 0 : if (forknum != FSM_FORKNUM)
1409 : 0 : BlockRefTableSetLimitBlock(brtab, &parsed.xlocators[i],
1410 : : forknum, 0);
1411 : : }
1412 : : }
816 rhaas@postgresql.org 1413 :CBC 1322 : }
1414 : :
1415 : : /*
1416 : : * Special handling for WAL records with RM_XLOG_ID.
1417 : : *
1418 : : * The return value is true if WAL summarization should stop before this
1419 : : * record and false otherwise. When the return value is true,
1420 : : * *new_fast_forward indicates whether future processing should be done
1421 : : * in fast forward mode (i.e. read WAL without emitting summaries) or not.
1422 : : */
1423 : : static bool
605 1424 : 695 : SummarizeXlogRecord(XLogReaderState *xlogreader, bool *new_fast_forward)
1425 : : {
816 1426 : 695 : uint8 info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
1427 : : int record_wal_level;
1428 : :
605 1429 [ + + ]: 695 : if (info == XLOG_CHECKPOINT_REDO)
1430 : : {
1431 : : /* Payload is wal_level at the time record was written. */
1432 : 23 : memcpy(&record_wal_level, XLogRecGetData(xlogreader), sizeof(int));
1433 : : }
1434 [ + + ]: 672 : else if (info == XLOG_CHECKPOINT_SHUTDOWN)
1435 : : {
1436 : : CheckPoint rec_ckpt;
1437 : :
1438 : : /* Extract wal_level at time record was written from payload. */
1439 : 12 : memcpy(&rec_ckpt, XLogRecGetData(xlogreader), sizeof(CheckPoint));
1440 : 12 : record_wal_level = rec_ckpt.wal_level;
1441 : : }
1442 [ + + ]: 660 : else if (info == XLOG_PARAMETER_CHANGE)
1443 : : {
1444 : : xl_parameter_change xlrec;
1445 : :
1446 : : /* Extract wal_level at time record was written from payload. */
1447 : 4 : memcpy(&xlrec, XLogRecGetData(xlogreader),
1448 : : sizeof(xl_parameter_change));
1449 : 4 : record_wal_level = xlrec.wal_level;
1450 : : }
1451 [ - + ]: 656 : else if (info == XLOG_END_OF_RECOVERY)
1452 : : {
1453 : : xl_end_of_recovery xlrec;
1454 : :
1455 : : /* Extract wal_level at time record was written from payload. */
605 rhaas@postgresql.org 1456 :UBC 0 : memcpy(&xlrec, XLogRecGetData(xlogreader), sizeof(xl_end_of_recovery));
1457 : 0 : record_wal_level = xlrec.wal_level;
1458 : : }
1459 : : else
1460 : : {
1461 : : /* No special handling required. Return false. */
605 rhaas@postgresql.org 1462 :CBC 656 : return false;
1463 : : }
1464 : :
1465 : : /*
1466 : : * Redo can only begin at an XLOG_CHECKPOINT_REDO or
1467 : : * XLOG_CHECKPOINT_SHUTDOWN record, so we want WAL summarization to begin
1468 : : * at those points. Hence, when those records are encountered, return
1469 : : * true, so that we stop just before summarizing either of those records.
1470 : : *
1471 : : * We also reach here if we just saw XLOG_END_OF_RECOVERY or
1472 : : * XLOG_PARAMETER_CHANGE. These are not places where recovery can start,
1473 : : * but they're still relevant here. A new timeline can begin with
1474 : : * XLOG_END_OF_RECOVERY, so we need to confirm the WAL level at that
1475 : : * point; and a restart can provoke XLOG_PARAMETER_CHANGE after an
1476 : : * intervening change to postgresql.conf, which might force us to stop
1477 : : * summarizing.
1478 : : */
1479 : 39 : *new_fast_forward = (record_wal_level == WAL_LEVEL_MINIMAL);
1480 : 39 : return true;
1481 : : }
1482 : :
1483 : : /*
1484 : : * Similar to read_local_xlog_page, but limited to read from one particular
1485 : : * timeline. If the end of WAL is reached, it will wait for more if reading
1486 : : * from the current timeline, or give up if reading from a historic timeline.
1487 : : * In the latter case, it will also set private_data->end_of_wal = true.
1488 : : *
1489 : : * Caller must set private_data->tli to the TLI of interest,
1490 : : * private_data->read_upto to the lowest LSN that is not known to be safe
1491 : : * to read on that timeline, and private_data->historic to true if and only
1492 : : * if the timeline is not the current timeline. This function will update
1493 : : * private_data->read_upto and private_data->historic if more WAL appears
1494 : : * on the current timeline or if the current timeline becomes historic.
1495 : : */
1496 : : static int
816 1497 : 2017 : summarizer_read_local_xlog_page(XLogReaderState *state,
1498 : : XLogRecPtr targetPagePtr, int reqLen,
1499 : : XLogRecPtr targetRecPtr, char *cur_page)
1500 : : {
1501 : : int count;
1502 : : WALReadError errinfo;
1503 : : SummarizerReadLocalXLogPrivate *private_data;
1504 : :
375 heikki.linnakangas@i 1505 : 2017 : ProcessWalSummarizerInterrupts();
1506 : :
816 rhaas@postgresql.org 1507 : 2017 : private_data = (SummarizerReadLocalXLogPrivate *)
1508 : : state->private_data;
1509 : :
1510 : : while (1)
1511 : : {
1512 [ + + ]: 2031 : if (targetPagePtr + XLOG_BLCKSZ <= private_data->read_upto)
1513 : : {
1514 : : /*
1515 : : * more than one block available; read only that block, have
1516 : : * caller come back if they need more.
1517 : : */
1518 : 1997 : count = XLOG_BLCKSZ;
1519 : 1997 : break;
1520 : : }
1521 [ + + ]: 34 : else if (targetPagePtr + reqLen > private_data->read_upto)
1522 : : {
1523 : : /* We don't seem to have enough data. */
1524 [ - + ]: 17 : if (private_data->historic)
1525 : : {
1526 : : /*
1527 : : * This is a historic timeline, so there will never be any
1528 : : * more data than we have currently.
1529 : : */
816 rhaas@postgresql.org 1530 :UBC 0 : private_data->end_of_wal = true;
1531 : 0 : return -1;
1532 : : }
1533 : : else
1534 : : {
1535 : : XLogRecPtr latest_lsn;
1536 : : TimeLineID latest_tli;
1537 : :
1538 : : /*
1539 : : * This is - or at least was up until very recently - the
1540 : : * current timeline, so more data might show up. Delay here
1541 : : * so we don't tight-loop.
1542 : : */
375 heikki.linnakangas@i 1543 :CBC 17 : ProcessWalSummarizerInterrupts();
816 rhaas@postgresql.org 1544 : 14 : summarizer_wait_for_wal();
1545 : :
1546 : : /* Recheck end-of-WAL. */
1547 : 14 : latest_lsn = GetLatestLSN(&latest_tli);
1548 [ + - ]: 14 : if (private_data->tli == latest_tli)
1549 : : {
1550 : : /* Still the current timeline, update max LSN. */
1551 [ - + ]: 14 : Assert(latest_lsn >= private_data->read_upto);
1552 : 14 : private_data->read_upto = latest_lsn;
1553 : : }
1554 : : else
1555 : : {
816 rhaas@postgresql.org 1556 :UBC 0 : List *tles = readTimeLineHistory(latest_tli);
1557 : : XLogRecPtr switchpoint;
1558 : :
1559 : : /*
1560 : : * The timeline we're scanning is no longer the latest
1561 : : * one. Figure out when it ended.
1562 : : */
1563 : 0 : private_data->historic = true;
1564 : 0 : switchpoint = tliSwitchPoint(private_data->tli, tles,
1565 : : NULL);
1566 : :
1567 : : /*
1568 : : * Allow reads up to exactly the switch point.
1569 : : *
1570 : : * It's possible that this will cause read_upto to move
1571 : : * backwards, because we might have been promoted before
1572 : : * reaching the end of the previous timeline. In that
1573 : : * case, the next loop iteration will likely conclude that
1574 : : * we've reached end of WAL.
1575 : : */
1576 : 0 : private_data->read_upto = switchpoint;
1577 : :
1578 : : /* Debugging output. */
1579 [ # # ]: 0 : ereport(DEBUG1,
1580 : : errmsg_internal("timeline %u became historic, can read up to %X/%08X",
1581 : : private_data->tli, LSN_FORMAT_ARGS(private_data->read_upto)));
1582 : : }
1583 : :
1584 : : /* Go around and try again. */
1585 : : }
1586 : : }
1587 : : else
1588 : : {
1589 : : /* enough bytes available to satisfy the request */
816 rhaas@postgresql.org 1590 :CBC 17 : count = private_data->read_upto - targetPagePtr;
1591 : 17 : break;
1592 : : }
1593 : : }
1594 : :
758 jdavis@postgresql.or 1595 [ - + ]: 2014 : if (!WALRead(state, cur_page, targetPagePtr, count,
1596 : : private_data->tli, &errinfo))
816 rhaas@postgresql.org 1597 :UBC 0 : WALReadRaiseError(&errinfo);
1598 : :
1599 : : /* Track that we read a page, for sleep time calculation. */
816 rhaas@postgresql.org 1600 :CBC 2014 : ++pages_read_since_last_sleep;
1601 : :
1602 : : /* number of valid bytes in the buffer */
1603 : 2014 : return count;
1604 : : }
1605 : :
1606 : : /*
1607 : : * Sleep for long enough that we believe it's likely that more WAL will
1608 : : * be available afterwards.
1609 : : */
1610 : : static void
1611 : 14 : summarizer_wait_for_wal(void)
1612 : : {
1613 [ + + ]: 14 : if (pages_read_since_last_sleep == 0)
1614 : : {
1615 : : /*
1616 : : * No pages were read since the last sleep, so double the sleep time,
1617 : : * but not beyond the maximum allowable value.
1618 : : */
1619 : 5 : sleep_quanta = Min(sleep_quanta * 2, MAX_SLEEP_QUANTA);
1620 : : }
1621 [ + + ]: 9 : else if (pages_read_since_last_sleep > 1)
1622 : : {
1623 : : /*
1624 : : * Multiple pages were read since the last sleep, so reduce the sleep
1625 : : * time.
1626 : : *
1627 : : * A large burst of activity should be able to quickly reduce the
1628 : : * sleep time to the minimum, but we don't want a handful of extra WAL
1629 : : * records to provoke a strong reaction. We choose to reduce the sleep
1630 : : * time by 1 quantum for each page read beyond the first, which is a
1631 : : * fairly arbitrary way of trying to be reactive without overreacting.
1632 : : */
1633 [ + - ]: 6 : if (pages_read_since_last_sleep > sleep_quanta - 1)
1634 : 6 : sleep_quanta = 1;
1635 : : else
816 rhaas@postgresql.org 1636 :UBC 0 : sleep_quanta -= pages_read_since_last_sleep;
1637 : : }
1638 : :
1639 : : /* Report pending statistics to the cumulative stats system. */
375 michael@paquier.xyz 1640 :CBC 14 : pgstat_report_wal(false);
1641 : :
1642 : : /* OK, now sleep. */
816 rhaas@postgresql.org 1643 : 14 : (void) WaitLatch(MyLatch,
1644 : : WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
1645 : : sleep_quanta * MS_PER_SLEEP_QUANTUM,
1646 : : WAIT_EVENT_WAL_SUMMARIZER_WAL);
1647 : 14 : ResetLatch(MyLatch);
1648 : :
1649 : : /* Reset count of pages read. */
1650 : 14 : pages_read_since_last_sleep = 0;
1651 : 14 : }
1652 : :
1653 : : /*
1654 : : * Remove WAL summaries whose mtimes are older than wal_summary_keep_time.
1655 : : */
1656 : : static void
1657 : 21 : MaybeRemoveOldWalSummaries(void)
1658 : : {
1659 : 21 : XLogRecPtr redo_pointer = GetRedoRecPtr();
1660 : : List *wslist;
1661 : : time_t cutoff_time;
1662 : :
1663 : : /* If WAL summary removal is disabled, don't do anything. */
1664 [ - + ]: 21 : if (wal_summary_keep_time == 0)
816 rhaas@postgresql.org 1665 :UBC 0 : return;
1666 : :
1667 : : /*
1668 : : * If the redo pointer has not advanced, don't do anything.
1669 : : *
1670 : : * This has the effect that we only try to remove old WAL summary files
1671 : : * once per checkpoint cycle.
1672 : : */
816 rhaas@postgresql.org 1673 [ + + ]:CBC 21 : if (redo_pointer == redo_pointer_at_last_summary_removal)
1674 : 15 : return;
1675 : 6 : redo_pointer_at_last_summary_removal = redo_pointer;
1676 : :
1677 : : /*
1678 : : * Files should only be removed if the last modification time precedes the
1679 : : * cutoff time we compute here.
1680 : : */
725 nathan@postgresql.or 1681 : 6 : cutoff_time = time(NULL) - wal_summary_keep_time * SECS_PER_MINUTE;
1682 : :
1683 : : /* Get all the summaries that currently exist. */
816 rhaas@postgresql.org 1684 : 6 : wslist = GetWalSummaries(0, InvalidXLogRecPtr, InvalidXLogRecPtr);
1685 : :
1686 : : /* Loop until all summaries have been considered for removal. */
1687 [ + + ]: 9 : while (wslist != NIL)
1688 : : {
1689 : : ListCell *lc;
1690 : : XLogSegNo oldest_segno;
1691 : 3 : XLogRecPtr oldest_lsn = InvalidXLogRecPtr;
1692 : : TimeLineID selected_tli;
1693 : :
375 heikki.linnakangas@i 1694 : 3 : ProcessWalSummarizerInterrupts();
1695 : :
1696 : : /*
1697 : : * Pick a timeline for which some summary files still exist on disk,
1698 : : * and find the oldest LSN that still exists on disk for that
1699 : : * timeline.
1700 : : */
816 rhaas@postgresql.org 1701 : 3 : selected_tli = ((WalSummaryFile *) linitial(wslist))->tli;
1702 : 3 : oldest_segno = XLogGetOldestSegno(selected_tli);
1703 [ + - ]: 3 : if (oldest_segno != 0)
1704 : 3 : XLogSegNoOffsetToRecPtr(oldest_segno, 0, wal_segment_size,
1705 : : oldest_lsn);
1706 : :
1707 : :
1708 : : /* Consider each WAL file on the selected timeline in turn. */
1709 [ + + + - : 28 : foreach(lc, wslist)
+ + ]
1710 : : {
1711 : 25 : WalSummaryFile *ws = lfirst(lc);
1712 : :
375 heikki.linnakangas@i 1713 : 25 : ProcessWalSummarizerInterrupts();
1714 : :
1715 : : /* If it's not on this timeline, it's not time to consider it. */
816 rhaas@postgresql.org 1716 [ - + ]: 25 : if (selected_tli != ws->tli)
816 rhaas@postgresql.org 1717 :UBC 0 : continue;
1718 : :
1719 : : /*
1720 : : * If the WAL doesn't exist any more, we can remove it if the file
1721 : : * modification time is old enough.
1722 : : */
129 alvherre@kurilemu.de 1723 [ + - + + ]:GNC 25 : if (!XLogRecPtrIsValid(oldest_lsn) || ws->end_lsn <= oldest_lsn)
816 rhaas@postgresql.org 1724 :CBC 7 : RemoveWalSummaryIfOlderThan(ws, cutoff_time);
1725 : :
1726 : : /*
1727 : : * Whether we removed the file or not, we need not consider it
1728 : : * again.
1729 : : */
1730 : 25 : wslist = foreach_delete_current(wslist, lc);
1731 : 25 : pfree(ws);
1732 : : }
1733 : : }
1734 : : }
|