| index 012cebb04..e481df618 100644 |
| --- a/internal/host/movesprobeowners_fix1_test.go |
| +++ b/internal/host/movesprobeowners_fix1_test.go |
| @@ -16,6 +16,7 @@ import ( |
| "github.com/carlosframework/platform/internal/hostwrite" |
| "github.com/carlosframework/platform/internal/moves" |
| "github.com/carlosframework/platform/internal/provision" |
| + "github.com/superfly/ltx" |
| ) |
| |
| // fenceWork is an admitted spawn selection whose fence can be closed: once |
| @@ -237,10 +238,22 @@ func TestMovesProbeOwnersChangedConfigThroughThePolicy(t *testing.T) { |
| |
| // Finding 8, ruled order: admit, publish the verified prepared marker under |
| // the writer's fence (superseding the earlier stay's marker by If-Match), |
| -// then the start check, then the spawn. |
| +// then the start check, then the spawn. Fix round 2: the earlier stay is |
| +// really in the admission history, a cold admission on this box and region |
| +// that does not descend from the move's admission; it is still an earlier |
| +// stay, superseded, never taken for a restart of this landing. |
| func TestMovesProbeOwnersSupersedeTheEarlierStaysMarker(t *testing.T) { |
| f := newPOFixture(t) |
| ctx := t.Context() |
| + c := f.poFlipClaim() |
| + c.Holder.Admissions = []hostclaim.AdmissionRecord{ |
| + {Session: 5, Box: "uk-box", Generation: "earlier", Predecessor: "", Region: "uk", Mode: "cold"}, |
| + {Session: 15, Box: "ie-box", Generation: "src", Predecessor: "earlier", Region: "ie", PredecessorRegion: "uk", Mode: "cold"}, |
| + } |
| + if err := c.Validate(); err != nil { |
| + t.Fatal(err) |
| + } |
| + f.putClaim(c) |
| f.bucket.Put("data", f.markerKey(), mustJSON(t, genselect.Marker{Op: "earlier", Incarnation: f.inc, Host: poHost, Box: "uk-box", InstalledAt: time.Unix(1, 0).UTC(), Pin: genselect.Pin{SnapshotTXID: 3}})) |
| admitted, in := f.selectAndAdmit() |
| if err := f.owners.Materialize(ctx, admitted, in, newFenceWork()); err != nil { |
| @@ -249,6 +262,81 @@ func TestMovesProbeOwnersSupersedeTheEarlierStaysMarker(t *testing.T) { |
| if m, _ := f.currentMarker(); m.Op != poOp { |
| t.Fatalf("the earlier stay's marker was not superseded: %+v", m) |
| } |
| + if err := f.owners.Publish(ctx, admitted, in); err != nil { |
| + t.Fatalf("Group D's prepared publication: %v", err) |
| + } |
| + if err := f.owners.PublicationEvidence(ctx, admitted); err != nil { |
| + t.Fatalf("publication evidence: %v", err) |
| + } |
| +} |
| + |
| +// Fix round 2: a genuine restart after a crash is still this landing's. The |
| +// first attempt published the move id; the restarted attempt fresh1 |
| +// published its own marker after its root and crashed. The next attempt's |
| +// baseline is the certified remote from fresh1, whose chain reaches the move |
| +// id, and fresh1's marker is kept, not superseded by the prepared one. (That |
| +// attempt then stops at Task 4's files rule: .files-gen names the move id, |
| +// two cold admissions back.) |
| +func TestMovesProbeOwnersRecogniseARestartAfterACrash(t *testing.T) { |
| + f := newPOFixture(t) |
| + ctx := t.Context() |
| + first, in := f.selectAndAdmit() |
| + if err := f.owners.Materialize(ctx, first, in, newFenceWork()); err != nil { |
| + t.Fatal(err) |
| + } |
| + again, err := f.owners.Select(ctx, f.route, first, poProbe) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + restarted := f.admit(first, again) |
| + fresh := again.Admission.NewGeneration |
| + f.bucket.Put("data", "instances/"+poHost+"/g-"+fresh+"/0009/"+ltx.FormatFilename(1, 44), f.ltxHeader(1, 44)) |
| + pub := ClaimPublication{Sync: func(context.Context) error { return nil }, Check: func(context.Context) error { return nil }} |
| + if _, err := f.owners.PublishFenced(ctx, restarted, again, pub); err != nil { |
| + t.Fatal(err) |
| + } |
| + // The crash: the next record attempt admits again over fresh1. |
| + next, err := f.owners.Select(ctx, f.route, restarted, poProbe) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if next.Baseline.Generation != fresh || next.Baseline.Marker == nil || next.Baseline.Marker.Op != fresh || next.Baseline.MaxTXID != 44 { |
| + t.Fatalf("the restart's baseline is not the certified remote of %s: %+v", fresh, next.Baseline) |
| + } |
| + admitted := f.admit(restarted, next) |
| + f.writeLocal(poOp, admitted.Claim.Holder.Generation, f.inc) |
| + written := f.bucket.LatestID("data", f.markerKey()) |
| + err = f.owners.Materialize(ctx, admitted, next, newFenceWork()) |
| + if m, _ := f.currentMarker(); m.Op != fresh || f.bucket.LatestID("data", f.markerKey()) != written { |
| + t.Fatalf("the restart's marker was superseded: %+v", m) |
| + } |
| + if !errors.Is(err, moves.ErrStartFiles) { |
| + t.Fatalf("the second restart: %v", err) |
| + } |
| +} |
| + |
| +// Fix round 2, the predicate itself: a generation is this landing's only |
| +// with positive proof, the chain of cold admissions on this box and region |
| +// back from it reaching the move id. |
| +func TestMovesProbeOwnersLandingMarkerNeedsDescent(t *testing.T) { |
| + f := newPOFixture(t) |
| + v := probeLanding{op: poOp, manifest: f.manifest, holder: &hostclaim.Holder{Incarnation: f.inc, Admissions: []hostclaim.AdmissionRecord{ |
| + {Session: 5, Box: "uk-box", Generation: "earlier", Region: "uk", Mode: "cold"}, |
| + {Session: 15, Box: "ie-box", Generation: "src", Predecessor: "earlier", Region: "ie", Mode: "cold"}, |
| + {Session: 20, Box: "uk-box", Generation: poOp, Predecessor: "src", Region: "uk", Mode: "cold", Coverage: "sealed"}, |
| + {Session: 21, Box: "uk-box", Generation: "f1", Predecessor: poOp, Region: "uk", Mode: "cold"}, |
| + {Session: 22, Box: "uk-box", Generation: "f2", Predecessor: "f1", Region: "uk", Mode: "cold"}, |
| + {Session: 23, Box: "uk-box", Generation: "orphan", Predecessor: "gone", Region: "uk", Mode: "cold"}, |
| + {Session: 24, Box: "other-box", Generation: "f3", Predecessor: "f2", Region: "uk", Mode: "cold"}, |
| + }}} |
| + marker := func(op string) *genselect.Marker { |
| + return &genselect.Marker{Op: op, Incarnation: f.inc, Host: poHost, Box: "uk-box"} |
| + } |
| + for op, want := range map[string]bool{"f1": true, "f2": true, "earlier": false, "src": false, "orphan": false, "f3": false, "unknown": false} { |
| + if got := f.owners.landingMarker(marker(op), poHost, v, ""); got != want { |
| + t.Errorf("%s: %v, want %v", op, got, want) |
| + } |
| + } |
| } |
| |
| // Finding 8: a history trimmed so that the admitted generation's ancestry |