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