| new file mode 100644 |
| index 000000000..d1bd77d9a |
| --- /dev/null |
| +++ b/internal/moves/budget_test.go |
| @@ -0,0 +1,161 @@ |
| +package moves |
| + |
| +import ( |
| + "math" |
| + "testing" |
| + "time" |
| +) |
| + |
| +// Ported from internal/sim/hostlife/budget_test.go at 9c417aaa (the model of |
| +// moves spec §7.3 T11, rev 7.7 with rev 7.11's per-attempt pricing). The |
| +// model's fitsRev73 helper is dropped: it priced a rule the spec retired. |
| + |
| +func TestReadinessBudget(t *testing.T) { |
| + // a tiny instance: every fixed cost at the floor of its measured rate |
| + tiny := Estimate(1, ExampleRates) |
| + // rev 7.3 required three attempts always: 150 s of probing alone, which |
| + // the default window never held |
| + if old := tiny.Fixed() + ProbeAttemptsMax*(tiny.Probe+tiny.Settle); old <= CutWindowDefault { |
| + t.Fatalf("rev 7.3's formula admits a tiny instance in the default window (%.1f s): R3-7 is stale", old) |
| + } |
| + if n := tiny.AttemptsAdmitted(CutWindowDefault); n != 2 { |
| + t.Fatalf("rev 7.4's formula admits %d attempts for a tiny instance in %.0f s, want 2", n, CutWindowDefault) |
| + } |
| + // the boundary: fixed costs of 70 s leave exactly one attempt; 71 s none |
| + edge := CutEstimate{Stop: 70 - 5, Open: 5, Probe: ProbeSeconds, Settle: SettleSeconds} |
| + if n := edge.AttemptsAdmitted(CutWindowDefault); n != 1 { |
| + t.Fatalf("fixed costs of 70 s admit %d attempts, want 1", n) |
| + } |
| + edge.Stop++ |
| + if n := edge.AttemptsAdmitted(CutWindowDefault); n != 0 { |
| + t.Fatalf("fixed costs of 71 s admit %d attempts, want 0 (refused)", n) |
| + } |
| + // a generous window never permits more than three |
| + if n := tiny.AttemptsAdmitted(600); n != ProbeAttemptsMax { |
| + t.Fatalf("a 600 s window admits %d attempts, want %d", n, ProbeAttemptsMax) |
| + } |
| + // R5-5: the spec's example, a 2 GiB database at the example rates. Rev |
| + // 7.5's full-snapshot transfer alone takes 205 s and could never fit; |
| + // the delta within the bar and the target's build do, with one attempt |
| + big := Estimate(2048, ExampleRates) |
| + fullTransfer := 2048.0 / ExampleRates.Cross |
| + if fullTransfer < CutWindowDefault { |
| + t.Fatalf("the example no longer illustrates R5-5") |
| + } |
| + if n := big.AttemptsAdmitted(CutWindowDefault); n != 1 { |
| + t.Fatalf("a 2 GiB database at the example rates admits %d attempts, want 1 (fixed %.1f s, first attempt %.1f s)", n, big.Fixed(), big.Attempt(1)) |
| + } |
| + if math.Abs(big.Fixed()-53.6) > 0.1 { |
| + t.Fatalf("the example's fixed costs are %.1f s; the spec says 53.6 s", big.Fixed()) |
| + } |
| + // rev 7.11 (R10-8): the first attempt restores the prepared generation |
| + // again and pays the admission: 12.3 + 2 + 30 + 20 = 64.3 s, so the |
| + // example runs to 117.9 s and admits one attempt by 2.1 s; a second |
| + // attempt would add 76.6 s (a fresh generation's full upload too) |
| + if a := big.Attempt(1); math.Abs(a-64.3) > 0.1 { |
| + t.Fatalf("the example's first attempt costs %.1f s; the spec says 64.3 s", a) |
| + } |
| + if tot := big.Fixed() + big.Attempt(1); math.Abs(tot-117.9) > 0.1 { |
| + t.Fatalf("the example runs to %.1f s with one attempt; the spec says 117.9 s", tot) |
| + } |
| + if a := big.Attempt(2); math.Abs(a-76.6) > 0.1 { |
| + t.Fatalf("the example's second attempt would cost %.1f s; the spec says 76.6 s", a) |
| + } |
| + // the review's 2.5 GiB (R10-8): rev 7.10's arithmetic admitted it at |
| + // 113.8 s; with the probe's own restore it is 129.2 s, past the window, |
| + // and refused |
| + bigger := Estimate(2560, ExampleRates) |
| + old := bigger.Fixed() + ProbeSeconds + SettleSeconds |
| + if math.Abs(old-113.8) > 0.1 { |
| + t.Fatalf("rev 7.10's arithmetic for 2.5 GiB is %.1f s; the review says 113.8 s", old) |
| + } |
| + if n := bigger.AttemptsAdmitted(CutWindowDefault); n != 0 { |
| + t.Fatalf("2.5 GiB at the example rates admits %d attempts, want 0 (%.1f s with one attempt)", n, bigger.Fixed()+bigger.Attempt(1)) |
| + } |
| + // and a cross-region snapshot transfer, if a builder did that instead, |
| + // is refused by the same arithmetic |
| + naive := big |
| + naive.Mirror, naive.RestoreStaged, naive.SnapshotBuild = fullTransfer, 0, 0 |
| + if n := naive.AttemptsAdmitted(CutWindowDefault); n != 0 { |
| + t.Fatalf("a full cross-region snapshot transfer admits %d attempts, want 0", n) |
| + } |
| + // R6-3: rev 7.6 priced three passes at one local rate and then restored |
| + // the generation cold a fourth time, unpriced. The review's workload: 3 |
| + // GiB, 200 MiB/s local, 10 MiB/s across regions, one probe. Three passes |
| + // admit one attempt (108.68 s with the probe); the fourth pass takes the |
| + // same workload to 124.04 s, past the window |
| + r := Rates{Cross: 10, S3: 200, Disk: 200} |
| + three := Estimate(3072, r) |
| + three.RestoreStaged, three.SnapshotBuild, three.Anchor, three.Verify = 3072/200.0, 3072/200.0, 3072/200.0, 0 // rev 7.6's arithmetic |
| + three.RestoreAttempt, three.Admit, three.FreshUpload = 0, 0, 0 // rev 7.6's arithmetic knew no per-attempt restore |
| + if n := three.AttemptsAdmitted(CutWindowDefault); n != 1 { |
| + t.Fatalf("rev 7.6's three passes admit %d attempts for the review's workload, want 1 (the finding's premise; fixed %.2f s)", n, three.Fixed()) |
| + } |
| + four := three |
| + four.Verify = 3072 / 200.0 // the unpriced cold restore, at the same rate |
| + if n := four.AttemptsAdmitted(CutWindowDefault); n != 0 || four.Fixed()+ProbeSeconds+SettleSeconds < 124 { |
| + t.Fatalf("with the fourth pass priced the review's workload admits %d attempts at %.2f s with one probe, want 0 past 124 s", n, four.Fixed()+ProbeSeconds+SettleSeconds) |
| + } |
| + // rev 7.7 verifies the built database instead of restoring it again: |
| + // the same workload is refused honestly, by its own arithmetic |
| + if n := Estimate(3072, r).AttemptsAdmitted(CutWindowDefault); n != 0 { |
| + t.Fatalf("rev 7.7 admits %d attempts for the review's workload, want 0 (fixed %.1f s)", n, Estimate(3072, r).Fixed()) |
| + } |
| +} |
| + |
| +func TestBudgetTotalAndDeadline(t *testing.T) { |
| + e := Estimate(2048, ExampleRates) |
| + n := e.AttemptsAdmitted(CutWindowDefault) |
| + if n != 1 || math.Abs(e.Total(n)-117.9) > 0.1 { |
| + t.Fatalf("attempts %d total %.1f", n, e.Total(n)) |
| + } |
| + // cut_deadline = cut_at + cut_budget - open_budget (§7.4). |
| + at := time.Date(2026, 10, 6, 9, 0, 0, 0, time.UTC) |
| + if d := CutDeadline(at, CutWindowDefault); !d.Equal(at.Add(115 * time.Second)) { |
| + t.Fatal(d) |
| + } |
| +} |
| + |
| +// The mirror bar is the one input Task 11 overrides |
| +// (CARLOS_MOVE_READY_DELTA_BYTES, Ruling F28(e)): zero means the default, |
| +// and the bar scales only the mirror. |
| +func TestEstimateMirrorBar(t *testing.T) { |
| + def := Estimate(2048, ExampleRates) |
| + if want := MirrorBarMiB / ExampleRates.Cross; def.Mirror != want { |
| + t.Fatalf("default mirror %.2f s, want %.2f s", def.Mirror, want) |
| + } |
| + explicit := ExampleRates |
| + explicit.MirrorBar = MirrorBarMiB |
| + if got := Estimate(2048, explicit); got != def { |
| + t.Fatalf("an explicit default bar changed the estimate: %+v vs %+v", got, def) |
| + } |
| + wide := ExampleRates |
| + wide.MirrorBar = 64 |
| + w := Estimate(2048, wide) |
| + if math.Abs(w.Mirror-6.4) > 1e-9 { |
| + t.Fatalf("a 64 MiB bar at 10 MiB/s mirrors in %.2f s, want 6.4", w.Mirror) |
| + } |
| + if math.Abs((w.Fixed()-def.Fixed())-(6.4-1.6)) > 1e-9 { |
| + t.Fatalf("the bar moved more than the mirror: fixed %.2f vs %.2f", w.Fixed(), def.Fixed()) |
| + } |
| + if w.Attempt(1) != def.Attempt(1) { |
| + t.Fatalf("the bar changed a per-attempt cost") |
| + } |
| + // the open phase is the §7.4 open budget, not a second literal |
| + if def.Open != OpenBudget { |
| + t.Fatalf("Open %.1f, want OpenBudget %.1f", def.Open, OpenBudget) |
| + } |
| +} |
| + |
| +// A zero or missing rate prices a phase at infinity: refused, never admitted. |
| +func TestEstimateZeroRateRefuses(t *testing.T) { |
| + for name, r := range map[string]Rates{ |
| + "cross": {Cross: 0, S3: 250, Disk: 500}, |
| + "s3": {Cross: 10, S3: 0, Disk: 500}, |
| + "disk": {Cross: 10, S3: 250, Disk: 0}, |
| + } { |
| + if n := Estimate(1, r).AttemptsAdmitted(CutWindowDefault); n != 0 { |
| + t.Fatalf("%s rate of zero admits %d attempts, want 0", name, n) |
| + } |
| + } |
| +} |