• postgresql源码学习(38)—— 备份还原② - do_pg_start_backup函数


    一、 pg中的pg_stop_backup函数

           在执行pg_start_backup函数开启备份模式后,务必要执行pg_stop_backup函数结束备份 (详细参考下方源码)。

    结束排他备份

    1. postgres=# SELECT pg_start_backup('backup02',false,true);
    2. pg_start_backup
    3. -----------------
    4. 0/3000028
    5. (1 row)
    6. postgres=# SELECT pg_stop_backup(true);
    7. NOTICE: WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup
    8. pg_stop_backup
    9. ----------------
    10. (0/4000138,,)
    11. (1 row)

    自动删除backup_label文件

    结束非排他备份

    1. postgres=# SELECT pg_start_backup('pgdata backup',false,false);
    2. pg_start_backup
    3. -----------------
    4. 0/2000060
    5. (1 row)
    6. postgres=# SELECT pg_stop_backup(false);
    7. NOTICE: WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup
    8. pg_stop_backup
    9. ---------------------------------------------------------------------------
    10. (0/5000088,"START WAL LOCATION: 0/2000060 (file 000000010000000000000002)+
    11. CHECKPOINT LOCATION: 0/2000098 +
    12. BACKUP METHOD: streamed +
    13. BACKUP FROM: primary +
    14. START TIME: 2022-07-14 18:39:47 CST +
    15. LABEL: pgdata backup +
    16. START TIMELINE: 1 +
    17. ","")
    18. (1 row)

    二、 do_pg_stop_backup函数

    在源码中,pg_stop_backup实际对应的是do_pg_stop_backup函数。

    1. 主要参数

    • labelfile:若为空,停止排他备份;若不为空,停止该标签名对应的非排他备份
    • waitforarchive:是否等待WAL日志被归档
    • stoptli_p:时间线ID

    2. 返回值

    从该备份还原时必须存在的最后一个WAL位置,以及*.stoptli_p中相应的时间线ID

    3. 主要流程

    • 预检查,与start函数类似
    • 如果是排他模式,删除backup_label文件。(对于非排他模式,backup_label中的信息由每个备份进程自己维护,在执行pg_stop_backup函数时返回给用户)
    • 修改备份状态和计数器
    • 如果当前已没有其他备份,关闭强制全页写 forcePageWrites
    • 非排他模式,解析备份进程中的backup_label信息
    • 强制日志切换
    • 写一个备份结束的XLOG记录
    • 创建备份历史文件,该文件包含 backup_label文件的内容及执行pg_stop_backup的时间信息
    • 等待所需WAL文件归档完成(可选)
    • 返回备份结束的WAL位置
    1. /*
    2. * do_pg_stop_backup
    3. *
    4. * Utility function called at the end of an online backup. It cleans up the backup state and can optionally wait for WAL segments to be archived.
    5. *
    6. * Returns the last WAL location that must be present to restore from this backup, and the corresponding timeline ID in *stoptli_p.
    7. */
    8. XLogRecPtr
    9. do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
    10. {
    11. bool exclusive = (labelfile == NULL);
    12. bool backup_started_in_recovery = false;
    13. XLogRecPtr startpoint;
    14. XLogRecPtr stoppoint;
    15. TimeLineID stoptli;
    16. pg_time_t stamp_time;
    17. char strfbuf[128];
    18. char histfilepath[MAXPGPATH];
    19. char startxlogfilename[MAXFNAMELEN];
    20. char stopxlogfilename[MAXFNAMELEN];
    21. char lastxlogfilename[MAXFNAMELEN];
    22. char histfilename[MAXFNAMELEN];
    23. char backupfrom[20];
    24. XLogSegNo _logSegNo;
    25. FILE *lfp;
    26. FILE *fp;
    27. char ch;
    28. int seconds_before_warning;
    29. int waits = 0;
    30. bool reported_waiting = false;
    31. char *remaining;
    32. char *ptr;
    33. uint32 hi,
    34. lo;
    35. /* 开头的预检查跟start函数类似,这里不再重复介绍 */
    36. backup_started_in_recovery = RecoveryInProgress();
    37. /*
    38. * Currently only non-exclusive backup can be taken during recovery.
    39. */
    40. if (backup_started_in_recovery && exclusive)
    41. ereport(ERROR,
    42. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    43. errmsg("recovery is in progress"),
    44. errhint("WAL control functions cannot be executed during recovery.")));
    45. /*
    46. * During recovery, we don't need to check WAL level. Because, if WAL
    47. * level is not sufficient, it's impossible to get here during recovery.
    48. */
    49. if (!backup_started_in_recovery && !XLogIsNeeded())
    50. ereport(ERROR,
    51. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    52. errmsg("WAL level not sufficient for making an online backup"),
    53. errhint("wal_level must be set to \"replica\" or \"logical\" at server start.")));
    54. if (exclusive)
    55. {
    56. /*
    57. * At first, mark that we're now stopping an exclusive backup, to
    58. * ensure that there are no other sessions currently running
    59. * pg_start_backup() or pg_stop_backup().
    60. */
    61. WALInsertLockAcquireExclusive();
    62. if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS)
    63. {
    64. WALInsertLockRelease();
    65. ereport(ERROR,
    66. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    67. errmsg("exclusive backup not in progress")));
    68. }
    69. XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING;
    70. WALInsertLockRelease();
    71. /*
    72. * Remove backup_label. In case of failure, the state for an exclusive backup is switched back to in-progress.
    73. * 同样下面代码如果运行失败,会执行回调函数pg_stop_backup_callback,将排他备份状态改回运行中
    74. */
    75. PG_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
    76. {
    77. /*
    78. * Read the existing label file into memory.将标签文件读入内存
    79. */
    80. struct stat statbuf;
    81. int r;
    82. if (stat(BACKUP_LABEL_FILE, &statbuf))
    83. {
    84. /* should not happen per the upper checks */
    85. if (errno != ENOENT)
    86. ereport(ERROR,
    87. (errcode_for_file_access(),
    88. errmsg("could not stat file \"%s\": %m",
    89. BACKUP_LABEL_FILE)));
    90. ereport(ERROR,
    91. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    92. errmsg("a backup is not in progress")));
    93. }
    94. lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
    95. if (!lfp)
    96. {
    97. ereport(ERROR,
    98. (errcode_for_file_access(),
    99. errmsg("could not read file \"%s\": %m",
    100. BACKUP_LABEL_FILE)));
    101. }
    102. labelfile = palloc(statbuf.st_size + 1);
    103. r = fread(labelfile, statbuf.st_size, 1, lfp);
    104. labelfile[statbuf.st_size] = '\0';
    105. /*
    106. * Close and remove the backup label file,关闭并删除标签文件
    107. */
    108. if (r != 1 || ferror(lfp) || FreeFile(lfp))
    109. ereport(ERROR,
    110. (errcode_for_file_access(),
    111. errmsg("could not read file \"%s\": %m",
    112. BACKUP_LABEL_FILE)));
    113. durable_unlink(BACKUP_LABEL_FILE, ERROR);
    114. /*
    115. * Remove tablespace_map file if present, it is created only if there are tablespaces. 如果表空间映射文件存在,也将其删除
    116. */
    117. durable_unlink(TABLESPACE_MAP, DEBUG1);
    118. }
    119. PG_END_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
    120. }
    121. /*
    122. * OK to update backup counters, forcePageWrites and session-level lock.
    123. *
    124. * Note that CHECK_FOR_INTERRUPTS() must not occur while updating them. Otherwise they can be updated inconsistently, and which might cause do_pg_abort_backup() to fail.
    125. */
    126. WALInsertLockAcquireExclusive();
    127. if (exclusive)
    128. {
    129. /* 排他模式,修改备份状态为NONE */
    130. XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE;
    131. }
    132. else
    133. {
    134. /* 非排他模式,计数器-1 */
    135. Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
    136. XLogCtl->Insert.nonExclusiveBackups--;
    137. }
    138. /* 若当前已无备份(包括排他模式和非排他模式),关闭强制全页写 */
    139. if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
    140. XLogCtl->Insert.nonExclusiveBackups == 0)
    141. {
    142. XLogCtl->Insert.forcePageWrites = false;
    143. }
    144. /*
    145. * Clean up session-level lock.
    146. * 更新会话级备份状态,并释放插入锁
    147. */
    148. sessionBackupState = SESSION_BACKUP_NONE;
    149. WALInsertLockRelease();
    150. /*
    151. * Read and parse the START WAL LOCATION line,读取并解析labelfile中的START WAL LOCATION行。注意labelfile是非排他模式备份传入的标签文件名,跟前面删除的排他文件生成的backup_label文件不一样。
    152. */
    153. if (sscanf(labelfile, "START WAL LOCATION: %X/%X (file %24s)%c",
    154. &hi, &lo, startxlogfilename,
    155. &ch) != 4 || ch != '\n')
    156. ereport(ERROR,
    157. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    158. errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
    159. startpoint = ((uint64) hi) << 32 | lo;
    160. remaining = strchr(labelfile, '\n') + 1; /* %n is not portable enough */
    161. /*
    162. * 在剩余内容中解析 BACKUP FROM 行.
    163. */
    164. ptr = strstr(remaining, "BACKUP FROM:");
    165. if (!ptr || sscanf(ptr, "BACKUP FROM: %19s\n", backupfrom) != 1)
    166. ereport(ERROR,
    167. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    168. errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
    169. if (strcmp(backupfrom, "standby") == 0 && !backup_started_in_recovery)
    170. ereport(ERROR,
    171. (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    172. errmsg("the standby was promoted during online backup"),
    173. errhint("This means that the backup being taken is corrupt "
    174. "and should not be used. "
    175. "Try taking another online backup.")));
    176. /* 如果处于恢复阶段,要做一些设置,略 */
    177. if (backup_started_in_recovery)
    178. {
    179. }
    180. else
    181. {
    182. /*
    183. * Write the backup-end xlog record,写backup-end的XLOG记录
    184. */
    185. XLogBeginInsert();
    186. XLogRegisterData((char *) (&startpoint), sizeof(startpoint));
    187. stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);
    188. stoptli = ThisTimeLineID;
    189. /*
    190. * Force a switch to a new xlog segment file, 强制日志切换
    191. */
    192. RequestXLogSwitch(false);
    193. XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size);
    194. XLogFileName(stopxlogfilename, stoptli, _logSegNo, wal_segment_size);
    195. /* Use the log timezone here, not the session timezone */
    196. stamp_time = (pg_time_t) time(NULL);
    197. pg_strftime(strfbuf, sizeof(strfbuf),
    198. "%Y-%m-%d %H:%M:%S %Z",
    199. pg_localtime(&stamp_time, log_timezone));
    200. /*
    201. * Write the backup history file,写备份历史文件
    202. */
    203. XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
    204. BackupHistoryFilePath(histfilepath, stoptli, _logSegNo,
    205. startpoint, wal_segment_size);
    206. fp = AllocateFile(histfilepath, "w");
    207. if (!fp)
    208. ereport(ERROR,
    209. (errcode_for_file_access(),
    210. errmsg("could not create file \"%s\": %m",
    211. histfilepath)));
    212. fprintf(fp, "START WAL LOCATION: %X/%X (file %s)\n",
    213. LSN_FORMAT_ARGS(startpoint), startxlogfilename);
    214. fprintf(fp, "STOP WAL LOCATION: %X/%X (file %s)\n",
    215. LSN_FORMAT_ARGS(stoppoint), stopxlogfilename);
    216. /*
    217. * Transfer remaining lines including label and start timeline to history file.
    218. */
    219. fprintf(fp, "%s", remaining);
    220. fprintf(fp, "STOP TIME: %s\n", strfbuf);
    221. fprintf(fp, "STOP TIMELINE: %u\n", stoptli);
    222. if (fflush(fp) || ferror(fp) || FreeFile(fp))
    223. ereport(ERROR,
    224. (errcode_for_file_access(),
    225. errmsg("could not write file \"%s\": %m",
    226. histfilepath)));
    227. /*
    228. * Clean out any no-longer-needed history files. As a side effect, this will post a .ready file for the newly created history file, notifying the archiver that history file may be archived immediately. 清理备份历史信息
    229. */
    230. CleanupBackupHistory();
    231. }
    232. /*
    233. * 如果是归档模式,且设置了waitforarchive,则等待所需日志归档完成
    234. */
    235. if (waitforarchive &&
    236. ((!backup_started_in_recovery && XLogArchivingActive()) ||
    237. (backup_started_in_recovery && XLogArchivingAlways())))
    238. {
    239. //等待所需日志归档完成,略
    240. }
    241. /*
    242. * 如果不是归档模式,但又设置了waitforarchive,发送提醒给用户,需要自己保证备份期间的WAL文件存在
    243. */
    244. else if (waitforarchive)
    245. ereport(NOTICE,
    246. (errmsg("WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup")));
    247. /*
    248. * We're done. As a convenience, return the ending WAL location.
    249. * 函数执行结束,返回WAL结束位置
    250. */
    251. if (stoptli_p)
    252. *stoptli_p = stoptli;
    253. return stoppoint;
    254. }

    三、 pg_stop_backup_callback函数

    前面代码中有一段

    PG_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));

    确保如果PG_ENSURE_ERROR_CLEANUP中的代码运行失败,则将排他备份状态改回运行中。

    1. /*
    2. * Error cleanup callback for pg_stop_backup
    3. */
    4. static void
    5. pg_stop_backup_callback(int code, Datum arg)
    6. {
    7. bool exclusive = DatumGetBool(arg);
    8. /* Update backup status on failure */
    9. WALInsertLockAcquireExclusive();
    10. if (exclusive)
    11. {
    12. Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING);
    13. XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS;
    14. }
    15. WALInsertLockRelease();
    16. }

    参考

    PostgreSQL技术内幕:事务处理深度探索》第4

  • 相关阅读:
    DiFi A Go-as-You-Pay Wi-Fi Access System 精读笔记(三)
    try-except 搭配装饰器使用
    【C语言】哈夫曼树,再来一次解剖
    美国服务器:全面剖析其主要优点与潜在缺点
    ZooKeeper 概述
    使用sonar-scanner扫描代码
    我的创作纪念日-从写作到阿里云专家博主的故事
    STM32F103C8T6 驱动MTS4温度传感器
    论文《Enhancing Hypergraph Neural Networks with Intent Disentanglement for SBR》阅读
    面试面经|Java开发面试JVM面试题
  • 原文地址:https://blog.csdn.net/Hehuyi_In/article/details/126321077