| index 57cb51d41..16bbc04b8 100644 |
| --- a/internal/host/movesretry_test.go |
| +++ b/internal/host/movesretry_test.go |
| @@ -259,3 +259,67 @@ func TestRetryLeavesTheClaimAndTheRowAlone(t *testing.T) { |
| t.Fatalf("a retry of a move that is not stuck: %v", err) |
| } |
| } |
| + |
| +// countEvent counts event in events. |
| +func countEvent(events []string, event string) int { |
| + n := 0 |
| + for _, e := range events { |
| + if e == event { |
| + n++ |
| + } |
| + } |
| + return n |
| +} |
| + |
| +// Final review I3: a retry's event comes from the box's own pass. After |
| +// `move resolve --retry` (the claim-backed MoveRetryRecovery, which advances |
| +// the epoch) the record is rolling_back or reverting, which movesPass sends |
| +// to the source's recovery or revert before phase1MoveRound; the pass still |
| +// logs retry-<epoch>, once, however many passes see it. |
| +func TestRetryEventIsLoggedByThePass(t *testing.T) { |
| + t.Run("a rollback's source recovery", func(t *testing.T) { |
| + h := newRecoverHarness(t, false) |
| + h.released(t) |
| + if err := os.WriteFile(filepath.Join(h.sdir, p1Host, "upload.txt"), []byte("changed on the source"), 0o644); err != nil { |
| + t.Fatal(err) |
| + } |
| + h.srcPass(t) |
| + if m := h.read(t, p1Host).Move; m.State != provision.MoveStuck { |
| + t.Fatalf("not stuck: %+v", m) |
| + } |
| + h.retry(t) |
| + if m := h.read(t, p1Host).Move; m.State != provision.MoveRollingBack || m.RecoveryRetryEpoch != 1 { |
| + t.Fatalf("after the retry: %+v", m) |
| + } |
| + h.srcPass(t) |
| + h.srcPass(t) |
| + if n := countEvent(h.sEvents, "retry"); n != 1 { |
| + t.Fatalf("retry events %d, want one: %v", n, h.sEvents) |
| + } |
| + }) |
| + |
| + t.Run("a revert", func(t *testing.T) { |
| + h := newRevertHarness(t) |
| + h.fromStopped(t) |
| + h.arm(t) |
| + if err := os.WriteFile(filepath.Join(h.cfg.DataDir, p1Host, "upload.txt"), []byte("changed while stopped"), 0o644); err != nil { |
| + t.Fatal(err) |
| + } |
| + h.pass(t) |
| + if m := h.move(t); m.State != provision.MoveStuck { |
| + t.Fatalf("not stuck: %+v", m) |
| + } |
| + g := provision.MoveGuard{ID: p1ID, Incarnation: h.inc} |
| + if _, err := provision.SetRecordMove(context.Background(), h.s, "bab", "app", p1Host, provision.MoveRetryRecovery(g)); err != nil { |
| + t.Fatal(err) |
| + } |
| + if m := h.move(t); m.State != provision.MoveReverting || m.RecoveryRetryEpoch != 1 { |
| + t.Fatalf("after the retry: %+v", m) |
| + } |
| + h.pass(t) |
| + h.pass(t) |
| + if n := countEvent(h.events, "retry"); n != 1 { |
| + t.Fatalf("retry events %d, want one: %v", n, h.events) |
| + } |
| + }) |
| +} |