/src/postgres/src/backend/backup/basebackup_progress.c
Line | Count | Source (jump to first uncovered line) |
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-2025, 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 | | |
42 | | static const bbsink_ops bbsink_progress_ops = { |
43 | | .begin_backup = bbsink_progress_begin_backup, |
44 | | .begin_archive = bbsink_forward_begin_archive, |
45 | | .archive_contents = bbsink_progress_archive_contents, |
46 | | .end_archive = bbsink_progress_end_archive, |
47 | | .begin_manifest = bbsink_forward_begin_manifest, |
48 | | .manifest_contents = bbsink_forward_manifest_contents, |
49 | | .end_manifest = bbsink_forward_end_manifest, |
50 | | .end_backup = bbsink_forward_end_backup, |
51 | | .cleanup = bbsink_forward_cleanup |
52 | | }; |
53 | | |
54 | | /* |
55 | | * Create a new basebackup sink that performs progress tracking functions and |
56 | | * forwards data to a successor sink. |
57 | | */ |
58 | | bbsink * |
59 | | bbsink_progress_new(bbsink *next, bool estimate_backup_size) |
60 | 0 | { |
61 | 0 | bbsink *sink; |
62 | |
|
63 | 0 | Assert(next != NULL); |
64 | |
|
65 | 0 | sink = palloc0(sizeof(bbsink)); |
66 | 0 | *((const bbsink_ops **) &sink->bbs_ops) = &bbsink_progress_ops; |
67 | 0 | sink->bbs_next = next; |
68 | | |
69 | | /* |
70 | | * Report that a base backup is in progress, and set the total size of the |
71 | | * backup to -1, which will get translated to NULL. If we're estimating |
72 | | * the backup size, we'll insert the real estimate when we have it. |
73 | | */ |
74 | 0 | pgstat_progress_start_command(PROGRESS_COMMAND_BASEBACKUP, InvalidOid); |
75 | 0 | pgstat_progress_update_param(PROGRESS_BASEBACKUP_BACKUP_TOTAL, -1); |
76 | |
|
77 | 0 | return sink; |
78 | 0 | } |
79 | | |
80 | | /* |
81 | | * Progress reporting at start of backup. |
82 | | */ |
83 | | static void |
84 | | bbsink_progress_begin_backup(bbsink *sink) |
85 | 0 | { |
86 | 0 | const int index[] = { |
87 | 0 | PROGRESS_BASEBACKUP_PHASE, |
88 | 0 | PROGRESS_BASEBACKUP_BACKUP_TOTAL, |
89 | 0 | PROGRESS_BASEBACKUP_TBLSPC_TOTAL |
90 | 0 | }; |
91 | 0 | int64 val[3]; |
92 | | |
93 | | /* |
94 | | * Report that we are now streaming database files as a base backup. Also |
95 | | * advertise the number of tablespaces, and, if known, the estimated total |
96 | | * backup size. |
97 | | */ |
98 | 0 | val[0] = PROGRESS_BASEBACKUP_PHASE_STREAM_BACKUP; |
99 | 0 | if (sink->bbs_state->bytes_total_is_valid) |
100 | 0 | val[1] = sink->bbs_state->bytes_total; |
101 | 0 | else |
102 | 0 | val[1] = -1; |
103 | 0 | val[2] = list_length(sink->bbs_state->tablespaces); |
104 | 0 | pgstat_progress_update_multi_param(3, index, val); |
105 | | |
106 | | /* Delegate to next sink. */ |
107 | 0 | bbsink_forward_begin_backup(sink); |
108 | 0 | } |
109 | | |
110 | | /* |
111 | | * End-of archive progress reporting. |
112 | | */ |
113 | | static void |
114 | | bbsink_progress_end_archive(bbsink *sink) |
115 | 0 | { |
116 | | /* |
117 | | * We expect one archive per tablespace, so reaching the end of an archive |
118 | | * also means reaching the end of a tablespace. (Some day we might have a |
119 | | * reason to decouple these concepts.) |
120 | | * |
121 | | * If WAL is included in the backup, we'll mark the last tablespace |
122 | | * complete before the last archive is complete, so we need a guard here |
123 | | * to ensure that the number of tablespaces streamed doesn't exceed the |
124 | | * total. |
125 | | */ |
126 | 0 | if (sink->bbs_state->tablespace_num < list_length(sink->bbs_state->tablespaces)) |
127 | 0 | pgstat_progress_update_param(PROGRESS_BASEBACKUP_TBLSPC_STREAMED, |
128 | 0 | sink->bbs_state->tablespace_num + 1); |
129 | | |
130 | | /* Delegate to next sink. */ |
131 | 0 | bbsink_forward_end_archive(sink); |
132 | | |
133 | | /* |
134 | | * This is a convenient place to update the bbsink_state's notion of which |
135 | | * is the current tablespace. Note that the bbsink_state object is shared |
136 | | * across all bbsink objects involved, but we're the outermost one and |
137 | | * this is the very last thing we do. |
138 | | */ |
139 | 0 | sink->bbs_state->tablespace_num++; |
140 | 0 | } |
141 | | |
142 | | /* |
143 | | * Handle progress tracking for new archive contents. |
144 | | * |
145 | | * Increment the counter for the amount of data already streamed |
146 | | * by the given number of bytes, and update the progress report for |
147 | | * pg_stat_progress_basebackup. |
148 | | */ |
149 | | static void |
150 | | bbsink_progress_archive_contents(bbsink *sink, size_t len) |
151 | 0 | { |
152 | 0 | bbsink_state *state = sink->bbs_state; |
153 | 0 | const int index[] = { |
154 | 0 | PROGRESS_BASEBACKUP_BACKUP_STREAMED, |
155 | 0 | PROGRESS_BASEBACKUP_BACKUP_TOTAL |
156 | 0 | }; |
157 | 0 | int64 val[2]; |
158 | 0 | int nparam = 0; |
159 | | |
160 | | /* First update bbsink_state with # of bytes done. */ |
161 | 0 | state->bytes_done += len; |
162 | | |
163 | | /* Now forward to next sink. */ |
164 | 0 | bbsink_forward_archive_contents(sink, len); |
165 | | |
166 | | /* Prepare to set # of bytes done for command progress reporting. */ |
167 | 0 | val[nparam++] = state->bytes_done; |
168 | | |
169 | | /* |
170 | | * We may also want to update # of total bytes, to avoid overflowing past |
171 | | * 100% or the full size. This may make the total size number change as we |
172 | | * approach the end of the backup (the estimate will always be wrong if |
173 | | * WAL is included), but that's better than having the done column be |
174 | | * bigger than the total. |
175 | | */ |
176 | 0 | if (state->bytes_total_is_valid && state->bytes_done > state->bytes_total) |
177 | 0 | val[nparam++] = state->bytes_done; |
178 | |
|
179 | 0 | pgstat_progress_update_multi_param(nparam, index, val); |
180 | 0 | } |
181 | | |
182 | | /* |
183 | | * Advertise that we are waiting for the start-of-backup checkpoint. |
184 | | */ |
185 | | void |
186 | | basebackup_progress_wait_checkpoint(void) |
187 | 0 | { |
188 | 0 | pgstat_progress_update_param(PROGRESS_BASEBACKUP_PHASE, |
189 | 0 | PROGRESS_BASEBACKUP_PHASE_WAIT_CHECKPOINT); |
190 | 0 | } |
191 | | |
192 | | /* |
193 | | * Advertise that we are estimating the backup size. |
194 | | */ |
195 | | void |
196 | | basebackup_progress_estimate_backup_size(void) |
197 | 0 | { |
198 | 0 | pgstat_progress_update_param(PROGRESS_BASEBACKUP_PHASE, |
199 | 0 | PROGRESS_BASEBACKUP_PHASE_ESTIMATE_BACKUP_SIZE); |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * Advertise that we are waiting for WAL archiving at end-of-backup. |
204 | | */ |
205 | | void |
206 | | basebackup_progress_wait_wal_archive(bbsink_state *state) |
207 | 0 | { |
208 | 0 | const int index[] = { |
209 | 0 | PROGRESS_BASEBACKUP_PHASE, |
210 | 0 | PROGRESS_BASEBACKUP_TBLSPC_STREAMED |
211 | 0 | }; |
212 | 0 | int64 val[2]; |
213 | | |
214 | | /* |
215 | | * We report having finished all tablespaces at this point, even if the |
216 | | * archive for the main tablespace is still open, because what's going to |
217 | | * be added is WAL files, not files that are really from the main |
218 | | * tablespace. |
219 | | */ |
220 | 0 | val[0] = PROGRESS_BASEBACKUP_PHASE_WAIT_WAL_ARCHIVE; |
221 | 0 | val[1] = list_length(state->tablespaces); |
222 | 0 | pgstat_progress_update_multi_param(2, index, val); |
223 | 0 | } |
224 | | |
225 | | /* |
226 | | * Advertise that we are transferring WAL files into the final archive. |
227 | | */ |
228 | | void |
229 | | basebackup_progress_transfer_wal(void) |
230 | 0 | { |
231 | 0 | pgstat_progress_update_param(PROGRESS_BASEBACKUP_PHASE, |
232 | 0 | PROGRESS_BASEBACKUP_PHASE_TRANSFER_WAL); |
233 | 0 | } |
234 | | |
235 | | /* |
236 | | * Advertise that we are no longer performing a backup. |
237 | | */ |
238 | | void |
239 | | basebackup_progress_done(void) |
240 | 0 | { |
241 | 0 | pgstat_progress_end_command(); |
242 | 0 | } |