/src/postgres/src/backend/access/transam/xlogarchive.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * xlogarchive.c |
4 | | * Functions for archiving WAL files and restoring from the archive. |
5 | | * |
6 | | * |
7 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
8 | | * Portions Copyright (c) 1994, Regents of the University of California |
9 | | * |
10 | | * src/backend/access/transam/xlogarchive.c |
11 | | * |
12 | | *------------------------------------------------------------------------- |
13 | | */ |
14 | | |
15 | | #include "postgres.h" |
16 | | |
17 | | #include <sys/stat.h> |
18 | | #include <sys/wait.h> |
19 | | #include <signal.h> |
20 | | #include <unistd.h> |
21 | | |
22 | | #include "access/xlog.h" |
23 | | #include "access/xlog_internal.h" |
24 | | #include "access/xlogarchive.h" |
25 | | #include "common/archive.h" |
26 | | #include "common/percentrepl.h" |
27 | | #include "miscadmin.h" |
28 | | #include "pgstat.h" |
29 | | #include "postmaster/pgarch.h" |
30 | | #include "postmaster/startup.h" |
31 | | #include "replication/walsender.h" |
32 | | #include "storage/fd.h" |
33 | | #include "storage/ipc.h" |
34 | | #include "utils/wait_event.h" |
35 | | |
36 | | /* |
37 | | * Attempt to retrieve the specified file from off-line archival storage. |
38 | | * If successful, fill "path" with its complete path (note that this will be |
39 | | * a temp file name that doesn't follow the normal naming convention), and |
40 | | * return true. |
41 | | * |
42 | | * If not successful, fill "path" with the name of the normal on-line file |
43 | | * (which may or may not actually exist, but we'll try to use it), and return |
44 | | * false. |
45 | | * |
46 | | * For fixed-size files, the caller may pass the expected size as an |
47 | | * additional crosscheck on successful recovery. If the file size is not |
48 | | * known, set expectedSize = 0. |
49 | | * |
50 | | * When 'cleanupEnabled' is false, refrain from deleting any old WAL segments |
51 | | * in the archive. This is used when fetching the initial checkpoint record, |
52 | | * when we are not yet sure how far back we need the WAL. |
53 | | */ |
54 | | bool |
55 | | RestoreArchivedFile(char *path, const char *xlogfname, |
56 | | const char *recovername, off_t expectedSize, |
57 | | bool cleanupEnabled) |
58 | 0 | { |
59 | 0 | char xlogpath[MAXPGPATH]; |
60 | 0 | char *xlogRestoreCmd; |
61 | 0 | char lastRestartPointFname[MAXPGPATH]; |
62 | 0 | int rc; |
63 | 0 | struct stat stat_buf; |
64 | 0 | XLogSegNo restartSegNo; |
65 | 0 | XLogRecPtr restartRedoPtr; |
66 | 0 | TimeLineID restartTli; |
67 | | |
68 | | /* |
69 | | * Ignore restore_command when not in archive recovery (meaning we are in |
70 | | * crash recovery). |
71 | | */ |
72 | 0 | if (!ArchiveRecoveryRequested) |
73 | 0 | goto not_available; |
74 | | |
75 | | /* In standby mode, restore_command might not be supplied */ |
76 | 0 | if (recoveryRestoreCommand == NULL || strcmp(recoveryRestoreCommand, "") == 0) |
77 | 0 | goto not_available; |
78 | | |
79 | | /* |
80 | | * When doing archive recovery, we always prefer an archived log file even |
81 | | * if a file of the same name exists in XLOGDIR. The reason is that the |
82 | | * file in XLOGDIR could be an old, un-filled or partly-filled version |
83 | | * that was copied and restored as part of backing up $PGDATA. |
84 | | * |
85 | | * We could try to optimize this slightly by checking the local copy |
86 | | * lastchange timestamp against the archived copy, but we have no API to |
87 | | * do this, nor can we guarantee that the lastchange timestamp was |
88 | | * preserved correctly when we copied to archive. Our aim is robustness, |
89 | | * so we elect not to do this. |
90 | | * |
91 | | * If we cannot obtain the log file from the archive, however, we will try |
92 | | * to use the XLOGDIR file if it exists. This is so that we can make use |
93 | | * of log segments that weren't yet transferred to the archive. |
94 | | * |
95 | | * Notice that we don't actually overwrite any files when we copy back |
96 | | * from archive because the restore_command may inadvertently restore |
97 | | * inappropriate xlogs, or they may be corrupt, so we may wish to fallback |
98 | | * to the segments remaining in current XLOGDIR later. The |
99 | | * copy-from-archive filename is always the same, ensuring that we don't |
100 | | * run out of disk space on long recoveries. |
101 | | */ |
102 | 0 | snprintf(xlogpath, MAXPGPATH, XLOGDIR "/%s", recovername); |
103 | | |
104 | | /* |
105 | | * Make sure there is no existing file named recovername. |
106 | | */ |
107 | 0 | if (stat(xlogpath, &stat_buf) != 0) |
108 | 0 | { |
109 | 0 | if (errno != ENOENT) |
110 | 0 | ereport(FATAL, |
111 | 0 | (errcode_for_file_access(), |
112 | 0 | errmsg("could not stat file \"%s\": %m", |
113 | 0 | xlogpath))); |
114 | 0 | } |
115 | 0 | else |
116 | 0 | { |
117 | 0 | if (unlink(xlogpath) != 0) |
118 | 0 | ereport(FATAL, |
119 | 0 | (errcode_for_file_access(), |
120 | 0 | errmsg("could not remove file \"%s\": %m", |
121 | 0 | xlogpath))); |
122 | 0 | } |
123 | | |
124 | | /* |
125 | | * Calculate the archive file cutoff point for use during log shipping |
126 | | * replication. All files earlier than this point can be deleted from the |
127 | | * archive, though there is no requirement to do so. |
128 | | * |
129 | | * If cleanup is not enabled, initialise this with the filename of |
130 | | * InvalidXLogRecPtr, which will prevent the deletion of any WAL files |
131 | | * from the archive because of the alphabetic sorting property of WAL |
132 | | * filenames. |
133 | | * |
134 | | * Once we have successfully located the redo pointer of the checkpoint |
135 | | * from which we start recovery we never request a file prior to the redo |
136 | | * pointer of the last restartpoint. When redo begins we know that we have |
137 | | * successfully located it, so there is no need for additional status |
138 | | * flags to signify the point when we can begin deleting WAL files from |
139 | | * the archive. |
140 | | */ |
141 | 0 | if (cleanupEnabled) |
142 | 0 | { |
143 | 0 | GetOldestRestartPoint(&restartRedoPtr, &restartTli); |
144 | 0 | XLByteToSeg(restartRedoPtr, restartSegNo, wal_segment_size); |
145 | 0 | XLogFileName(lastRestartPointFname, restartTli, restartSegNo, |
146 | 0 | wal_segment_size); |
147 | | /* we shouldn't need anything earlier than last restart point */ |
148 | 0 | Assert(strcmp(lastRestartPointFname, xlogfname) <= 0); |
149 | 0 | } |
150 | 0 | else |
151 | 0 | XLogFileName(lastRestartPointFname, 0, 0, wal_segment_size); |
152 | | |
153 | | /* Build the restore command to execute */ |
154 | 0 | xlogRestoreCmd = BuildRestoreCommand(recoveryRestoreCommand, |
155 | 0 | xlogpath, xlogfname, |
156 | 0 | lastRestartPointFname); |
157 | |
|
158 | 0 | ereport(DEBUG3, |
159 | 0 | (errmsg_internal("executing restore command \"%s\"", |
160 | 0 | xlogRestoreCmd))); |
161 | | |
162 | 0 | fflush(NULL); |
163 | 0 | pgstat_report_wait_start(WAIT_EVENT_RESTORE_COMMAND); |
164 | | |
165 | | /* |
166 | | * PreRestoreCommand() informs the SIGTERM handler for the startup process |
167 | | * that it should proc_exit() right away. This is done for the duration |
168 | | * of the system() call because there isn't a good way to break out while |
169 | | * it is executing. Since we might call proc_exit() in a signal handler, |
170 | | * it is best to put any additional logic before or after the |
171 | | * PreRestoreCommand()/PostRestoreCommand() section. |
172 | | */ |
173 | 0 | PreRestoreCommand(); |
174 | | |
175 | | /* |
176 | | * Copy xlog from archival storage to XLOGDIR |
177 | | */ |
178 | 0 | rc = system(xlogRestoreCmd); |
179 | |
|
180 | 0 | PostRestoreCommand(); |
181 | |
|
182 | 0 | pgstat_report_wait_end(); |
183 | 0 | pfree(xlogRestoreCmd); |
184 | |
|
185 | 0 | if (rc == 0) |
186 | 0 | { |
187 | | /* |
188 | | * command apparently succeeded, but let's make sure the file is |
189 | | * really there now and has the correct size. |
190 | | */ |
191 | 0 | if (stat(xlogpath, &stat_buf) == 0) |
192 | 0 | { |
193 | 0 | if (expectedSize > 0 && stat_buf.st_size != expectedSize) |
194 | 0 | { |
195 | 0 | int elevel; |
196 | | |
197 | | /* |
198 | | * If we find a partial file in standby mode, we assume it's |
199 | | * because it's just being copied to the archive, and keep |
200 | | * trying. |
201 | | * |
202 | | * Otherwise treat a wrong-sized file as FATAL to ensure the |
203 | | * DBA would notice it, but is that too strong? We could try |
204 | | * to plow ahead with a local copy of the file ... but the |
205 | | * problem is that there probably isn't one, and we'd |
206 | | * incorrectly conclude we've reached the end of WAL and we're |
207 | | * done recovering ... |
208 | | */ |
209 | 0 | if (StandbyMode && stat_buf.st_size < expectedSize) |
210 | 0 | elevel = DEBUG1; |
211 | 0 | else |
212 | 0 | elevel = FATAL; |
213 | 0 | ereport(elevel, |
214 | 0 | (errmsg("archive file \"%s\" has wrong size: %lld instead of %lld", |
215 | 0 | xlogfname, |
216 | 0 | (long long int) stat_buf.st_size, |
217 | 0 | (long long int) expectedSize))); |
218 | 0 | return false; |
219 | 0 | } |
220 | 0 | else |
221 | 0 | { |
222 | 0 | ereport(LOG, |
223 | 0 | (errmsg("restored log file \"%s\" from archive", |
224 | 0 | xlogfname))); |
225 | 0 | strcpy(path, xlogpath); |
226 | 0 | return true; |
227 | 0 | } |
228 | 0 | } |
229 | 0 | else |
230 | 0 | { |
231 | | /* stat failed */ |
232 | 0 | int elevel = (errno == ENOENT) ? LOG : FATAL; |
233 | |
|
234 | 0 | ereport(elevel, |
235 | 0 | (errcode_for_file_access(), |
236 | 0 | errmsg("could not stat file \"%s\": %m", xlogpath), |
237 | 0 | errdetail("\"restore_command\" returned a zero exit status, but stat() failed."))); |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * Remember, we rollforward UNTIL the restore fails so failure here is |
243 | | * just part of the process... that makes it difficult to determine |
244 | | * whether the restore failed because there isn't an archive to restore, |
245 | | * or because the administrator has specified the restore program |
246 | | * incorrectly. We have to assume the former. |
247 | | * |
248 | | * However, if the failure was due to any sort of signal, it's best to |
249 | | * punt and abort recovery. (If we "return false" here, upper levels will |
250 | | * assume that recovery is complete and start up the database!) It's |
251 | | * essential to abort on child SIGINT and SIGQUIT, because per spec |
252 | | * system() ignores SIGINT and SIGQUIT while waiting; if we see one of |
253 | | * those it's a good bet we should have gotten it too. |
254 | | * |
255 | | * On SIGTERM, assume we have received a fast shutdown request, and exit |
256 | | * cleanly. It's pure chance whether we receive the SIGTERM first, or the |
257 | | * child process. If we receive it first, the signal handler will call |
258 | | * proc_exit, otherwise we do it here. If we or the child process received |
259 | | * SIGTERM for any other reason than a fast shutdown request, postmaster |
260 | | * will perform an immediate shutdown when it sees us exiting |
261 | | * unexpectedly. |
262 | | * |
263 | | * We treat hard shell errors such as "command not found" as fatal, too. |
264 | | */ |
265 | 0 | if (wait_result_is_signal(rc, SIGTERM)) |
266 | 0 | proc_exit(1); |
267 | |
|
268 | 0 | ereport(wait_result_is_any_signal(rc, true) ? FATAL : DEBUG2, |
269 | 0 | (errmsg("could not restore file \"%s\" from archive: %s", |
270 | 0 | xlogfname, wait_result_to_str(rc)))); |
271 | | |
272 | 0 | not_available: |
273 | | |
274 | | /* |
275 | | * if an archived file is not available, there might still be a version of |
276 | | * this file in XLOGDIR, so return that as the filename to open. |
277 | | * |
278 | | * In many recovery scenarios we expect this to fail also, but if so that |
279 | | * just means we've reached the end of WAL. |
280 | | */ |
281 | 0 | snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlogfname); |
282 | 0 | return false; |
283 | 0 | } |
284 | | |
285 | | /* |
286 | | * Attempt to execute an external shell command during recovery. |
287 | | * |
288 | | * 'command' is the shell command to be executed, 'commandName' is a |
289 | | * human-readable name describing the command emitted in the logs. If |
290 | | * 'failOnSignal' is true and the command is killed by a signal, a FATAL |
291 | | * error is thrown. Otherwise a WARNING is emitted. |
292 | | * |
293 | | * This is currently used for recovery_end_command and archive_cleanup_command. |
294 | | */ |
295 | | void |
296 | | ExecuteRecoveryCommand(const char *command, const char *commandName, |
297 | | bool failOnSignal, uint32 wait_event_info) |
298 | 0 | { |
299 | 0 | char *xlogRecoveryCmd; |
300 | 0 | char lastRestartPointFname[MAXPGPATH]; |
301 | 0 | int rc; |
302 | 0 | XLogSegNo restartSegNo; |
303 | 0 | XLogRecPtr restartRedoPtr; |
304 | 0 | TimeLineID restartTli; |
305 | |
|
306 | 0 | Assert(command && commandName); |
307 | | |
308 | | /* |
309 | | * Calculate the archive file cutoff point for use during log shipping |
310 | | * replication. All files earlier than this point can be deleted from the |
311 | | * archive, though there is no requirement to do so. |
312 | | */ |
313 | 0 | GetOldestRestartPoint(&restartRedoPtr, &restartTli); |
314 | 0 | XLByteToSeg(restartRedoPtr, restartSegNo, wal_segment_size); |
315 | 0 | XLogFileName(lastRestartPointFname, restartTli, restartSegNo, |
316 | 0 | wal_segment_size); |
317 | | |
318 | | /* |
319 | | * construct the command to be executed |
320 | | */ |
321 | 0 | xlogRecoveryCmd = replace_percent_placeholders(command, commandName, "r", lastRestartPointFname); |
322 | |
|
323 | 0 | ereport(DEBUG3, |
324 | 0 | (errmsg_internal("executing %s \"%s\"", commandName, command))); |
325 | | |
326 | | /* |
327 | | * execute the constructed command |
328 | | */ |
329 | 0 | fflush(NULL); |
330 | 0 | pgstat_report_wait_start(wait_event_info); |
331 | 0 | rc = system(xlogRecoveryCmd); |
332 | 0 | pgstat_report_wait_end(); |
333 | |
|
334 | 0 | pfree(xlogRecoveryCmd); |
335 | |
|
336 | 0 | if (rc != 0) |
337 | 0 | { |
338 | | /* |
339 | | * If the failure was due to any sort of signal, it's best to punt and |
340 | | * abort recovery. See comments in RestoreArchivedFile(). |
341 | | */ |
342 | 0 | ereport((failOnSignal && wait_result_is_any_signal(rc, true)) ? FATAL : WARNING, |
343 | | /*------ |
344 | | translator: First %s represents a postgresql.conf parameter name like |
345 | | "recovery_end_command", the 2nd is the value of that parameter, the |
346 | | third an already translated error message. */ |
347 | 0 | (errmsg("%s \"%s\": %s", commandName, |
348 | 0 | command, wait_result_to_str(rc)))); |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | | |
353 | | /* |
354 | | * A file was restored from the archive under a temporary filename (path), |
355 | | * and now we want to keep it. Rename it under the permanent filename in |
356 | | * pg_wal (xlogfname), replacing any existing file with the same name. |
357 | | */ |
358 | | void |
359 | | KeepFileRestoredFromArchive(const char *path, const char *xlogfname) |
360 | 0 | { |
361 | 0 | char xlogfpath[MAXPGPATH]; |
362 | 0 | bool reload = false; |
363 | 0 | struct stat statbuf; |
364 | |
|
365 | 0 | snprintf(xlogfpath, MAXPGPATH, XLOGDIR "/%s", xlogfname); |
366 | |
|
367 | 0 | if (stat(xlogfpath, &statbuf) == 0) |
368 | 0 | { |
369 | | #ifdef WIN32 |
370 | | char oldpath[MAXPGPATH]; |
371 | | static unsigned int deletedcounter = 1; |
372 | | |
373 | | /* |
374 | | * On Windows, if another process (e.g a walsender process) holds the |
375 | | * file open in FILE_SHARE_DELETE mode, unlink will succeed, but the |
376 | | * file will still show up in directory listing until the last handle |
377 | | * is closed, and we cannot rename the new file in its place until |
378 | | * that. To avoid that problem, rename the old file to a temporary |
379 | | * name first. Use a counter to create a unique filename, because the |
380 | | * same file might be restored from the archive multiple times, and a |
381 | | * walsender could still be holding onto an old deleted version of it. |
382 | | * |
383 | | * Note that this leaves a short window where the target name does not |
384 | | * exist, during which a concurrent walsender opening the segment |
385 | | * could fail. |
386 | | */ |
387 | | snprintf(oldpath, MAXPGPATH, "%s.deleted%u", |
388 | | xlogfpath, deletedcounter++); |
389 | | if (rename(xlogfpath, oldpath) != 0) |
390 | | { |
391 | | ereport(ERROR, |
392 | | (errcode_for_file_access(), |
393 | | errmsg("could not rename file \"%s\" to \"%s\": %m", |
394 | | xlogfpath, oldpath))); |
395 | | } |
396 | | |
397 | | if (unlink(oldpath) != 0) |
398 | | ereport(FATAL, |
399 | | (errcode_for_file_access(), |
400 | | errmsg("could not remove file \"%s\": %m", |
401 | | oldpath))); |
402 | | #endif |
403 | 0 | reload = true; |
404 | 0 | } |
405 | |
|
406 | 0 | durable_rename(path, xlogfpath, ERROR); |
407 | | |
408 | | /* |
409 | | * Create .done file forcibly to prevent the restored segment from being |
410 | | * archived again later. |
411 | | */ |
412 | 0 | if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS) |
413 | 0 | XLogArchiveForceDone(xlogfname); |
414 | 0 | else |
415 | 0 | XLogArchiveNotify(xlogfname); |
416 | | |
417 | | /* |
418 | | * If the existing file was replaced, since walsenders might have it open, |
419 | | * request them to reload a currently-open segment. This is only required |
420 | | * for WAL segments, walsenders don't hold other files open, but there's |
421 | | * no harm in doing this too often, and we don't know what kind of a file |
422 | | * we're dealing with here. |
423 | | */ |
424 | 0 | if (reload) |
425 | 0 | WalSndRqstFileReload(); |
426 | | |
427 | | /* |
428 | | * Signal walsender that new WAL has arrived. Again, this isn't necessary |
429 | | * if we restored something other than a WAL segment, but it does no harm |
430 | | * either. |
431 | | */ |
432 | 0 | WalSndWakeup(true, false); |
433 | 0 | } |
434 | | |
435 | | /* |
436 | | * XLogArchiveNotify |
437 | | * |
438 | | * Create an archive notification file |
439 | | * |
440 | | * The name of the notification file is the message that will be picked up |
441 | | * by the archiver, e.g. we write 0000000100000001000000C6.ready |
442 | | * and the archiver then knows to archive XLOGDIR/0000000100000001000000C6, |
443 | | * then when complete, rename it to 0000000100000001000000C6.done |
444 | | */ |
445 | | void |
446 | | XLogArchiveNotify(const char *xlog) |
447 | 0 | { |
448 | 0 | char archiveStatusPath[MAXPGPATH]; |
449 | 0 | FILE *fd; |
450 | | |
451 | | /* insert an otherwise empty file called <XLOG>.ready */ |
452 | 0 | StatusFilePath(archiveStatusPath, xlog, ".ready"); |
453 | 0 | fd = AllocateFile(archiveStatusPath, "w"); |
454 | 0 | if (fd == NULL) |
455 | 0 | { |
456 | 0 | ereport(LOG, |
457 | 0 | (errcode_for_file_access(), |
458 | 0 | errmsg("could not create archive status file \"%s\": %m", |
459 | 0 | archiveStatusPath))); |
460 | 0 | return; |
461 | 0 | } |
462 | 0 | if (FreeFile(fd)) |
463 | 0 | { |
464 | 0 | ereport(LOG, |
465 | 0 | (errcode_for_file_access(), |
466 | 0 | errmsg("could not write archive status file \"%s\": %m", |
467 | 0 | archiveStatusPath))); |
468 | 0 | return; |
469 | 0 | } |
470 | | |
471 | | /* |
472 | | * Timeline history files are given the highest archival priority to lower |
473 | | * the chance that a promoted standby will choose a timeline that is |
474 | | * already in use. However, the archiver ordinarily tries to gather |
475 | | * multiple files to archive from each scan of the archive_status |
476 | | * directory, which means that newly created timeline history files could |
477 | | * be left unarchived for a while. To ensure that the archiver picks up |
478 | | * timeline history files as soon as possible, we force the archiver to |
479 | | * scan the archive_status directory the next time it looks for a file to |
480 | | * archive. |
481 | | */ |
482 | 0 | if (IsTLHistoryFileName(xlog)) |
483 | 0 | PgArchForceDirScan(); |
484 | | |
485 | | /* Notify archiver that it's got something to do */ |
486 | 0 | if (IsUnderPostmaster) |
487 | 0 | PgArchWakeup(); |
488 | 0 | } |
489 | | |
490 | | /* |
491 | | * Convenience routine to notify using segment number representation of filename |
492 | | */ |
493 | | void |
494 | | XLogArchiveNotifySeg(XLogSegNo segno, TimeLineID tli) |
495 | 0 | { |
496 | 0 | char xlog[MAXFNAMELEN]; |
497 | |
|
498 | 0 | Assert(tli != 0); |
499 | |
|
500 | 0 | XLogFileName(xlog, tli, segno, wal_segment_size); |
501 | 0 | XLogArchiveNotify(xlog); |
502 | 0 | } |
503 | | |
504 | | /* |
505 | | * XLogArchiveForceDone |
506 | | * |
507 | | * Emit notification forcibly that an XLOG segment file has been successfully |
508 | | * archived, by creating <XLOG>.done regardless of whether <XLOG>.ready |
509 | | * exists or not. |
510 | | */ |
511 | | void |
512 | | XLogArchiveForceDone(const char *xlog) |
513 | | { |
514 | | char archiveReady[MAXPGPATH]; |
515 | | char archiveDone[MAXPGPATH]; |
516 | | struct stat stat_buf; |
517 | | FILE *fd; |
518 | | |
519 | | /* Exit if already known done */ |
520 | | StatusFilePath(archiveDone, xlog, ".done"); |
521 | | if (stat(archiveDone, &stat_buf) == 0) |
522 | | return; |
523 | | |
524 | | /* If .ready exists, rename it to .done */ |
525 | | StatusFilePath(archiveReady, xlog, ".ready"); |
526 | | if (stat(archiveReady, &stat_buf) == 0) |
527 | | { |
528 | | (void) durable_rename(archiveReady, archiveDone, WARNING); |
529 | | return; |
530 | | } |
531 | | |
532 | | /* insert an otherwise empty file called <XLOG>.done */ |
533 | | fd = AllocateFile(archiveDone, "w"); |
534 | | if (fd == NULL) |
535 | | { |
536 | | ereport(LOG, |
537 | | (errcode_for_file_access(), |
538 | | errmsg("could not create archive status file \"%s\": %m", |
539 | | archiveDone))); |
540 | | return; |
541 | | } |
542 | | if (FreeFile(fd)) |
543 | | { |
544 | | ereport(LOG, |
545 | | (errcode_for_file_access(), |
546 | | errmsg("could not write archive status file \"%s\": %m", |
547 | | archiveDone))); |
548 | | return; |
549 | | } |
550 | | } |
551 | | |
552 | | /* |
553 | | * XLogArchiveCheckDone |
554 | | * |
555 | | * This is called when we are ready to delete or recycle an old XLOG segment |
556 | | * file or backup history file. If it is okay to delete it then return true. |
557 | | * If it is not time to delete it, make sure a .ready file exists, and return |
558 | | * false. |
559 | | * |
560 | | * If <XLOG>.done exists, then return true; else if <XLOG>.ready exists, |
561 | | * then return false; else create <XLOG>.ready and return false. |
562 | | * |
563 | | * The reason we do things this way is so that if the original attempt to |
564 | | * create <XLOG>.ready fails, we'll retry during subsequent checkpoints. |
565 | | */ |
566 | | bool |
567 | | XLogArchiveCheckDone(const char *xlog) |
568 | 0 | { |
569 | 0 | char archiveStatusPath[MAXPGPATH]; |
570 | 0 | struct stat stat_buf; |
571 | | |
572 | | /* The file is always deletable if archive_mode is "off". */ |
573 | 0 | if (!XLogArchivingActive()) |
574 | 0 | return true; |
575 | | |
576 | | /* |
577 | | * During archive recovery, the file is deletable if archive_mode is not |
578 | | * "always". |
579 | | */ |
580 | 0 | if (!XLogArchivingAlways() && |
581 | 0 | GetRecoveryState() == RECOVERY_STATE_ARCHIVE) |
582 | 0 | return true; |
583 | | |
584 | | /* |
585 | | * At this point of the logic, note that we are either a primary with |
586 | | * archive_mode set to "on" or "always", or a standby with archive_mode |
587 | | * set to "always". |
588 | | */ |
589 | | |
590 | | /* First check for .done --- this means archiver is done with it */ |
591 | 0 | StatusFilePath(archiveStatusPath, xlog, ".done"); |
592 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
593 | 0 | return true; |
594 | | |
595 | | /* check for .ready --- this means archiver is still busy with it */ |
596 | 0 | StatusFilePath(archiveStatusPath, xlog, ".ready"); |
597 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
598 | 0 | return false; |
599 | | |
600 | | /* Race condition --- maybe archiver just finished, so recheck */ |
601 | 0 | StatusFilePath(archiveStatusPath, xlog, ".done"); |
602 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
603 | 0 | return true; |
604 | | |
605 | | /* Retry creation of the .ready file */ |
606 | 0 | XLogArchiveNotify(xlog); |
607 | 0 | return false; |
608 | 0 | } |
609 | | |
610 | | /* |
611 | | * XLogArchiveIsBusy |
612 | | * |
613 | | * Check to see if an XLOG segment file is still unarchived. |
614 | | * This is almost but not quite the inverse of XLogArchiveCheckDone: in |
615 | | * the first place we aren't chartered to recreate the .ready file, and |
616 | | * in the second place we should consider that if the file is already gone |
617 | | * then it's not busy. (This check is needed to handle the race condition |
618 | | * that a checkpoint already deleted the no-longer-needed file.) |
619 | | */ |
620 | | bool |
621 | | XLogArchiveIsBusy(const char *xlog) |
622 | 0 | { |
623 | 0 | char archiveStatusPath[MAXPGPATH]; |
624 | 0 | struct stat stat_buf; |
625 | | |
626 | | /* First check for .done --- this means archiver is done with it */ |
627 | 0 | StatusFilePath(archiveStatusPath, xlog, ".done"); |
628 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
629 | 0 | return false; |
630 | | |
631 | | /* check for .ready --- this means archiver is still busy with it */ |
632 | 0 | StatusFilePath(archiveStatusPath, xlog, ".ready"); |
633 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
634 | 0 | return true; |
635 | | |
636 | | /* Race condition --- maybe archiver just finished, so recheck */ |
637 | 0 | StatusFilePath(archiveStatusPath, xlog, ".done"); |
638 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
639 | 0 | return false; |
640 | | |
641 | | /* |
642 | | * Check to see if the WAL file has been removed by checkpoint, which |
643 | | * implies it has already been archived, and explains why we can't see a |
644 | | * status file for it. |
645 | | */ |
646 | 0 | snprintf(archiveStatusPath, MAXPGPATH, XLOGDIR "/%s", xlog); |
647 | 0 | if (stat(archiveStatusPath, &stat_buf) != 0 && |
648 | 0 | errno == ENOENT) |
649 | 0 | return false; |
650 | | |
651 | 0 | return true; |
652 | 0 | } |
653 | | |
654 | | /* |
655 | | * XLogArchiveIsReadyOrDone |
656 | | * |
657 | | * Check to see if an XLOG segment file has a .ready or .done file. |
658 | | * This is similar to XLogArchiveIsBusy(), but returns true if the file |
659 | | * is already archived or is about to be archived. |
660 | | * |
661 | | * This is currently only used at recovery. During normal operation this |
662 | | * would be racy: the file might get removed or marked with .ready as we're |
663 | | * checking it, or immediately after we return. |
664 | | */ |
665 | | bool |
666 | | XLogArchiveIsReadyOrDone(const char *xlog) |
667 | 0 | { |
668 | 0 | char archiveStatusPath[MAXPGPATH]; |
669 | 0 | struct stat stat_buf; |
670 | | |
671 | | /* First check for .done --- this means archiver is done with it */ |
672 | 0 | StatusFilePath(archiveStatusPath, xlog, ".done"); |
673 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
674 | 0 | return true; |
675 | | |
676 | | /* check for .ready --- this means archiver is still busy with it */ |
677 | 0 | StatusFilePath(archiveStatusPath, xlog, ".ready"); |
678 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
679 | 0 | return true; |
680 | | |
681 | | /* Race condition --- maybe archiver just finished, so recheck */ |
682 | 0 | StatusFilePath(archiveStatusPath, xlog, ".done"); |
683 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
684 | 0 | return true; |
685 | | |
686 | 0 | return false; |
687 | 0 | } |
688 | | |
689 | | /* |
690 | | * XLogArchiveIsReady |
691 | | * |
692 | | * Check to see if an XLOG segment file has an archive notification (.ready) |
693 | | * file. |
694 | | */ |
695 | | bool |
696 | | XLogArchiveIsReady(const char *xlog) |
697 | 0 | { |
698 | 0 | char archiveStatusPath[MAXPGPATH]; |
699 | 0 | struct stat stat_buf; |
700 | |
|
701 | 0 | StatusFilePath(archiveStatusPath, xlog, ".ready"); |
702 | 0 | if (stat(archiveStatusPath, &stat_buf) == 0) |
703 | 0 | return true; |
704 | | |
705 | 0 | return false; |
706 | 0 | } |
707 | | |
708 | | /* |
709 | | * XLogArchiveCleanup |
710 | | * |
711 | | * Cleanup archive notification file(s) for a particular xlog segment |
712 | | */ |
713 | | void |
714 | | XLogArchiveCleanup(const char *xlog) |
715 | 0 | { |
716 | 0 | char archiveStatusPath[MAXPGPATH]; |
717 | | |
718 | | /* Remove the .done file */ |
719 | 0 | StatusFilePath(archiveStatusPath, xlog, ".done"); |
720 | 0 | unlink(archiveStatusPath); |
721 | | /* should we complain about failure? */ |
722 | | |
723 | | /* Remove the .ready file if present --- normally it shouldn't be */ |
724 | 0 | StatusFilePath(archiveStatusPath, xlog, ".ready"); |
725 | 0 | unlink(archiveStatusPath); |
726 | | /* should we complain about failure? */ |
727 | 0 | } |