1 /*-------------------------------------------------------------------------
4 * Functions for accessing and managing WAL summary data.
6 * Portions Copyright (c) 2010-2024, PostgreSQL Global Development Group
8 * src/backend/backup/walsummary.c
10 *-------------------------------------------------------------------------
18 #include "access/xlog_internal.h"
19 #include "backup/walsummary.h"
20 #include "common/int.h"
21 #include "utils/wait_event.h"
23 static bool IsWalSummaryFilename(char *filename
);
24 static int ListComparatorForWalSummaryFiles(const ListCell
*a
,
28 * Get a list of WAL summaries.
30 * If tli != 0, only WAL summaries with the indicated TLI will be included.
32 * If start_lsn != InvalidXLogRecPtr, only summaries that end after the
33 * indicated LSN will be included.
35 * If end_lsn != InvalidXLogRecPtr, only summaries that start before the
36 * indicated LSN will be included.
38 * The intent is that you can call GetWalSummaries(tli, start_lsn, end_lsn)
39 * to get all WAL summaries on the indicated timeline that overlap the
40 * specified LSN range.
43 GetWalSummaries(TimeLineID tli
, XLogRecPtr start_lsn
, XLogRecPtr end_lsn
)
49 sdir
= AllocateDir(XLOGDIR
"/summaries");
50 while ((dent
= ReadDir(sdir
, XLOGDIR
"/summaries")) != NULL
)
55 XLogRecPtr file_start_lsn
;
56 XLogRecPtr file_end_lsn
;
58 /* Decode filename, or skip if it's not in the expected format. */
59 if (!IsWalSummaryFilename(dent
->d_name
))
61 sscanf(dent
->d_name
, "%08X%08X%08X%08X%08X",
62 &tmp
[0], &tmp
[1], &tmp
[2], &tmp
[3], &tmp
[4]);
64 file_start_lsn
= ((uint64
) tmp
[1]) << 32 | tmp
[2];
65 file_end_lsn
= ((uint64
) tmp
[3]) << 32 | tmp
[4];
67 /* Skip if it doesn't match the filter criteria. */
68 if (tli
!= 0 && tli
!= file_tli
)
70 if (!XLogRecPtrIsInvalid(start_lsn
) && start_lsn
>= file_end_lsn
)
72 if (!XLogRecPtrIsInvalid(end_lsn
) && end_lsn
<= file_start_lsn
)
75 /* Add it to the list. */
76 ws
= palloc(sizeof(WalSummaryFile
));
78 ws
->start_lsn
= file_start_lsn
;
79 ws
->end_lsn
= file_end_lsn
;
80 result
= lappend(result
, ws
);
88 * Build a new list of WAL summaries based on an existing list, but filtering
89 * out summaries that don't match the search parameters.
91 * If tli != 0, only WAL summaries with the indicated TLI will be included.
93 * If start_lsn != InvalidXLogRecPtr, only summaries that end after the
94 * indicated LSN will be included.
96 * If end_lsn != InvalidXLogRecPtr, only summaries that start before the
97 * indicated LSN will be included.
100 FilterWalSummaries(List
*wslist
, TimeLineID tli
,
101 XLogRecPtr start_lsn
, XLogRecPtr end_lsn
)
106 /* Loop over input. */
109 WalSummaryFile
*ws
= lfirst(lc
);
111 /* Skip if it doesn't match the filter criteria. */
112 if (tli
!= 0 && tli
!= ws
->tli
)
114 if (!XLogRecPtrIsInvalid(start_lsn
) && start_lsn
> ws
->end_lsn
)
116 if (!XLogRecPtrIsInvalid(end_lsn
) && end_lsn
< ws
->start_lsn
)
119 /* Add it to the result list. */
120 result
= lappend(result
, ws
);
127 * Check whether the supplied list of WalSummaryFile objects covers the
128 * whole range of LSNs from start_lsn to end_lsn. This function ignores
129 * timelines, so the caller should probably filter using the appropriate
130 * timeline before calling this.
132 * If the whole range of LSNs is covered, returns true, otherwise false.
133 * If false is returned, *missing_lsn is set either to InvalidXLogRecPtr
134 * if there are no WAL summary files in the input list, or to the first LSN
135 * in the range that is not covered by a WAL summary file in the input list.
138 WalSummariesAreComplete(List
*wslist
, XLogRecPtr start_lsn
,
139 XLogRecPtr end_lsn
, XLogRecPtr
*missing_lsn
)
141 XLogRecPtr current_lsn
= start_lsn
;
144 /* Special case for empty list. */
147 *missing_lsn
= InvalidXLogRecPtr
;
151 /* Make a private copy of the list and sort it by start LSN. */
152 wslist
= list_copy(wslist
);
153 list_sort(wslist
, ListComparatorForWalSummaryFiles
);
156 * Consider summary files in order of increasing start_lsn, advancing the
157 * known-summarized range from start_lsn toward end_lsn.
159 * Normally, the summary files should cover non-overlapping WAL ranges,
160 * but this algorithm is intended to be correct even in case of overlap.
164 WalSummaryFile
*ws
= lfirst(lc
);
166 if (ws
->start_lsn
> current_lsn
)
168 /* We found a gap. */
171 if (ws
->end_lsn
> current_lsn
)
174 * Next summary extends beyond end of previous summary, so extend
175 * the end of the range known to be summarized.
177 current_lsn
= ws
->end_lsn
;
180 * If the range we know to be summarized has reached the required
181 * end LSN, we have proved completeness.
183 if (current_lsn
>= end_lsn
)
189 * We either ran out of summary files without reaching the end LSN, or we
190 * hit a gap in the sequence that resulted in us bailing out of the loop
193 *missing_lsn
= current_lsn
;
198 * Open a WAL summary file.
200 * This will throw an error in case of trouble. As an exception, if
201 * missing_ok = true and the trouble is specifically that the file does
202 * not exist, it will not throw an error and will return a value less than 0.
205 OpenWalSummaryFile(WalSummaryFile
*ws
, bool missing_ok
)
207 char path
[MAXPGPATH
];
210 snprintf(path
, MAXPGPATH
,
211 XLOGDIR
"/summaries/%08X%08X%08X%08X%08X.summary",
213 LSN_FORMAT_ARGS(ws
->start_lsn
),
214 LSN_FORMAT_ARGS(ws
->end_lsn
));
216 file
= PathNameOpenFile(path
, O_RDONLY
);
217 if (file
< 0 && (errno
!= EEXIST
|| !missing_ok
))
219 (errcode_for_file_access(),
220 errmsg("could not open file \"%s\": %m", path
)));
226 * Remove a WAL summary file if the last modification time precedes the
230 RemoveWalSummaryIfOlderThan(WalSummaryFile
*ws
, time_t cutoff_time
)
232 char path
[MAXPGPATH
];
235 snprintf(path
, MAXPGPATH
,
236 XLOGDIR
"/summaries/%08X%08X%08X%08X%08X.summary",
238 LSN_FORMAT_ARGS(ws
->start_lsn
),
239 LSN_FORMAT_ARGS(ws
->end_lsn
));
241 if (lstat(path
, &statbuf
) != 0)
246 (errcode_for_file_access(),
247 errmsg("could not stat file \"%s\": %m", path
)));
249 if (statbuf
.st_mtime
>= cutoff_time
)
251 if (unlink(path
) != 0)
253 (errcode_for_file_access(),
254 errmsg("could not stat file \"%s\": %m", path
)));
256 (errmsg_internal("removing file \"%s\"", path
)));
260 * Test whether a filename looks like a WAL summary file.
263 IsWalSummaryFilename(char *filename
)
265 return strspn(filename
, "0123456789ABCDEF") == 40 &&
266 strcmp(filename
+ 40, ".summary") == 0;
270 * Data read callback for use with CreateBlockRefTableReader.
273 ReadWalSummary(void *wal_summary_io
, void *data
, int length
)
275 WalSummaryIO
*io
= wal_summary_io
;
278 nbytes
= FileRead(io
->file
, data
, length
, io
->filepos
,
279 WAIT_EVENT_WAL_SUMMARY_READ
);
282 (errcode_for_file_access(),
283 errmsg("could not read file \"%s\": %m",
284 FilePathName(io
->file
))));
286 io
->filepos
+= nbytes
;
291 * Data write callback for use with WriteBlockRefTable.
294 WriteWalSummary(void *wal_summary_io
, void *data
, int length
)
296 WalSummaryIO
*io
= wal_summary_io
;
299 nbytes
= FileWrite(io
->file
, data
, length
, io
->filepos
,
300 WAIT_EVENT_WAL_SUMMARY_WRITE
);
303 (errcode_for_file_access(),
304 errmsg("could not write file \"%s\": %m",
305 FilePathName(io
->file
))));
306 if (nbytes
!= length
)
308 (errcode_for_file_access(),
309 errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
310 FilePathName(io
->file
), nbytes
,
311 length
, (unsigned) io
->filepos
),
312 errhint("Check free disk space.")));
314 io
->filepos
+= nbytes
;
319 * Error-reporting callback for use with CreateBlockRefTableReader.
322 ReportWalSummaryError(void *callback_arg
, char *fmt
,...)
328 initStringInfo(&buf
);
332 needed
= appendStringInfoVA(&buf
, fmt
, ap
);
336 enlargeStringInfo(&buf
, needed
);
339 errcode(ERRCODE_DATA_CORRUPTED
),
340 errmsg_internal("%s", buf
.data
));
344 * Comparator to sort a List of WalSummaryFile objects by start_lsn.
347 ListComparatorForWalSummaryFiles(const ListCell
*a
, const ListCell
*b
)
349 WalSummaryFile
*ws1
= lfirst(a
);
350 WalSummaryFile
*ws2
= lfirst(b
);
352 return pg_cmp_u64(ws1
->start_lsn
, ws2
->start_lsn
);