1616
1717static void delete_walfiles (XLogRecPtr oldest_lsn ,TimeLineID oldest_tli ,
1818uint32 xlog_seg_size );
19- static void do_retention_internal (void );
19+ static void do_retention_internal (parray * backup_list ,parray * to_keep_list ,
20+ parray * to_purge_list );
21+ static void do_retention_merge (parray * backup_list ,parray * to_keep_list ,
22+ parray * to_purge_list );
23+ static void do_retention_purge (parray * to_keep_list ,parray * to_purge_list );
2024static void do_retention_wal (void );
2125
2226static bool backup_deleted = false;/* At least one backup was deleted */
@@ -108,8 +112,15 @@ do_delete(time_t backup_id)
108112
109113int do_retention (void )
110114{
115+ parray * backup_list = NULL ;
116+ parray * to_keep_list = parray_new ();
117+ parray * to_purge_list = parray_new ();
118+
111119bool retention_is_set = false;/* At least one retention policy is set */
112120
121+ /* Get a complete list of backups. */
122+ backup_list = catalog_get_backup_list (INVALID_BACKUP_ID );
123+
113124if (delete_expired || merge_expired )
114125{
115126if (instance_config .retention_redundancy > 0 )
@@ -131,9 +142,17 @@ int do_retention(void)
131142retention_is_set = true;
132143}
133144
134- if (retention_is_set && ((delete_expired || merge_expired )|| dry_run ))
135- do_retention_internal ();
145+ /* show fancy message */
146+ if (retention_is_set )
147+ do_retention_internal (backup_list ,to_keep_list ,to_purge_list );
148+
149+ if (merge_expired && !dry_run )
150+ do_retention_merge (backup_list ,to_keep_list ,to_purge_list );
151+
152+ if (delete_expired && !dry_run )
153+ do_retention_purge (to_keep_list ,to_purge_list );
136154
155+ /* TODO: some sort of dry run for delete_wal */
137156if (delete_wal && !dry_run )
138157do_retention_wal ();
139158
@@ -145,6 +164,12 @@ int do_retention(void)
145164else
146165elog (INFO ,"There are no backups to delete by retention policy" );
147166
167+ /* Cleanup */
168+ parray_walk (backup_list ,pgBackupFree );
169+ parray_free (backup_list );
170+ parray_free (to_keep_list );
171+ parray_free (to_purge_list );
172+
148173return 0 ;
149174
150175}
@@ -160,13 +185,9 @@ int do_retention(void)
160185 * but if invalid backup is not guarded by retention - it is removed
161186 */
162187static void
163- do_retention_internal (void )
188+ do_retention_internal (parray * backup_list , parray * to_keep_list , parray * to_purge_list )
164189{
165- parray * backup_list = NULL ;
166- parray * to_purge_list = parray_new ();
167- parray * to_keep_list = parray_new ();
168190int i ;
169- int j ;
170191time_t current_time ;
171192bool backup_list_is_empty = false;
172193
@@ -178,8 +199,7 @@ do_retention_internal(void)
178199/* For fancy reporting */
179200float actual_window = 0 ;
180201
181- /* Get a complete list of backups. */
182- backup_list = catalog_get_backup_list (INVALID_BACKUP_ID );
202+ /* sanity */
183203if (parray_num (backup_list )== 0 )
184204backup_list_is_empty = true;
185205
@@ -257,36 +277,66 @@ do_retention_internal(void)
257277parray_append (to_purge_list ,backup );
258278continue ;
259279}
280+ }
281+
282+ /* sort keep_list and purge list */
283+ parray_qsort (to_keep_list ,pgBackupCompareIdDesc );
284+ parray_qsort (to_purge_list ,pgBackupCompareIdDesc );
285+
286+ /* FULL
287+ * PAGE
288+ * PAGE <- Only such backups must go into keep list
289+ ---------retention window ----
290+ * PAGE
291+ * FULL
292+ * PAGE
293+ * FULL
294+ */
295+
296+ for (i = 0 ;i < parray_num (backup_list );i ++ )
297+ {
298+
299+ pgBackup * backup = (pgBackup * )parray_get (backup_list ,i );
260300
261301/* Do not keep invalid backups by retention */
262302if (backup -> status != BACKUP_STATUS_OK &&
263303backup -> status != BACKUP_STATUS_DONE )
264304continue ;
265305
266- elog (VERBOSE ,"Mark backup %s for retention." ,base36enc (backup -> start_time ));
267- parray_append (to_keep_list ,backup );
306+ if (backup -> backup_mode == BACKUP_MODE_FULL )
307+ continue ;
308+
309+ /* orphan backup cannot be in keep list */
310+ if (!backup -> parent_backup_link )
311+ continue ;
312+
313+ if (parray_bsearch (to_purge_list ,backup ,pgBackupCompareIdDesc ))
314+ continue ;
315+
316+ /* if parent in purge_list, add backup to keep list */
317+ if (parray_bsearch (to_purge_list ,
318+ backup -> parent_backup_link ,
319+ pgBackupCompareIdDesc ))
320+ {
321+ /* make keep list a bit sparse */
322+ parray_append (to_keep_list ,backup );
323+ continue ;
324+ }
268325}
269326
270327/* Message about retention state of backups
271328 * TODO: Float is ugly, rewrite somehow.
272329 */
273330
274- /* sort keep_list and purge list */
275- parray_qsort (to_keep_list ,pgBackupCompareIdDesc );
276- parray_qsort (to_purge_list ,pgBackupCompareIdDesc );
277-
278331cur_full_backup_num = 1 ;
279332for (i = 0 ;i < parray_num (backup_list );i ++ )
280333{
281- char * action = "Ignore " ;
334+ char * action = "Keep " ;
282335
283336pgBackup * backup = (pgBackup * )parray_get (backup_list ,i );
284337
285- if (parray_bsearch (to_keep_list ,backup ,pgBackupCompareIdDesc ))
286- action = "Keep" ;
287-
288338if (parray_bsearch (to_purge_list ,backup ,pgBackupCompareIdDesc ))
289- action = "Keep " ;
339+ action = "Purge " ;
290340
291341if (backup -> recovery_time == 0 )
292342actual_window = 0 ;
@@ -305,33 +355,21 @@ do_retention_internal(void)
305355if (backup -> backup_mode == BACKUP_MODE_FULL )
306356cur_full_backup_num ++ ;
307357}
358+ }
308359
309- if (dry_run )
310- gotofinish ;
311-
312- /* Extreme example of keep_list
313- *
314- *FULLc <- keep
315- *PAGEb2 <- keep
316- *PAGEb1 <- keep
317- *PAGEa2 <- keep
318- *PAGEa1 <- keep
319- *FULLb <- in purge_list
320- *FULLa <- in purge_list
321- */
322-
323- /* Go to purge */
324- if (delete_expired && !merge_expired )
325- gotopurge ;
360+ static void
361+ do_retention_merge (parray * backup_list ,parray * to_keep_list ,parray * to_purge_list )
362+ {
363+ int i ;
364+ int j ;
326365
327366/* IMPORTANT: we can merge to only those FULL backup, that is NOT
328367 * guarded by retention and final targets of such merges must be
329368 * incremental backup that is guarded by retention !!!
330369 *
331- *
332370 * PAGE4 E
333371 * PAGE3 D
334- * --------retention window ---
372+ --------retention window ---
335373 * PAGE2 C
336374 * PAGE1 B
337375 * FULL A
@@ -352,32 +390,9 @@ do_retention_internal(void)
352390
353391/* keep list may shrink during merge */
354392if (!keep_backup )
355- break ;
356-
357- elog (INFO ,"Consider backup %s for merge" ,base36enc (keep_backup -> start_time ));
358-
359- /* In keep list we are looking for incremental backups */
360- if (keep_backup -> backup_mode == BACKUP_MODE_FULL )
361- continue ;
362-
363- /* Retain orphan backups in keep_list */
364- if (!keep_backup -> parent_backup_link )
365393continue ;
366394
367- /* If parent of current backup is also in keep list, go to the next */
368- if (parray_bsearch (to_keep_list ,
369- keep_backup -> parent_backup_link ,
370- pgBackupCompareIdDesc ))
371- {
372- /* make keep list a bit sparse */
373- elog (LOG ,"Sparsing keep list, remove %s" ,base36enc (keep_backup -> start_time ));
374- parray_remove (to_keep_list ,i );
375- i -- ;
376- continue ;
377- }
378-
379- elog (INFO ,"Lookup parents for backup %s" ,
380- base36enc (keep_backup -> start_time ));
395+ elog (INFO ,"Consider backup %s for merge" ,base36enc (keep_backup -> start_time ));
381396
382397/* Got valid incremental backup, find its FULL ancestor */
383398full_backup = find_parent_full_backup (keep_backup );
@@ -473,9 +488,7 @@ do_retention_internal(void)
473488
474489/* Try to remove merged incremental backup from both keep and purge lists */
475490parray_rm (to_purge_list ,from_backup ,pgBackupCompareId );
476-
477- if (parray_rm (to_keep_list ,from_backup ,pgBackupCompareId )&& (i >=0 ))
478- i -- ;
491+ parray_set (to_keep_list ,i ,NULL );
479492}
480493
481494/* Cleanup */
@@ -484,11 +497,13 @@ do_retention_internal(void)
484497
485498elog (INFO ,"Retention merging finished" );
486499
487- if (!delete_expired )
488- gotofinish ;
500+ }
489501
490- /* Do purging here */
491- purge :
502+ static void
503+ do_retention_purge (parray * to_keep_list ,parray * to_purge_list )
504+ {
505+ int i ;
506+ int j ;
492507
493508/* Remove backups by retention policy. Retention policy is configured by
494509 * retention_redundancy and retention_window
@@ -517,6 +532,10 @@ do_retention_internal(void)
517532
518533pgBackup * keep_backup = (pgBackup * )parray_get (to_keep_list ,i );
519534
535+ /* item could have been nullified in merge */
536+ if (!keep_backup )
537+ continue ;
538+
520539/* Full backup cannot be a descendant */
521540if (keep_backup -> backup_mode == BACKUP_MODE_FULL )
522541continue ;
@@ -557,14 +576,6 @@ do_retention_internal(void)
557576backup_deleted = true;
558577
559578}
560-
561- finish :
562- /* Cleanup */
563- parray_walk (backup_list ,pgBackupFree );
564- parray_free (backup_list );
565- parray_free (to_keep_list );
566- parray_free (to_purge_list );
567-
568579}
569580
570581/* Purge WAL */