fix(planning): replan a plan once when reconsiderations race
Retracting a plan's assumption replanned it and withdrew its pending approval. An executor tick holding the old plan then saw the withdrawn approval, treated it as expired and replanned a second time. Entering replanning is now a compare-and-set, so only the first reconsideration replans and a stale one does nothing. A regression test fails with three plans without the guard.
This commit is contained in:
@@ -538,3 +538,22 @@ func TestStaleStepCopyCannotMoveStepBackwards(t *testing.T) {
|
||||
t.Fatalf("stale copy overwrote a claimed step: %s", got.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// Two reconsiderations holding the same plan (an event reaction and an
|
||||
// executor tick) must replan it once, not twice.
|
||||
func TestConcurrentReconsiderationReplansOnce(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
f.allow("service.restart")
|
||||
it := f.intention(false)
|
||||
p := f.start(it)
|
||||
trig := volition.Trigger{Kind: volition.TriggerStepFailed, Detail: "test"}
|
||||
if err := f.exec.reconsider(f.ctx, p, it, trig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.exec.reconsider(f.ctx, p, it, trig); err != nil { // stale copies of the plan and intention
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ps, _ := f.plans.ForIntention(f.ctx, nil, it.ID); len(ps) != 2 {
|
||||
t.Fatalf("want one replan, got %d plans", len(ps))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user