Skip to content

Commit ec2a5a2

Browse files
mantissahzderekbit
authored andcommitted
fix(backup): delete duplicated backup volumes
ref: longhorn/longhorn 11154 Signed-off-by: James Lu <james.lu@suse.com>
1 parent 596d573 commit ec2a5a2

2 files changed

Lines changed: 109 additions & 19 deletions

File tree

controller/backup_target_controller.go

Lines changed: 106 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,16 @@ func (btc *BackupTargetController) reconcile(name string) (err error) {
433433
}
434434
}()
435435

436+
// clean up invalid backup volumes that are created during split-brain
437+
// https://github.com/longhorn/longhorn/issues/11154
438+
clusterVolumeBVMap, duplicatedBackupVolumeSet, err := btc.getClusterBVsDuplicatedBVs(backupTarget)
439+
if err != nil {
440+
return err
441+
}
442+
if err := btc.cleanupDuplicateBackupVolumeForBackupTarget(backupTarget, duplicatedBackupVolumeSet); err != nil {
443+
return err
444+
}
445+
436446
if backupTarget.Spec.BackupTargetURL == "" {
437447
stopTimer(backupTarget.Name)
438448

@@ -463,7 +473,7 @@ func (btc *BackupTargetController) reconcile(name string) (err error) {
463473
longhorn.BackupTargetConditionTypeUnavailable, longhorn.ConditionStatusFalse,
464474
"", "")
465475

466-
if err = btc.syncBackupVolume(backupTarget, info.backupStoreBackupVolumeNames, syncTime, log); err != nil {
476+
if err = btc.syncBackupVolume(backupTarget, info.backupStoreBackupVolumeNames, clusterVolumeBVMap, syncTime, log); err != nil {
467477
return err
468478
}
469479

@@ -546,20 +556,54 @@ func (btc *BackupTargetController) getInfoFromBackupStore(backupTarget *longhorn
546556
return info, nil
547557
}
548558

549-
func (btc *BackupTargetController) syncBackupVolume(backupTarget *longhorn.BackupTarget, backupStoreBackupVolumeNames []string, syncTime metav1.Time, log logrus.FieldLogger) error {
550-
backupStoreBackupVolumes := sets.New[string](backupStoreBackupVolumeNames...)
559+
func (btc *BackupTargetController) getClusterBVsDuplicatedBVs(backupTarget *longhorn.BackupTarget) (map[string]*longhorn.BackupVolume, sets.Set[string], error) {
560+
log := getLoggerForBackupTarget(btc.logger, backupTarget)
561+
backupTargetName := backupTarget.Name
551562

552-
// Get a list of all the backup volumes that exist as custom resources in the cluster
553-
clusterBackupVolumes, err := btc.ds.ListBackupVolumesWithBackupTargetNameRO(backupTarget.Name)
563+
// Get a list of the backup volumes of the backup target that exist as custom resources in the cluster
564+
backupVolumeList, err := btc.ds.ListBackupVolumesWithBackupTargetNameRO(backupTargetName)
554565
if err != nil {
555-
return err
566+
return nil, nil, err
556567
}
557568

558-
clusterVolumeBVMap := make(map[string]*longhorn.BackupVolume, len(clusterBackupVolumes))
569+
duplicateBackupVolumeSet := sets.New[string]()
570+
volumeBVMap := make(map[string]*longhorn.BackupVolume, len(backupVolumeList))
571+
for _, bv := range backupVolumeList {
572+
if bv.Spec.BackupTargetName == "" {
573+
log.WithField("backupVolume", bv.Name).Debug("spec.backupTargetName is empty")
574+
duplicateBackupVolumeSet.Insert(bv.Name)
575+
continue
576+
}
577+
if bv.Spec.VolumeName == "" {
578+
log.WithField("backupVolume", bv.Name).Debug("spec.volumeName is empty")
579+
duplicateBackupVolumeSet.Insert(bv.Name)
580+
continue
581+
}
582+
if bv.Spec.BackupTargetName != backupTargetName {
583+
log.WithField("backupVolume", bv.Name).Debugf("spec.backupTargetName %v is different from label backup-target", bv.Spec.BackupTargetName)
584+
duplicateBackupVolumeSet.Insert(bv.Name)
585+
continue
586+
}
587+
if existingBV, exists := volumeBVMap[bv.Spec.VolumeName]; exists {
588+
if existingBV.CreationTimestamp.Before(&bv.CreationTimestamp) {
589+
log.WithField("backupVolume", bv.Name).Warnf("Found duplicated BackupVolume with volume name %s", bv.Spec.VolumeName)
590+
duplicateBackupVolumeSet.Insert(bv.Name)
591+
continue
592+
}
593+
log.WithField("backupVolume", existingBV.Name).Warnf("Found duplicated BackupVolume with volume name %s", existingBV.Spec.VolumeName)
594+
duplicateBackupVolumeSet.Insert(existingBV.Name)
595+
}
596+
volumeBVMap[bv.Spec.VolumeName] = bv
597+
}
598+
599+
return volumeBVMap, duplicateBackupVolumeSet, nil
600+
}
601+
602+
func (btc *BackupTargetController) syncBackupVolume(backupTarget *longhorn.BackupTarget, backupStoreBackupVolumeNames []string, clusterVolumeBVMap map[string]*longhorn.BackupVolume, syncTime metav1.Time, log logrus.FieldLogger) error {
603+
backupStoreBackupVolumes := sets.New[string](backupStoreBackupVolumeNames...)
559604
clusterBackupVolumesSet := sets.New[string]()
560-
for _, bv := range clusterBackupVolumes {
605+
for _, bv := range clusterVolumeBVMap {
561606
clusterBackupVolumesSet.Insert(bv.Spec.VolumeName)
562-
clusterVolumeBVMap[bv.Spec.VolumeName] = bv
563607
}
564608

565609
// TODO: add a unit test
@@ -580,12 +624,20 @@ func (btc *BackupTargetController) syncBackupVolume(backupTarget *longhorn.Backu
580624

581625
// Update the BackupVolume CR spec.syncRequestAt to request the
582626
// backup_volume_controller to reconcile the BackupVolume CR
583-
for backupVolumeName, backupVolume := range clusterBackupVolumes {
627+
multiError := util.NewMultiError()
628+
for volumeName, backupVolume := range clusterVolumeBVMap {
629+
if !backupStoreBackupVolumes.Has(volumeName) {
630+
continue
631+
}
584632
backupVolume.Spec.SyncRequestedAt = syncTime
585-
if _, err = btc.ds.UpdateBackupVolume(backupVolume); err != nil && !apierrors.IsConflict(errors.Cause(err)) {
586-
log.WithError(err).Errorf("Failed to update backup volume %s spec", backupVolumeName)
633+
if _, err := btc.ds.UpdateBackupVolume(backupVolume); err != nil && !apierrors.IsConflict(errors.Cause(err)) {
634+
log.WithError(err).Errorf("Failed to update backup volume %s", backupVolume.Name)
635+
multiError.Append(util.NewMultiError(fmt.Sprintf("%v: %v", backupVolume.Name, err)))
587636
}
588637
}
638+
if len(multiError) > 0 {
639+
return fmt.Errorf("failed to update backup volumes: %v", multiError.Join())
640+
}
589641

590642
return nil
591643
}
@@ -596,7 +648,7 @@ func (btc *BackupTargetController) pullBackupVolumeFromBackupTarget(backupTarget
596648
log.Infof("Found %d backup volumes in the backup target that do not exist in the cluster and need to be pulled", count)
597649
}
598650
for remoteVolumeName := range backupVolumesToPull {
599-
backupVolumeName := types.GetBackupVolumeNameFromVolumeName(remoteVolumeName)
651+
backupVolumeName := types.GetBackupVolumeNameFromVolumeName(remoteVolumeName, backupTarget.Name)
600652
backupVolume := &longhorn.BackupVolume{
601653
ObjectMeta: metav1.ObjectMeta{
602654
Name: backupVolumeName,
@@ -624,6 +676,7 @@ func (btc *BackupTargetController) cleanupBackupVolumeNotExistOnBackupTarget(clu
624676
log.Infof("Found %d backup volumes in the backup target that do not exist in the cluster and need to be deleted from the cluster", count)
625677
}
626678

679+
multiError := util.NewMultiError()
627680
for volumeName := range backupVolumesToDelete {
628681
bv, exists := clusterVolumeBVMap[volumeName]
629682
if !exists {
@@ -633,14 +686,50 @@ func (btc *BackupTargetController) cleanupBackupVolumeNotExistOnBackupTarget(clu
633686

634687
backupVolumeName := bv.Name
635688
log.WithField("backupVolume", backupVolumeName).Info("Deleting BackupVolume not exist in backupstore")
636-
if err = datastore.AddBackupVolumeDeleteCustomResourceOnlyLabel(btc.ds, backupVolumeName); err != nil {
637-
return errors.Wrapf(err, "failed to add label delete-custom-resource-only to Backupvolume %s", backupVolumeName)
689+
if err := btc.deleteBackupVolumeCROnly(backupVolumeName, log); err != nil {
690+
if apierrors.IsNotFound(err) {
691+
continue
692+
}
693+
multiError.Append(util.NewMultiError(fmt.Sprintf("%v: %v", backupVolumeName, err)))
638694
}
639-
if err = btc.ds.DeleteBackupVolume(backupVolumeName); err != nil {
640-
return errors.Wrapf(err, "failed to delete backup volume %s from cluster", backupVolumeName)
695+
}
696+
697+
if len(multiError) > 0 {
698+
return fmt.Errorf("failed to delete backup volumes from cluster: %v", multiError.Join())
699+
}
700+
return nil
701+
}
702+
703+
func (btc *BackupTargetController) deleteBackupVolumeCROnly(backupVolumeName string, log logrus.FieldLogger) error {
704+
if err := datastore.AddBackupVolumeDeleteCustomResourceOnlyLabel(btc.ds, backupVolumeName); err != nil {
705+
return errors.Wrapf(err, "failed to add label delete-custom-resource-only to BackupVolume %s", backupVolumeName)
706+
}
707+
if err := btc.ds.DeleteBackupVolume(backupVolumeName); err != nil {
708+
return errors.Wrapf(err, "failed to delete BackupVolume %s", backupVolumeName)
709+
}
710+
return nil
711+
}
712+
713+
func (btc *BackupTargetController) cleanupDuplicateBackupVolumeForBackupTarget(backupTarget *longhorn.BackupTarget, duplicateBackupVolumesSet sets.Set[string]) (err error) {
714+
log := getLoggerForBackupTarget(btc.logger, backupTarget)
715+
if count := duplicateBackupVolumesSet.Len(); count > 0 {
716+
log.Infof("Found %d duplicated backup volume CRs for the backup target and need to be deleted from the cluster", count)
717+
}
718+
719+
multiError := util.NewMultiError()
720+
for bvName := range duplicateBackupVolumesSet {
721+
log.WithField("backupVolume", bvName).Info("Deleting BackupVolume that has duplicate volume name in cluster")
722+
if err := btc.deleteBackupVolumeCROnly(bvName, log); err != nil {
723+
if apierrors.IsNotFound(err) {
724+
continue
725+
}
726+
multiError.Append(util.NewMultiError(fmt.Sprintf("%v: %v", bvName, err)))
641727
}
642728
}
643729

730+
if len(multiError) > 0 {
731+
return fmt.Errorf("failed to delete backup volumes: %v", multiError.Join())
732+
}
644733
return nil
645734
}
646735

types/types.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,8 +778,9 @@ func GetShareManagerNameFromShareManagerPodName(podName string) string {
778778
return strings.TrimPrefix(podName, shareManagerPrefix)
779779
}
780780

781-
func GetBackupVolumeNameFromVolumeName(volumeName string) string {
782-
return volumeName + "-" + util.RandomID()
781+
func GetBackupVolumeNameFromVolumeName(volumeName, backupTargetName string) string {
782+
// Generate a unique backup volume name based on the volume name and backup target name to prevent re-creation from a split-brain scenario.
783+
return volumeName + "-" + util.GetStringChecksumSHA256(strings.TrimSpace(fmt.Sprintf("%s-%s", volumeName, backupTargetName)))[:util.RandomIDLength]
783784
}
784785

785786
func GetBackupBackingImageNameFromBIName(backingImageName string) string {

0 commit comments

Comments
 (0)