Skip to content

Commit ab11edd

Browse files
authored
Merge pull request #159913 from kev-cao/blathers/backport-release-26.1-159618
release-26.1: backup: fix MakeBackupDestinationStores cleanup
2 parents 3a3e97c + d81770e commit ab11edd

File tree

2 files changed

+80
-12
lines changed

2 files changed

+80
-12
lines changed

pkg/backup/backupdest/incrementals.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -198,25 +198,27 @@ func MakeBackupDestinationStores(
198198
mkStore cloud.ExternalStorageFromURIFactory,
199199
destinationDirs []string,
200200
) ([]cloud.ExternalStorage, func() error, error) {
201-
stores := make([]cloud.ExternalStorage, len(destinationDirs))
202-
for i := range destinationDirs {
203-
store, err := mkStore(ctx, destinationDirs[i], user)
204-
if err != nil {
205-
return nil, nil, errors.Wrapf(err, "failed to open backup storage location")
206-
}
207-
stores[i] = store
208-
}
209-
210-
return stores, func() error {
211-
// Close all the stores in the returned cleanup function.
201+
stores := make([]cloud.ExternalStorage, 0, len(destinationDirs))
202+
cleanup := func() error {
212203
var combinedErr error
213204
for _, store := range stores {
214205
if err := store.Close(); err != nil {
215206
combinedErr = errors.CombineErrors(combinedErr, err)
216207
}
217208
}
218209
return combinedErr
219-
}, nil
210+
}
211+
212+
for _, dir := range destinationDirs {
213+
store, err := mkStore(ctx, dir, user)
214+
if err != nil {
215+
err := errors.Wrapf(err, "failed to open backup storage location")
216+
return nil, nil, errors.CombineErrors(err, cleanup())
217+
}
218+
stores = append(stores, store)
219+
}
220+
221+
return stores, cleanup, nil
220222
}
221223

222224
// ResolveIncrementalsBackupLocation returns the resolved locations of

pkg/backup/backupdest/incrementals_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,3 +594,69 @@ func TestLegacyFindPriorBackups(t *testing.T) {
594594
})
595595
}
596596
}
597+
598+
func TestCleanupMakeBackupDestinationStores(t *testing.T) {
599+
defer leaktest.AfterTest(t)()
600+
// This test ensures that in the event that MakeBackupDestinationStores
601+
// encounters an error either during store creation or during cleanup that all
602+
// stores that were opened have Close called.
603+
ctx := context.Background()
604+
605+
const maxStores = 10
606+
failAfterN := rand.Intn(maxStores + 1)
607+
608+
stores := make([]*fakeStore, 0, maxStores)
609+
mkStore := func(
610+
_ context.Context,
611+
_ string,
612+
_ username.SQLUsername,
613+
_ ...cloud.ExternalStorageOption,
614+
) (cloud.ExternalStorage, error) {
615+
if len(stores) == failAfterN {
616+
return nil, fmt.Errorf("simulated failure after %d stores", failAfterN)
617+
}
618+
s := &fakeStore{closeErrProbability: 0.1}
619+
stores = append(stores, s)
620+
return s, nil
621+
}
622+
623+
destinations := make([]string, maxStores)
624+
_, cleanup, err := backupdest.MakeBackupDestinationStores(
625+
ctx, username.RootUserName(), mkStore, destinations,
626+
)
627+
628+
countOpenStores := func() int {
629+
count := 0
630+
for _, s := range stores {
631+
if !s.calledClose {
632+
count++
633+
}
634+
}
635+
return count
636+
}
637+
638+
if failAfterN < maxStores {
639+
require.Error(t, err)
640+
require.Equal(t, 0, countOpenStores())
641+
} else {
642+
require.NoError(t, err)
643+
require.Equal(t, maxStores, countOpenStores())
644+
_ = cleanup()
645+
require.Equal(t, 0, countOpenStores())
646+
}
647+
}
648+
649+
type fakeStore struct {
650+
cloud.ExternalStorage
651+
calledClose bool
652+
closeErrProbability float32
653+
}
654+
655+
// Close() simulates closing the store and probabilistically returns an error.
656+
func (s *fakeStore) Close() error {
657+
s.calledClose = true
658+
if rand.Float32() < s.closeErrProbability {
659+
return fmt.Errorf("simulated close error")
660+
}
661+
return nil
662+
}

0 commit comments

Comments
 (0)