/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 | } |