pg_archivecleanup用于清理PostgreSQL WAL归档文件。指定归档目录,指定一个最老的日志段文件(在此之前的WAL日志都删掉), 用法如下:
shell
postgres@slpc:~$ pg_archivecleanup --help
pg_archivecleanup removes older WAL files from PostgreSQL archives.
Usage:
pg_archivecleanup [OPTION]... ARCHIVELOCATION OLDESTKEPTWALFILE
Options:
-d generate debug output (verbose mode)
-n dry run, show the names of the files that would be removed
-V, --version output version information, then exit
-x EXT clean up files if they have this extension
-?, --help show this help, then exit
For use as archive_cleanup_command in postgresql.conf:
archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %r'
e.g.
archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %r'
Or for use as a standalone archive cleaner:
e.g.
pg_archivecleanup /mnt/server/archiverdir 000000010000000000000010.00000020.backup
可参考文档:http://www.postgres.cn/docs/14/pgarchivecleanup.html
源码分析
源码不多,在pg_archivecleanup.c
中。
c
main(int argc, char **argv)
--> Initialize(); // 检查归档目录是否有效
--> SetWALFileNameForCleanup(); // 检查the oldest file we want to remain in archive是否有效
--> CleanupPriorWALFiles(); // 具体的清理归档日志
清理的代码实现如下:
c
static void CleanupPriorWALFiles(void)
{
int rc;
DIR *xldir;
struct dirent *xlde;
char walfile[MAXPGPATH];
if ((xldir = opendir(archiveLocation)) != NULL)
{
while (errno = 0, (xlde = readdir(xldir)) != NULL)
{
strlcpy(walfile, xlde->d_name, MAXPGPATH);
TrimExtension(walfile, additional_ext);
// 比较的时候,忽略时间线,
/*
* We ignore the timeline part of the XLOG segment identifiers in
* deciding whether a segment is still needed. This ensures that
* we won't prematurely remove a segment from a parent timeline.
* We could probably be a little more proactive about removing
* segments of non-parent timelines, but that would be a whole lot
* more complicated.
*
* We use the alphanumeric sorting property of the filenames to
* decide which ones are earlier than the exclusiveCleanupFileName
* file. Note that this means files are not removed in the order
* they were originally written, in case this worries you.
*/
if ((IsXLogFileName(walfile) || IsPartialXLogFileName(walfile)) &&
strcmp(walfile + 8, exclusiveCleanupFileName + 8) < 0)
{
char WALFilePath[MAXPGPATH * 2]; /* the file path
* including archive */
/*
* Use the original file name again now, including any
* extension that might have been chopped off before testing
* the sequence.
*/
snprintf(WALFilePath, sizeof(WALFilePath), "%s/%s",
archiveLocation, xlde->d_name);
if (dryrun)
{
/*
* Prints the name of the file to be removed and skips the
* actual removal. The regular printout is so that the
* user can pipe the output into some other program.
*/
printf("%s\n", WALFilePath);
pg_log_debug("file \"%s\" would be removed", WALFilePath);
continue;
}
pg_log_debug("removing file \"%s\"", WALFilePath);
rc = unlink(WALFilePath);
if (rc != 0)
pg_fatal("could not remove file \"%s\": %m",
WALFilePath);
}
}
if (errno)
pg_fatal("could not read archive location \"%s\": %m",
archiveLocation);
if (closedir(xldir))
pg_fatal("could not close archive location \"%s\": %m",
archiveLocation);
}
else
pg_fatal("could not open archive location \"%s\": %m",
archiveLocation);
}