Skip to content

Commit cfab52c

Browse files
committed
Add logic for --fallback-enabled and
... --allow-partly-done options
1 parent 13a32f2 commit cfab52c

File tree

2 files changed

+196
-18
lines changed

2 files changed

+196
-18
lines changed

pbm/restore/physical.go

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -249,31 +249,72 @@ func (r *PhysRestore) close(noerr, cleanup bool) {
249249
if err != nil {
250250
r.log.Warning("waiting for cluster status during cleanup: %v", err)
251251
}
252-
if cStatus == defs.StatusError && cleanup {
253-
r.log.Warning("apply db data from %s", fallbackDir)
254-
err := r.migrateFromFallbackDirToDBDir()
255-
if err != nil {
256-
r.log.Error("migrate from fallback dir: %v", err)
257-
}
258-
} else if (cStatus == defs.StatusDone || cStatus == defs.StatusPartlyDone) && cleanup {
259-
r.log.Debug("clean-up dbpath")
260-
err := removeAll(r.dbpath, r.log, getInternalLogFileSkipRule())
252+
if cleanup {
253+
r.resolveCleanupStrategy(cStatus)()
254+
}
255+
256+
if r.fallback {
257+
// free space by just deleting fallback dir in any other case
258+
err = r.removeFallback()
261259
if err != nil {
262-
r.log.Error("flush dbpath %s: %v", r.dbpath, err)
260+
r.log.Error("flush fallback: %v", err)
263261
}
264262
}
265263

266-
// free space by just deleting fallback dir in any other case
267-
err = r.removeFallback()
264+
if r.stopHB != nil {
265+
close(r.stopHB)
266+
}
267+
}
268+
269+
// doFullCleanup is node's cleanup strategy for deleting the whole dbpath.
270+
func (r *PhysRestore) doFullCleanup() {
271+
r.log.Debug("clean-up dbpath")
272+
err := removeAll(r.dbpath, r.log, getInternalLogFileSkipRule())
268273
if err != nil {
269-
r.log.Error("flush fallback: %v", err)
274+
r.log.Error("flush dbpath %s: %v", r.dbpath, err)
270275
}
276+
}
271277

272-
if r.stopHB != nil {
273-
close(r.stopHB)
278+
// doFallbackCleanup is node's cleanup strategy for recovering dbpath
279+
// from fallbacksync dir.
280+
func (r *PhysRestore) doFallbackCleanup() {
281+
r.log.Warning("apply db data from %s", fallbackDir)
282+
err := r.migrateFromFallbackDirToDBDir()
283+
if err != nil {
284+
r.log.Error("migrate from fallback dir: %v", err)
274285
}
275286
}
276287

288+
// resolveCleanupStrategy is helper which returns cleanup strategy based on
289+
// config parameters and cluster status.
290+
func (r *PhysRestore) resolveCleanupStrategy(clusterStatus defs.Status) func() {
291+
if !r.fallback {
292+
// fallback strategy is disabled
293+
return r.doFullCleanup
294+
}
295+
296+
// fallback strategy is enabled
297+
switch clusterStatus {
298+
case defs.StatusError:
299+
// restore failed, fallback recovery will be applied
300+
return r.doFallbackCleanup
301+
case defs.StatusPartlyDone:
302+
if r.allowPartlyDone {
303+
// cluster is partly-done with new restore, and this node will be cleaned
304+
return r.doFullCleanup
305+
} else {
306+
// cluster is partly-done, but anyway fallback recovery will be applied
307+
return r.doFallbackCleanup
308+
}
309+
case defs.StatusDone:
310+
// this shouldn't happen, because at least this node failed
311+
// anyway, we'll cleanup this node
312+
return r.doFullCleanup
313+
}
314+
315+
return r.doFullCleanup
316+
}
317+
277318
// waitClusterStatus blocks until cluster status file is set on one of final statuses.
278319
// It also checks HB to see if cluster is stucked.
279320
func (r *PhysRestore) waitClusterStatus() (defs.Status, error) {
@@ -364,9 +405,14 @@ func (r *PhysRestore) flush(ctx context.Context) error {
364405
}
365406
}
366407

367-
err = r.migrateDBDirToFallbackDir()
368-
if err != nil {
369-
return errors.Wrapf(err, "move files to fallback path")
408+
if r.fallback {
409+
err = r.migrateDBDirToFallbackDir()
410+
if err != nil {
411+
return errors.Wrapf(err, "move files to fallback path")
412+
}
413+
} else {
414+
// fallback strategy is disabled, just wipe-up everything in dbpath
415+
err = removeAll(r.dbpath, r.log)
370416
}
371417

372418
return nil

pbm/restore/physical_test.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import (
66
"os"
77
"path"
88
"path/filepath"
9+
"reflect"
910
"strings"
1011
"testing"
1112
"time"
1213

14+
"github.com/percona/percona-backup-mongodb/pbm/defs"
1315
"github.com/percona/percona-backup-mongodb/pbm/log"
1416
)
1517

@@ -221,6 +223,136 @@ func TestWaitMgoFreePort(t *testing.T) {
221223
})
222224
}
223225

226+
func TestResolveCleanupStrategy(t *testing.T) {
227+
cmpStrategy := func(fn any) uintptr {
228+
return reflect.ValueOf(fn).Pointer()
229+
}
230+
type strategyDesc string
231+
const (
232+
fallbackStrategy strategyDesc = "fallback"
233+
fullCleanupStrategy strategyDesc = "fullCleanup"
234+
)
235+
236+
testCases := []struct {
237+
desc string
238+
fallback bool
239+
allowPartlyDone bool
240+
clusterStatus defs.Status
241+
wantStrategy strategyDesc
242+
}{
243+
// fallback enabled
244+
{
245+
desc: "fallback: enabled, allow-partly-done: enabled, status: error",
246+
fallback: true,
247+
allowPartlyDone: true,
248+
clusterStatus: defs.StatusError,
249+
wantStrategy: fallbackStrategy,
250+
},
251+
{
252+
desc: "fallback: enabled, allow-partly-done: enabled, status: partly-done",
253+
fallback: true,
254+
allowPartlyDone: true,
255+
clusterStatus: defs.StatusPartlyDone,
256+
wantStrategy: fullCleanupStrategy,
257+
},
258+
{
259+
desc: "fallback: enabled, allow-partly-done: enabled, status: done",
260+
fallback: true,
261+
allowPartlyDone: true,
262+
clusterStatus: defs.StatusDone,
263+
wantStrategy: fullCleanupStrategy,
264+
},
265+
266+
{
267+
desc: "fallback: enabled, allow-partly-done: disabled, status: error",
268+
fallback: true,
269+
allowPartlyDone: false,
270+
clusterStatus: defs.StatusError,
271+
wantStrategy: fallbackStrategy,
272+
},
273+
{
274+
desc: "fallback: enabled, allow-partly-done: disabled, status: partly-done",
275+
fallback: true,
276+
allowPartlyDone: false,
277+
clusterStatus: defs.StatusPartlyDone,
278+
wantStrategy: fallbackStrategy,
279+
},
280+
{
281+
desc: "fallback: enabled, allow-partly-done: disabled, status: done",
282+
fallback: true,
283+
allowPartlyDone: false,
284+
clusterStatus: defs.StatusDone,
285+
wantStrategy: fullCleanupStrategy,
286+
},
287+
288+
// fallback disabled
289+
{
290+
desc: "fallback: disabled, allow-partly-done: enabled, status: error",
291+
fallback: false,
292+
allowPartlyDone: true,
293+
clusterStatus: defs.StatusError,
294+
wantStrategy: fullCleanupStrategy,
295+
},
296+
{
297+
desc: "fallback: disabled, allow-partly-done: enabled, status: partly-done",
298+
fallback: false,
299+
allowPartlyDone: true,
300+
clusterStatus: defs.StatusPartlyDone,
301+
wantStrategy: fullCleanupStrategy,
302+
},
303+
{
304+
desc: "fallback: disabled, allow-partly-done: enabled, status: done",
305+
fallback: false,
306+
allowPartlyDone: true,
307+
clusterStatus: defs.StatusDone,
308+
wantStrategy: fullCleanupStrategy,
309+
},
310+
311+
{
312+
desc: "fallback: disabled, allow-partly-done: disabled, status: error",
313+
fallback: false,
314+
allowPartlyDone: false,
315+
clusterStatus: defs.StatusError,
316+
wantStrategy: fullCleanupStrategy,
317+
},
318+
{
319+
desc: "fallback: disabled, allow-partly-done: disabled, status: partly-done",
320+
fallback: false,
321+
allowPartlyDone: false,
322+
clusterStatus: defs.StatusPartlyDone,
323+
wantStrategy: fullCleanupStrategy,
324+
},
325+
{
326+
desc: "fallback: disabled, allow-partly-done: disabled, status: done",
327+
fallback: false,
328+
allowPartlyDone: false,
329+
clusterStatus: defs.StatusDone,
330+
wantStrategy: fullCleanupStrategy,
331+
},
332+
}
333+
334+
for _, tC := range testCases {
335+
t.Run(tC.desc, func(t *testing.T) {
336+
r := &PhysRestore{
337+
fallback: tC.fallback,
338+
allowPartlyDone: tC.allowPartlyDone,
339+
log: log.DiscardLogger.NewDefaultEvent(),
340+
}
341+
strategy := r.resolveCleanupStrategy(tC.clusterStatus)
342+
if tC.wantStrategy == fullCleanupStrategy {
343+
if cmpStrategy(strategy) != cmpStrategy(r.doFullCleanup) {
344+
t.Fatalf("want=%s", fullCleanupStrategy)
345+
346+
}
347+
} else if tC.wantStrategy == fallbackStrategy {
348+
if cmpStrategy(strategy) != cmpStrategy(r.doFallbackCleanup) {
349+
t.Fatalf("want=%s", fallbackStrategy)
350+
}
351+
}
352+
})
353+
}
354+
}
355+
224356
func TestRemoveAll(t *testing.T) {
225357
t.Run("removes all files in dir", func(t *testing.T) {
226358
tmpDir := setupTestFiles(t)

0 commit comments

Comments
 (0)