Skip to content

Commit 54d9c2b

Browse files
authored
fix(scheduledbackup): use label-based indexer instead of ownership (cloudnative-pg#9489)
The ScheduledBackup controller was using a broken field indexer on owner references to find child Backups for concurrency checking. The indexer checked for the wrong owner kind, making it non-functional. This fix replaces the owner reference-based indexer with a more robust approach: a field indexer on the ParentScheduledBackupLabel that is already set on all backups created by ScheduledBackup. This label is always present regardless of the backupOwnerReference configuration. Closes cloudnative-pg#9485 Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
1 parent 36b2304 commit 54d9c2b

File tree

1 file changed

+19
-25
lines changed

1 file changed

+19
-25
lines changed

internal/controller/scheduledbackup_controller.go

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ import (
4343
)
4444

4545
const (
46-
backupOwnerKey = ".metadata.controller"
46+
// backupParentScheduledBackupIndex is the field indexer key for the parent ScheduledBackup label
47+
backupParentScheduledBackupIndex = "metadata.labels." + utils.ParentScheduledBackupLabelName
4748

4849
// ImmediateBackupLabelName label is applied to backups to tell if a backup
4950
// is immediate or not
@@ -100,23 +101,19 @@ func (r *ScheduledBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ
100101
return ctrl.Result{}, nil
101102
}
102103

103-
// We are supposed to start a new backup. Let's extract
104-
// the list of backups we have already taken to see if anything
105-
// is running now
104+
// Check if any backups created by this ScheduledBackup are still running.
105+
// This provides concurrency control at the ScheduledBackup level.
106106
childBackups, err := r.GetChildBackups(ctx, scheduledBackup)
107107
if err != nil {
108108
contextLogger.Error(err,
109109
"Cannot extract the list of created backups")
110110
return ctrl.Result{}, err
111111
}
112112

113-
// We are supposed to start a new backup. Let's extract
114-
// the list of backups we have already taken to see if anything
115-
// is running now
116113
for _, backup := range childBackups {
117114
if !backup.Status.IsDone() {
118115
contextLogger.Info(
119-
"The system is already taking a scheduledBackup, retrying in 60 seconds",
116+
"The system is already taking a backup for this ScheduledBackup, retrying in 60 seconds",
120117
"backupName", backup.GetName(),
121118
"backupPhase", backup.Status.Phase)
122119
return ctrl.Result{RequeueAfter: time.Minute}, nil
@@ -312,7 +309,9 @@ func createBackup(
312309
return ctrl.Result{RequeueAfter: nextBackupTime.Sub(now)}, nil
313310
}
314311

315-
// GetChildBackups gets all the backups scheduled by a certain scheduler
312+
// GetChildBackups gets all the backups created by a certain ScheduledBackup
313+
// by querying the ParentScheduledBackupLabel using an efficient field indexer.
314+
// This works regardless of the backupOwnerReference configuration.
316315
func (r *ScheduledBackupReconciler) GetChildBackups(
317316
ctx context.Context,
318317
scheduledBackup apiv1.ScheduledBackup,
@@ -321,9 +320,9 @@ func (r *ScheduledBackupReconciler) GetChildBackups(
321320

322321
if err := r.List(ctx, &childBackups,
323322
client.InNamespace(scheduledBackup.Namespace),
324-
client.MatchingFields{backupOwnerKey: scheduledBackup.Name},
323+
client.MatchingFields{backupParentScheduledBackupIndex: scheduledBackup.GetName()},
325324
); err != nil {
326-
return nil, fmt.Errorf("unable to list child pods resource: %w", err)
325+
return nil, fmt.Errorf("unable to list child backups: %w", err)
327326
}
328327

329328
return childBackups.Items, nil
@@ -335,27 +334,22 @@ func (r *ScheduledBackupReconciler) SetupWithManager(
335334
mgr ctrl.Manager,
336335
maxConcurrentReconciles int,
337336
) error {
338-
// Create a new indexed field on backups. This field will be used to easily
339-
// find all the backups created by this controller
337+
// Create a field indexer on the parent ScheduledBackup label to efficiently
338+
// find all backups created by a ScheduledBackup, regardless of the
339+
// backupOwnerReference configuration.
340340
if err := mgr.GetFieldIndexer().IndexField(
341341
ctx,
342342
&apiv1.Backup{},
343-
backupOwnerKey, func(rawObj client.Object) []string {
344-
pod := rawObj.(*apiv1.Backup)
345-
owner := metav1.GetControllerOf(pod)
346-
if owner == nil {
343+
backupParentScheduledBackupIndex, func(rawObj client.Object) []string {
344+
backup := rawObj.(*apiv1.Backup)
345+
if backup.Labels == nil {
347346
return nil
348347
}
349348

350-
if owner.Kind != apiv1.BackupKind {
351-
return nil
349+
if parent, ok := backup.Labels[ParentScheduledBackupLabelName]; ok {
350+
return []string{parent}
352351
}
353-
354-
if owner.APIVersion != apiSGVString {
355-
return nil
356-
}
357-
358-
return []string{owner.Name}
352+
return nil
359353
}); err != nil {
360354
return err
361355
}

0 commit comments

Comments
 (0)