pg_probackup
779 строк · 22.8 Кб
1/*-------------------------------------------------------------------------
2*
3* stream.c: pg_probackup specific code for WAL streaming
4*
5* Portions Copyright (c) 2015-2020, Postgres Professional
6*
7*-------------------------------------------------------------------------
8*/
9
10#include "pg_probackup.h"11#include "receivelog.h"12#include "streamutil.h"13#include "access/timeline.h"14
15#include <time.h>16#include <unistd.h>17
18/*
19* global variable needed by ReceiveXlogStream()
20*
21* standby_message_timeout controls how often we send a message
22* back to the primary letting it know our progress, in milliseconds.
23*
24* in pg_probackup we use a default setting = 10 sec
25*/
26static int standby_message_timeout = 10 * 1000;27
28/* stop_backup_lsn is set by pg_stop_backup() to stop streaming */
29XLogRecPtr stop_backup_lsn = InvalidXLogRecPtr;30static XLogRecPtr stop_stream_lsn = InvalidXLogRecPtr;31
32/*
33* How long we should wait for streaming end in seconds.
34* Retrieved as checkpoint_timeout + checkpoint_timeout * 0.1
35*/
36static uint32 stream_stop_timeout = 0;37/* Time in which we started to wait for streaming end */
38static time_t stream_stop_begin = 0;39
40/*
41* We need to wait end of WAL streaming before execute pg_stop_backup().
42*/
43typedef struct44{
45char basedir[MAXPGPATH];46PGconn *conn;47
48/*49* Return value from the thread.
50* 0 means there is no error, 1 - there is an error.
51*/
52int ret;53
54XLogRecPtr startpos;55TimeLineID starttli;56} StreamThreadArg;57
58static pthread_t stream_thread;59static StreamThreadArg stream_thread_arg = {"", NULL, 1};60
61static parray *xlog_files_list = NULL;62static bool do_crc = true;63
64static void IdentifySystem(StreamThreadArg *stream_thread_arg);65static int checkpoint_timeout(PGconn *backup_conn);66static void *StreamLog(void *arg);67static bool stop_streaming(XLogRecPtr xlogpos, uint32 timeline,68bool segment_finished);69static void add_walsegment_to_filelist(parray *filelist, uint32 timeline,70XLogRecPtr xlogpos, char *basedir,71uint32 xlog_seg_size);72static void add_history_file_to_filelist(parray *filelist, uint32 timeline,73char *basedir);74
75/*
76* Run IDENTIFY_SYSTEM through a given connection and
77* check system identifier and timeline are matching
78*/
79static void80IdentifySystem(StreamThreadArg *stream_thread_arg)81{
82PGresult *res;83
84uint64 stream_conn_sysidentifier = 0;85char *stream_conn_sysidentifier_str;86TimeLineID stream_conn_tli = 0;87
88if (!CheckServerVersionForStreaming(stream_thread_arg->conn))89{90PQfinish(stream_thread_arg->conn);91/*92* Error message already written in CheckServerVersionForStreaming().
93* There's no hope of recovering from a version mismatch, so don't
94* retry.
95*/
96elog(ERROR, "Cannot continue backup because stream connect has failed.");97}98
99/*100* Identify server, obtain server system identifier and timeline
101*/
102res = pgut_execute(stream_thread_arg->conn, "IDENTIFY_SYSTEM", 0, NULL);103
104if (PQresultStatus(res) != PGRES_TUPLES_OK)105{106elog(WARNING,"Could not send replication command \"%s\": %s",107"IDENTIFY_SYSTEM", PQerrorMessage(stream_thread_arg->conn));108PQfinish(stream_thread_arg->conn);109elog(ERROR, "Cannot continue backup because stream connect has failed.");110}111
112stream_conn_sysidentifier_str = PQgetvalue(res, 0, 0);113stream_conn_tli = atoll(PQgetvalue(res, 0, 1));114
115/* Additional sanity, primary for PG 9.5,116* where system id can be obtained only via "IDENTIFY SYSTEM"
117*/
118if (!parse_uint64(stream_conn_sysidentifier_str, &stream_conn_sysidentifier, 0))119elog(ERROR, "%s is not system_identifier", stream_conn_sysidentifier_str);120
121if (stream_conn_sysidentifier != instance_config.system_identifier)122elog(ERROR, "System identifier mismatch. Connected PostgreSQL instance has system id: "123"" UINT64_FORMAT ". Expected: " UINT64_FORMAT ".",124stream_conn_sysidentifier, instance_config.system_identifier);125
126if (stream_conn_tli != current.tli)127elog(ERROR, "Timeline identifier mismatch. "128"Connected PostgreSQL instance has timeline id: %X. Expected: %X.",129stream_conn_tli, current.tli);130
131PQclear(res);132}
133
134/*
135* Retrieve checkpoint_timeout GUC value in seconds.
136*/
137static int138checkpoint_timeout(PGconn *backup_conn)139{
140PGresult *res;141const char *val;142const char *hintmsg;143int val_int;144
145res = pgut_execute(backup_conn, "show checkpoint_timeout", 0, NULL);146val = PQgetvalue(res, 0, 0);147
148if (!parse_int(val, &val_int, OPTION_UNIT_S, &hintmsg))149{150PQclear(res);151if (hintmsg)152elog(ERROR, "Invalid value of checkout_timeout %s: %s", val,153hintmsg);154else155elog(ERROR, "Invalid value of checkout_timeout %s", val);156}157
158PQclear(res);159
160return val_int;161}
162
163/*
164* CreateReplicationSlot_compat() -- wrapper for CreateReplicationSlot() used in StreamLog()
165* src/bin/pg_basebackup/streamutil.c
166* CreateReplicationSlot() has different signatures on different PG versions:
167* PG 15
168* bool
169* CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin,
170* bool is_temporary, bool is_physical, bool reserve_wal,
171* bool slot_exists_ok, bool two_phase)
172* PG 11-14
173* bool
174* CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin,
175* bool is_temporary, bool is_physical, bool reserve_wal,
176* bool slot_exists_ok)
177* PG 9.5-10
178* CreateReplicationSlot(PGconn *conn, const char *slot_name, const char *plugin,
179* bool is_physical, bool slot_exists_ok)
180* NOTE: PG 9.6 and 10 support reserve_wal in
181* pg_catalog.pg_create_physical_replication_slot(slot_name name [, immediately_reserve boolean])
182* and
183* CREATE_REPLICATION_SLOT slot_name { PHYSICAL [ RESERVE_WAL ] | LOGICAL output_plugin }
184* replication protocol command, but CreateReplicationSlot() C function doesn't
185*/
186static bool187CreateReplicationSlot_compat(PGconn *conn, const char *slot_name, const char *plugin,188bool is_temporary, bool is_physical,189bool slot_exists_ok)190{
191#if PG_VERSION_NUM >= 150000192return CreateReplicationSlot(conn, slot_name, plugin, is_temporary, is_physical,193/* reserve_wal = */ true, slot_exists_ok, /* two_phase = */ false);194#elif PG_VERSION_NUM >= 110000195return CreateReplicationSlot(conn, slot_name, plugin, is_temporary, is_physical,196/* reserve_wal = */ true, slot_exists_ok);197#elif PG_VERSION_NUM >= 100000198/*199* PG-10 doesn't support creating temp_slot by calling CreateReplicationSlot(), but
200* it will be created by setting StreamCtl.temp_slot later in StreamLog()
201*/
202if (!is_temporary)203return CreateReplicationSlot(conn, slot_name, plugin, /*is_temporary,*/ is_physical, /*reserve_wal,*/ slot_exists_ok);204else205return true;206#else207/* these parameters not supported in PG < 10 */208Assert(!is_temporary);209return CreateReplicationSlot(conn, slot_name, plugin, /*is_temporary,*/ is_physical, /*reserve_wal,*/ slot_exists_ok);210#endif211}
212
213/*
214* Start the log streaming
215*/
216static void *217StreamLog(void *arg)218{
219StreamThreadArg *stream_arg = (StreamThreadArg *) arg;220
221/*222* Always start streaming at the beginning of a segment
223*/
224stream_arg->startpos -= stream_arg->startpos % instance_config.xlog_seg_size;225
226xlog_files_list = parray_new();227
228/* Initialize timeout */229stream_stop_begin = 0;230
231/* Create repslot */232#if PG_VERSION_NUM >= 100000233if (temp_slot || perm_slot)234if (!CreateReplicationSlot_compat(stream_arg->conn, replication_slot, NULL, temp_slot, true, false))235#else236if (perm_slot)237if (!CreateReplicationSlot_compat(stream_arg->conn, replication_slot, NULL, false, true, false))238#endif239{240interrupted = true;241elog(ERROR, "Couldn't create physical replication slot %s", replication_slot);242}243
244/*245* Start the replication
246*/
247if (replication_slot)248elog(LOG, "started streaming WAL at %X/%X (timeline %u) using%s slot %s",249(uint32) (stream_arg->startpos >> 32), (uint32) stream_arg->startpos,250stream_arg->starttli,251#if PG_VERSION_NUM >= 100000252temp_slot ? " temporary" : "",253#else254"",255#endif256replication_slot);257else258elog(LOG, "started streaming WAL at %X/%X (timeline %u)",259(uint32) (stream_arg->startpos >> 32), (uint32) stream_arg->startpos,260stream_arg->starttli);261
262#if PG_VERSION_NUM >= 90600263{264StreamCtl ctl;265
266MemSet(&ctl, 0, sizeof(ctl));267
268ctl.startpos = stream_arg->startpos;269ctl.timeline = stream_arg->starttli;270ctl.sysidentifier = NULL;271ctl.stream_stop = stop_streaming;272ctl.standby_message_timeout = standby_message_timeout;273ctl.partial_suffix = NULL;274ctl.synchronous = false;275ctl.mark_done = false;276
277#if PG_VERSION_NUM >= 100000278#if PG_VERSION_NUM >= 150000279ctl.walmethod = CreateWalDirectoryMethod(280stream_arg->basedir,281PG_COMPRESSION_NONE,2820,283false);284#else /* PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 150000 */285ctl.walmethod = CreateWalDirectoryMethod(286stream_arg->basedir,287// (instance_config.compress_alg == NONE_COMPRESS) ? 0 : instance_config.compress_level,
2880,289false);290#endif /* PG_VERSION_NUM >= 150000 */291ctl.replication_slot = replication_slot;292ctl.stop_socket = PGINVALID_SOCKET;293ctl.do_sync = false; /* We sync all files at the end of backup */294// ctl.mark_done /* for future use in s3 */
295#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000296/* StreamCtl.temp_slot used only for PG-10, in PG>10, temp_slots are created by calling CreateReplicationSlot() */297ctl.temp_slot = temp_slot;298#endif /* PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000 */299#else /* PG_VERSION_NUM < 100000 */300ctl.basedir = (char *) stream_arg->basedir;301#endif /* PG_VERSION_NUM >= 100000 */302
303if (ReceiveXlogStream(stream_arg->conn, &ctl) == false)304{305interrupted = true;306elog(ERROR, "Problem in receivexlog");307}308
309#if PG_VERSION_NUM >= 100000310#if PG_VERSION_NUM >= 160000311if (!ctl.walmethod->ops->finish(ctl.walmethod))312#else313if (!ctl.walmethod->finish())314#endif315{316interrupted = true;317elog(ERROR, "Could not finish writing WAL files: %s",318strerror(errno));319}320#endif /* PG_VERSION_NUM >= 100000 */321}322#else /* PG_VERSION_NUM < 90600 */323/* PG-9.5 */324if (ReceiveXlogStream(stream_arg->conn, stream_arg->startpos, stream_arg->starttli,325NULL, (char *) stream_arg->basedir, stop_streaming,326standby_message_timeout, NULL, false, false) == false)327{328interrupted = true;329elog(ERROR, "Problem in receivexlog");330}331#endif /* PG_VERSION_NUM >= 90600 */332
333/* be paranoid and sort xlog_files_list,334* so if stop_lsn segno is already in the list,
335* then list must be sorted to detect duplicates.
336*/
337parray_qsort(xlog_files_list, pgFileCompareRelPathWithExternal);338
339/* Add the last segment to the list */340add_walsegment_to_filelist(xlog_files_list, stream_arg->starttli,341stop_stream_lsn, (char *) stream_arg->basedir,342instance_config.xlog_seg_size);343
344/* append history file to walsegment filelist */345add_history_file_to_filelist(xlog_files_list, stream_arg->starttli, (char *) stream_arg->basedir);346
347/*348* TODO: remove redundant WAL segments
349* walk pg_wal and remove files with segno greater that of stop_lsn`s segno +1
350*/
351
352elog(LOG, "finished streaming WAL at %X/%X (timeline %u)",353(uint32) (stop_stream_lsn >> 32), (uint32) stop_stream_lsn, stream_arg->starttli);354stream_arg->ret = 0;355
356PQfinish(stream_arg->conn);357stream_arg->conn = NULL;358
359return NULL;360}
361
362/*
363* for ReceiveXlogStream
364*
365* The stream_stop callback will be called every time data
366* is received, and whenever a segment is completed. If it returns
367* true, the streaming will stop and the function
368* return. As long as it returns false, streaming will continue
369* indefinitely.
370*
371* Stop WAL streaming if current 'xlogpos' exceeds 'stop_backup_lsn', which is
372* set by pg_stop_backup().
373*
374*/
375static bool376stop_streaming(XLogRecPtr xlogpos, uint32 timeline, bool segment_finished)377{
378static uint32 prevtimeline = 0;379static XLogRecPtr prevpos = InvalidXLogRecPtr;380
381/* check for interrupt */382if (interrupted || thread_interrupted)383elog(ERROR, "Interrupted during WAL streaming");384
385/* we assume that we get called once at the end of each segment */386if (segment_finished)387{388elog(VERBOSE, _("finished segment at %X/%X (timeline %u)"),389(uint32) (xlogpos >> 32), (uint32) xlogpos, timeline);390
391add_walsegment_to_filelist(xlog_files_list, timeline, xlogpos,392(char*) stream_thread_arg.basedir,393instance_config.xlog_seg_size);394}395
396/*397* Note that we report the previous, not current, position here. After a
398* timeline switch, xlogpos points to the beginning of the segment because
399* that's where we always begin streaming. Reporting the end of previous
400* timeline isn't totally accurate, because the next timeline can begin
401* slightly before the end of the WAL that we received on the previous
402* timeline, but it's close enough for reporting purposes.
403*/
404if (prevtimeline != 0 && prevtimeline != timeline)405elog(LOG, _("switched to timeline %u at %X/%X\n"),406timeline, (uint32) (prevpos >> 32), (uint32) prevpos);407
408if (!XLogRecPtrIsInvalid(stop_backup_lsn))409{410if (xlogpos >= stop_backup_lsn)411{412stop_stream_lsn = xlogpos;413return true;414}415
416/* pg_stop_backup() was executed, wait for the completion of stream */417if (stream_stop_begin == 0)418{419elog(INFO, "Wait for LSN %X/%X to be streamed",420(uint32) (stop_backup_lsn >> 32), (uint32) stop_backup_lsn);421
422stream_stop_begin = time(NULL);423}424
425if (time(NULL) - stream_stop_begin > stream_stop_timeout)426elog(ERROR, "Target LSN %X/%X could not be streamed in %d seconds",427(uint32) (stop_backup_lsn >> 32), (uint32) stop_backup_lsn,428stream_stop_timeout);429}430
431prevtimeline = timeline;432prevpos = xlogpos;433
434return false;435}
436
437
438/* --- External API --- */
439
440/*
441* Maybe add a StreamOptions struct ?
442* Backup conn only needed to calculate stream_stop_timeout. Think about refactoring it.
443*/
444parray*445get_history_streaming(ConnectionOptions *conn_opt, TimeLineID tli, parray *backup_list)446{
447PGresult *res;448PGconn *conn;449char *history;450char query[128];451parray *result = NULL;452parray *tli_list = NULL;453timelineInfo *tlinfo = NULL;454int i,j;455
456snprintf(query, sizeof(query), "TIMELINE_HISTORY %u", tli);457
458/*459* Connect in replication mode to the server.
460*/
461conn = pgut_connect_replication(conn_opt->pghost,462conn_opt->pgport,463conn_opt->pgdatabase,464conn_opt->pguser,465false);466
467if (!conn)468return NULL;469
470res = PQexec(conn, query);471PQfinish(conn);472
473if (PQresultStatus(res) != PGRES_TUPLES_OK)474{475elog(WARNING, "Could not send replication command \"%s\": %s",476query, PQresultErrorMessage(res));477PQclear(res);478return NULL;479}480
481/*482* The response to TIMELINE_HISTORY is a single row result set
483* with two fields: filename and content
484*/
485
486if (PQnfields(res) != 2 || PQntuples(res) != 1)487{488elog(WARNING, "Unexpected response to TIMELINE_HISTORY command: "489"got %d rows and %d fields, expected %d rows and %d fields",490PQntuples(res), PQnfields(res), 1, 2);491PQclear(res);492return NULL;493}494
495history = pgut_strdup(PQgetvalue(res, 0, 1));496result = parse_tli_history_buffer(history, tli);497
498/* some cleanup */499pg_free(history);500PQclear(res);501
502if (result)503tlinfo = timelineInfoNew(tli);504else505return NULL;506
507/* transform TimeLineHistoryEntry into timelineInfo */508for (i = parray_num(result) -1; i >= 0; i--)509{510TimeLineHistoryEntry *tln = (TimeLineHistoryEntry *) parray_get(result, i);511
512tlinfo->parent_tli = tln->tli;513tlinfo->switchpoint = tln->end;514
515if (!tli_list)516tli_list = parray_new();517
518parray_append(tli_list, tlinfo);519
520/* Next tli */521tlinfo = timelineInfoNew(tln->tli);522
523/* oldest tli */524if (i == 0)525{526tlinfo->tli = tln->tli;527tlinfo->parent_tli = 0;528tlinfo->switchpoint = 0;529parray_append(tli_list, tlinfo);530}531}532
533/* link parent to child */534for (i = 0; i < parray_num(tli_list); i++)535{536tlinfo = (timelineInfo *) parray_get(tli_list, i);537
538for (j = 0; j < parray_num(tli_list); j++)539{540timelineInfo *tlinfo_parent = (timelineInfo *) parray_get(tli_list, j);541
542if (tlinfo->parent_tli == tlinfo_parent->tli)543{544tlinfo->parent_link = tlinfo_parent;545break;546}547}548}549
550/* add backups to each timeline info */551for (i = 0; i < parray_num(tli_list); i++)552{553tlinfo = parray_get(tli_list, i);554for (j = 0; j < parray_num(backup_list); j++)555{556pgBackup *backup = parray_get(backup_list, j);557if (tlinfo->tli == backup->tli)558{559if (tlinfo->backups == NULL)560tlinfo->backups = parray_new();561parray_append(tlinfo->backups, backup);562}563}564}565
566/* cleanup */567parray_walk(result, pg_free);568pg_free(result);569
570return tli_list;571}
572
573parray*574parse_tli_history_buffer(char *history, TimeLineID tli)575{
576char *curLine = history;577TimeLineHistoryEntry *entry;578TimeLineHistoryEntry *last_timeline = NULL;579parray *result = NULL;580
581/* Parse timeline history buffer string by string */582while (curLine)583{584char tempStr[1024];585char *nextLine = strchr(curLine, '\n');586int curLineLen = nextLine ? (nextLine-curLine) : strlen(curLine);587
588memcpy(tempStr, curLine, curLineLen);589tempStr[curLineLen] = '\0'; // NUL-terminate!590curLine = nextLine ? (nextLine+1) : NULL;591
592if (curLineLen > 0)593{594char *ptr;595TimeLineID tli;596uint32 switchpoint_hi;597uint32 switchpoint_lo;598int nfields;599
600for (ptr = tempStr; *ptr; ptr++)601{602if (!isspace((unsigned char) *ptr))603break;604}605if (*ptr == '\0' || *ptr == '#')606continue;607
608nfields = sscanf(tempStr, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);609
610if (nfields < 1)611{612/* expect a numeric timeline ID as first field of line */613elog(ERROR, "Syntax error in timeline history: \"%s\". Expected a numeric timeline ID.", tempStr);614}615if (nfields != 3)616elog(ERROR, "Syntax error in timeline history: \"%s\". Expected a transaction log switchpoint location.", tempStr);617
618if (last_timeline && tli <= last_timeline->tli)619elog(ERROR, "Timeline IDs must be in increasing sequence: \"%s\"", tempStr);620
621entry = pgut_new(TimeLineHistoryEntry);622entry->tli = tli;623entry->end = ((uint64) switchpoint_hi << 32) | switchpoint_lo;624
625last_timeline = entry;626/* Build list with newest item first */627if (!result)628result = parray_new();629parray_append(result, entry);630elog(VERBOSE, "parse_tli_history_buffer() found entry: tli = %X, end = %X/%X",631tli, switchpoint_hi, switchpoint_lo);632
633/* we ignore the remainder of each line */634}635}636
637return result;638}
639
640/*
641* Maybe add a StreamOptions struct ?
642* Backup conn only needed to calculate stream_stop_timeout. Think about refactoring it.
643*/
644void
645start_WAL_streaming(PGconn *backup_conn, char *stream_dst_path, ConnectionOptions *conn_opt,646XLogRecPtr startpos, TimeLineID starttli, bool is_backup)647{
648/* calculate crc only when running backup, catchup has no need for it */649do_crc = is_backup;650/* How long we should wait for streaming end after pg_stop_backup */651stream_stop_timeout = checkpoint_timeout(backup_conn);652//TODO Add a comment about this calculation653stream_stop_timeout = stream_stop_timeout + stream_stop_timeout * 0.1;654
655strlcpy(stream_thread_arg.basedir, stream_dst_path, sizeof(stream_thread_arg.basedir));656
657/*658* Connect in replication mode to the server.
659*/
660stream_thread_arg.conn = pgut_connect_replication(conn_opt->pghost,661conn_opt->pgport,662conn_opt->pgdatabase,663conn_opt->pguser,664true);665/* sanity check*/666IdentifySystem(&stream_thread_arg);667
668/* Set error exit code as default */669stream_thread_arg.ret = 1;670/* we must use startpos as start_lsn from start_backup */671stream_thread_arg.startpos = startpos;672stream_thread_arg.starttli = starttli;673
674thread_interrupted = false;675pthread_create(&stream_thread, NULL, StreamLog, &stream_thread_arg);676}
677
678/*
679* Wait for the completion of stream
680* append list of streamed xlog files
681* into backup_files_list (if it is not NULL)
682*/
683int
684wait_WAL_streaming_end(parray *backup_files_list)685{
686pthread_join(stream_thread, NULL);687
688if(backup_files_list != NULL)689parray_concat(backup_files_list, xlog_files_list);690parray_free(xlog_files_list);691return stream_thread_arg.ret;692}
693
694/* Append streamed WAL segment to filelist */
695void
696add_walsegment_to_filelist(parray *filelist, uint32 timeline, XLogRecPtr xlogpos, char *basedir, uint32 xlog_seg_size)697{
698XLogSegNo xlog_segno;699char wal_segment_name[MAXFNAMELEN];700char wal_segment_relpath[MAXPGPATH];701char wal_segment_fullpath[MAXPGPATH];702pgFile *file = NULL;703pgFile **existing_file = NULL;704
705GetXLogSegNo(xlogpos, xlog_segno, xlog_seg_size);706
707/*708* When xlogpos points to the zero offset (0/3000000),
709* it means that previous segment was just successfully streamed.
710* When xlogpos points to the positive offset,
711* then current segment is successfully streamed.
712*/
713if (WalSegmentOffset(xlogpos, xlog_seg_size) == 0)714xlog_segno--;715
716GetXLogFileName(wal_segment_name, timeline, xlog_segno, xlog_seg_size);717
718join_path_components(wal_segment_fullpath, basedir, wal_segment_name);719join_path_components(wal_segment_relpath, PG_XLOG_DIR, wal_segment_name);720
721file = pgFileNew(wal_segment_fullpath, wal_segment_relpath, false, 0, FIO_BACKUP_HOST);722
723/*724* Check if file is already in the list
725* stop_lsn segment can be added to this list twice, so
726* try not to add duplicates
727*/
728
729existing_file = (pgFile **) parray_bsearch(filelist, file, pgFileCompareRelPathWithExternal);730
731if (existing_file)732{733if (do_crc)734(*existing_file)->crc = pgFileGetCRC(wal_segment_fullpath, true, false);735(*existing_file)->write_size = xlog_seg_size;736(*existing_file)->uncompressed_size = xlog_seg_size;737
738return;739}740
741if (do_crc)742file->crc = pgFileGetCRC(wal_segment_fullpath, true, false);743
744/* Should we recheck it using stat? */745file->write_size = xlog_seg_size;746file->uncompressed_size = xlog_seg_size;747
748/* append file to filelist */749parray_append(filelist, file);750}
751
752/* Append history file to filelist */
753void
754add_history_file_to_filelist(parray *filelist, uint32 timeline, char *basedir)755{
756char filename[MAXFNAMELEN];757char fullpath[MAXPGPATH];758char relpath[MAXPGPATH];759pgFile *file = NULL;760
761/* Timeline 1 does not have a history file */762if (timeline == 1)763return;764
765snprintf(filename, lengthof(filename), "%08X.history", timeline);766join_path_components(fullpath, basedir, filename);767join_path_components(relpath, PG_XLOG_DIR, filename);768
769file = pgFileNew(fullpath, relpath, false, 0, FIO_BACKUP_HOST);770
771/* calculate crc */772if (do_crc)773file->crc = pgFileGetCRC(fullpath, true, false);774file->write_size = file->size;775file->uncompressed_size = file->size;776
777/* append file to filelist */778parray_append(filelist, file);779}
780