Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/backup/basebackup_progress.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * basebackup_progress.c
4
 *    Basebackup sink implementing progress tracking, including but not
5
 *    limited to command progress reporting.
6
 *
7
 * This should be used even if the PROGRESS option to the replication
8
 * command BASE_BACKUP is not specified. Without that option, we won't
9
 * have tallied up the size of the files that are going to need to be
10
 * backed up, but we can still report to the command progress reporting
11
 * facility how much data we've processed.
12
 *
13
 * Moreover, we also use this as a convenient place to update certain
14
 * fields of the bbsink_state. That work is accurately described as
15
 * keeping track of our progress, but it's not just for introspection.
16
 * We need those fields to be updated properly in order for base backups
17
 * to work.
18
 *
19
 * This particular basebackup sink requires extra callbacks that most base
20
 * backup sinks don't. Rather than cramming those into the interface, we just
21
 * have a few extra functions here that basebackup.c can call. (We could put
22
 * the logic directly into that file as it's fairly simple, but it seems
23
 * cleaner to have everything related to progress reporting in one place.)
24
 *
25
 * Portions Copyright (c) 2010-2026, PostgreSQL Global Development Group
26
 *
27
 * IDENTIFICATION
28
 *    src/backend/backup/basebackup_progress.c
29
 *
30
 *-------------------------------------------------------------------------
31
 */
32
#include "postgres.h"
33
34
#include "backup/basebackup_sink.h"
35
#include "commands/progress.h"
36
#include "pgstat.h"
37
38
static void bbsink_progress_begin_backup(bbsink *sink);
39
static void bbsink_progress_archive_contents(bbsink *sink, size_t len);
40
static void bbsink_progress_end_archive(bbsink *sink);
41
static void bbsink_progress_cleanup(bbsink *sink);
42
43
static const bbsink_ops bbsink_progress_ops = {
44
  .begin_backup = bbsink_progress_begin_backup,
45
  .begin_archive = bbsink_forward_begin_archive,
46
  .archive_contents = bbsink_progress_archive_contents,
47
  .end_archive = bbsink_progress_end_archive,
48
  .begin_manifest = bbsink_forward_begin_manifest,
49
  .manifest_contents = bbsink_forward_manifest_contents,
50
  .end_manifest = bbsink_forward_end_manifest,
51
  .end_backup = bbsink_forward_end_backup,
52
  .cleanup = bbsink_progress_cleanup
53
};
54
55
/*
56
 * Create a new basebackup sink that performs progress tracking functions and
57
 * forwards data to a successor sink.
58
 */
59
bbsink *
60
bbsink_progress_new(bbsink *next, bool estimate_backup_size, bool incremental)
61
0
{
62
0
  bbsink     *sink;
63
64
0
  Assert(next != NULL);
65
66
0
  sink = palloc0_object(bbsink);
67
0
  *((const bbsink_ops **) &sink->bbs_ops) = &bbsink_progress_ops;
68
0
  sink->bbs_next = next;
69
70
  /*
71
   * Report that a base backup is in progress, and set the total size of the
72
   * backup to -1, which will get translated to NULL. If we're estimating
73
   * the backup size, we'll insert the real estimate when we have it. Also,
74
   * the backup type is set.
75
   */
76
0
  pgstat_progress_start_command(PROGRESS_COMMAND_BASEBACKUP, InvalidOid);
77
0
  pgstat_progress_update_param(PROGRESS_BASEBACKUP_BACKUP_TOTAL, -1);
78
0
  pgstat_progress_update_param(PROGRESS_BASEBACKUP_BACKUP_TYPE,
79
0
                 incremental
80
0
                 ? PROGRESS_BASEBACKUP_BACKUP_TYPE_INCREMENTAL
81
0
                 : PROGRESS_BASEBACKUP_BACKUP_TYPE_FULL);
82
83
0
  return sink;
84
0
}
85
86
/*
87
 * Progress reporting at start of backup.
88
 */
89
static void
90
bbsink_progress_begin_backup(bbsink *sink)
91
0
{
92
0
  const int index[] = {
93
0
    PROGRESS_BASEBACKUP_PHASE,
94
0
    PROGRESS_BASEBACKUP_BACKUP_TOTAL,
95
0
    PROGRESS_BASEBACKUP_TBLSPC_TOTAL
96
0
  };
97
0
  int64   val[3];
98
99
  /*
100
   * Report that we are now streaming database files as a base backup. Also
101
   * advertise the number of tablespaces, and, if known, the estimated total
102
   * backup size.
103
   */
104
0
  val[0] = PROGRESS_BASEBACKUP_PHASE_STREAM_BACKUP;
105
0
  if (sink->bbs_state->bytes_total_is_valid)
106
0
    val[1] = sink->bbs_state->bytes_total;
107
0
  else
108
0
    val[1] = -1;
109
0
  val[2] = list_length(sink->bbs_state->tablespaces);
110
0
  pgstat_progress_update_multi_param(3, index, val);
111
112
  /* Delegate to next sink. */
113
0
  bbsink_forward_begin_backup(sink);
114
0
}
115
116
/*
117
 * End-of archive progress reporting.
118
 */
119
static void
120
bbsink_progress_end_archive(bbsink *sink)
121
0
{
122
  /*
123
   * We expect one archive per tablespace, so reaching the end of an archive
124
   * also means reaching the end of a tablespace. (Some day we might have a
125
   * reason to decouple these concepts.)
126
   *
127
   * If WAL is included in the backup, we'll mark the last tablespace
128
   * complete before the last archive is complete, so we need a guard here
129
   * to ensure that the number of tablespaces streamed doesn't exceed the
130
   * total.
131
   */
132
0
  if (sink->bbs_state->tablespace_num < list_length(sink->bbs_state->tablespaces))
133
0
    pgstat_progress_update_param(PROGRESS_BASEBACKUP_TBLSPC_STREAMED,
134
0
                   sink->bbs_state->tablespace_num + 1);
135
136
  /* Delegate to next sink. */
137
0
  bbsink_forward_end_archive(sink);
138
139
  /*
140
   * This is a convenient place to update the bbsink_state's notion of which
141
   * is the current tablespace. Note that the bbsink_state object is shared
142
   * across all bbsink objects involved, but we're the outermost one and
143
   * this is the very last thing we do.
144
   */
145
0
  sink->bbs_state->tablespace_num++;
146
0
}
147
148
/*
149
 * Handle progress tracking for new archive contents.
150
 *
151
 * Increment the counter for the amount of data already streamed
152
 * by the given number of bytes, and update the progress report for
153
 * pg_stat_progress_basebackup.
154
 */
155
static void
156
bbsink_progress_archive_contents(bbsink *sink, size_t len)
157
0
{
158
0
  bbsink_state *state = sink->bbs_state;
159
0
  const int index[] = {
160
0
    PROGRESS_BASEBACKUP_BACKUP_STREAMED,
161
0
    PROGRESS_BASEBACKUP_BACKUP_TOTAL
162
0
  };
163
0
  int64   val[2];
164
0
  int     nparam = 0;
165
166
  /* First update bbsink_state with # of bytes done. */
167
0
  state->bytes_done += len;
168
169
  /* Now forward to next sink. */
170
0
  bbsink_forward_archive_contents(sink, len);
171
172
  /* Prepare to set # of bytes done for command progress reporting. */
173
0
  val[nparam++] = state->bytes_done;
174
175
  /*
176
   * We may also want to update # of total bytes, to avoid overflowing past
177
   * 100% or the full size. This may make the total size number change as we
178
   * approach the end of the backup (the estimate will always be wrong if
179
   * WAL is included), but that's better than having the done column be
180
   * bigger than the total.
181
   */
182
0
  if (state->bytes_total_is_valid && state->bytes_done > state->bytes_total)
183
0
    val[nparam++] = state->bytes_done;
184
185
0
  pgstat_progress_update_multi_param(nparam, index, val);
186
0
}
187
188
/*
189
 * Clean up progress reporting.
190
 */
191
static void
192
bbsink_progress_cleanup(bbsink *sink)
193
0
{
194
0
  pgstat_progress_end_command();
195
0
  bbsink_forward_cleanup(sink);
196
0
}
197
198
/*
199
 * Advertise that we are waiting for the start-of-backup checkpoint.
200
 */
201
void
202
basebackup_progress_wait_checkpoint(void)
203
0
{
204
0
  pgstat_progress_update_param(PROGRESS_BASEBACKUP_PHASE,
205
0
                 PROGRESS_BASEBACKUP_PHASE_WAIT_CHECKPOINT);
206
0
}
207
208
/*
209
 * Advertise that we are estimating the backup size.
210
 */
211
void
212
basebackup_progress_estimate_backup_size(void)
213
0
{
214
0
  pgstat_progress_update_param(PROGRESS_BASEBACKUP_PHASE,
215
0
                 PROGRESS_BASEBACKUP_PHASE_ESTIMATE_BACKUP_SIZE);
216
0
}
217
218
/*
219
 * Advertise that we are waiting for WAL archiving at end-of-backup.
220
 */
221
void
222
basebackup_progress_wait_wal_archive(bbsink_state *state)
223
0
{
224
0
  const int index[] = {
225
0
    PROGRESS_BASEBACKUP_PHASE,
226
0
    PROGRESS_BASEBACKUP_TBLSPC_STREAMED
227
0
  };
228
0
  int64   val[2];
229
230
  /*
231
   * We report having finished all tablespaces at this point, even if the
232
   * archive for the main tablespace is still open, because what's going to
233
   * be added is WAL files, not files that are really from the main
234
   * tablespace.
235
   */
236
0
  val[0] = PROGRESS_BASEBACKUP_PHASE_WAIT_WAL_ARCHIVE;
237
0
  val[1] = list_length(state->tablespaces);
238
0
  pgstat_progress_update_multi_param(2, index, val);
239
0
}
240
241
/*
242
 * Advertise that we are transferring WAL files into the final archive.
243
 */
244
void
245
basebackup_progress_transfer_wal(void)
246
0
{
247
0
  pgstat_progress_update_param(PROGRESS_BASEBACKUP_PHASE,
248
0
                 PROGRESS_BASEBACKUP_PHASE_TRANSFER_WAL);
249
0
}