carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package moves
2
3import (
4 "math"
5 "testing"
6 "time"
7)
8
9// Ported from internal/sim/hostlife/budget_test.go at 9c417aaa (the model of
10// moves spec §7.3 T11, rev 7.7 with rev 7.11's per-attempt pricing). The
11// model's fitsRev73 helper is dropped: it priced a rule the spec retired.
12
13func TestReadinessBudget(t *testing.T) {
14 // a tiny instance: every fixed cost at the floor of its measured rate
15 tiny := Estimate(1, 0, ExampleRates)
16 // rev 7.3 required three attempts always: 150 s of probing alone, which
17 // the default window never held
18 if old := tiny.Fixed() + ProbeAttemptsMax*(tiny.Probe+tiny.Settle); old <= CutWindowDefault {
19 t.Fatalf("rev 7.3's formula admits a tiny instance in the default window (%.1f s): R3-7 is stale", old)
20 }
21 if n := tiny.AttemptsAdmitted(CutWindowDefault); n != 2 {
22 t.Fatalf("rev 7.4's formula admits %d attempts for a tiny instance in %.0f s, want 2", n, CutWindowDefault)
23 }
24 // the boundary: fixed costs of 70 s leave exactly one attempt; 71 s none
25 edge := CutEstimate{Stop: 70 - OpenBudget, Open: OpenBudget, Probe: ProbeSeconds, Settle: SettleSeconds}
26 if n := edge.AttemptsAdmitted(CutWindowDefault); n != 1 {
27 t.Fatalf("fixed costs of 70 s admit %d attempts, want 1", n)
28 }
29 edge.Stop++
30 if n := edge.AttemptsAdmitted(CutWindowDefault); n != 0 {
31 t.Fatalf("fixed costs of 71 s admit %d attempts, want 0 (refused)", n)
32 }
33 // a generous window never permits more than three
34 if n := tiny.AttemptsAdmitted(600); n != ProbeAttemptsMax {
35 t.Fatalf("a 600 s window admits %d attempts, want %d", n, ProbeAttemptsMax)
36 }
37 // R5-5: the spec's example, a 2 GiB database at the example rates. Rev
38 // 7.5's full-snapshot transfer alone takes 205 s and could never fit;
39 // the delta within the bar and the target's build do, with one attempt
40 big := Estimate(2048, 0, ExampleRates)
41 fullTransfer := 2048.0 / ExampleRates.Cross
42 if fullTransfer < CutWindowDefault {
43 t.Fatalf("the example no longer illustrates R5-5")
44 }
45 if n := big.AttemptsAdmitted(CutWindowDefault); n != 1 {
46 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))
47 }
48 if math.Abs(big.Fixed()-53.6) > 0.1 {
49 t.Fatalf("the example's fixed costs are %.1f s; the spec says 53.6 s", big.Fixed())
50 }
51 // rev 7.11 (R10-8): the first attempt restores the prepared generation
52 // again and pays the admission: 12.3 + 2 + 30 + 20 = 64.3 s, so the
53 // example runs to 117.9 s and admits one attempt by 2.1 s; a second
54 // attempt would add 76.6 s (a fresh generation's full upload too)
55 if a := big.Attempt(1); math.Abs(a-64.3) > 0.1 {
56 t.Fatalf("the example's first attempt costs %.1f s; the spec says 64.3 s", a)
57 }
58 if tot := big.Fixed() + big.Attempt(1); math.Abs(tot-117.9) > 0.1 {
59 t.Fatalf("the example runs to %.1f s with one attempt; the spec says 117.9 s", tot)
60 }
61 if a := big.Attempt(2); math.Abs(a-76.6) > 0.1 {
62 t.Fatalf("the example's second attempt would cost %.1f s; the spec says 76.6 s", a)
63 }
64 // the review's 2.5 GiB (R10-8): rev 7.10's arithmetic admitted it at
65 // 113.8 s; with the probe's own restore it is 129.2 s, past the window,
66 // and refused
67 bigger := Estimate(2560, 0, ExampleRates)
68 old := bigger.Fixed() + ProbeSeconds + SettleSeconds
69 if math.Abs(old-113.8) > 0.1 {
70 t.Fatalf("rev 7.10's arithmetic for 2.5 GiB is %.1f s; the review says 113.8 s", old)
71 }
72 if n := bigger.AttemptsAdmitted(CutWindowDefault); n != 0 {
73 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))
74 }
75 // and a cross-region snapshot transfer, if a builder did that instead,
76 // is refused by the same arithmetic
77 naive := big
78 naive.Mirror, naive.RestoreStaged, naive.SnapshotBuild = fullTransfer, 0, 0
79 if n := naive.AttemptsAdmitted(CutWindowDefault); n != 0 {
80 t.Fatalf("a full cross-region snapshot transfer admits %d attempts, want 0", n)
81 }
82 // R6-3: rev 7.6 priced three passes at one local rate and then restored
83 // the generation cold a fourth time, unpriced. The review's workload: 3
84 // GiB, 200 MiB/s local, 10 MiB/s across regions, one probe. Three passes
85 // admit one attempt (108.68 s with the probe); the fourth pass takes the
86 // same workload to 124.04 s, past the window
87 r := Rates{Cross: 10, S3: 200, Disk: 200}
88 three := Estimate(3072, 0, r)
89 three.RestoreStaged, three.SnapshotBuild, three.Anchor, three.Verify = 3072/200.0, 3072/200.0, 3072/200.0, 0 // rev 7.6's arithmetic
90 three.RestoreAttempt, three.Admit, three.FreshUpload = 0, 0, 0 // rev 7.6's arithmetic knew no per-attempt restore
91 if n := three.AttemptsAdmitted(CutWindowDefault); n != 1 {
92 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())
93 }
94 four := three
95 four.Verify = 3072 / 200.0 // the unpriced cold restore, at the same rate
96 if n := four.AttemptsAdmitted(CutWindowDefault); n != 0 || four.Fixed()+ProbeSeconds+SettleSeconds < 124 {
97 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)
98 }
99 // rev 7.7 verifies the built database instead of restoring it again:
100 // the same workload is refused honestly, by its own arithmetic
101 if n := Estimate(3072, 0, r).AttemptsAdmitted(CutWindowDefault); n != 0 {
102 t.Fatalf("rev 7.7 admits %d attempts for the review's workload, want 0 (fixed %.1f s)", n, Estimate(3072, 0, r).Fixed())
103 }
104}
105
106func TestBudgetTotalAndDeadline(t *testing.T) {
107 e := Estimate(2048, 0, ExampleRates)
108 n := e.AttemptsAdmitted(CutWindowDefault)
109 if n != 1 || math.Abs(e.Total(n)-117.9) > 0.1 {
110 t.Fatalf("attempts %d total %.1f", n, e.Total(n))
111 }
112 // cut_deadline = cut_at + cut_budget - open_budget (§7.4).
113 at := time.Date(2026, 10, 6, 9, 0, 0, 0, time.UTC)
114 if d := CutDeadline(at, CutWindowDefault); !d.Equal(at.Add(115 * time.Second)) {
115 t.Fatal(d)
116 }
117}
118
119// The mirror bar is the one input Task 11 overrides
120// (CARLOS_MOVE_READY_DELTA_BYTES, Ruling F28(e)): zero means the default,
121// and the bar scales only the mirror.
122func TestEstimateMirrorBar(t *testing.T) {
123 def := Estimate(2048, 0, ExampleRates)
124 if want := MirrorBarMiB / ExampleRates.Cross; def.Mirror != want {
125 t.Fatalf("default mirror %.2f s, want %.2f s", def.Mirror, want)
126 }
127 explicit := ExampleRates
128 explicit.MirrorBar = MirrorBarMiB
129 if got := Estimate(2048, 0, explicit); got != def {
130 t.Fatalf("an explicit default bar changed the estimate: %+v vs %+v", got, def)
131 }
132 wide := ExampleRates
133 wide.MirrorBar = 64
134 w := Estimate(2048, 0, wide)
135 if math.Abs(w.Mirror-6.4) > 1e-9 {
136 t.Fatalf("a 64 MiB bar at 10 MiB/s mirrors in %.2f s, want 6.4", w.Mirror)
137 }
138 if math.Abs((w.Fixed()-def.Fixed())-(6.4-1.6)) > 1e-9 {
139 t.Fatalf("the bar moved more than the mirror: fixed %.2f vs %.2f", w.Fixed(), def.Fixed())
140 }
141 if w.Attempt(1) != def.Attempt(1) {
142 t.Fatalf("the bar changed a per-attempt cost")
143 }
144 // the open phase is the §7.4 open budget, not a second literal
145 if def.Open != OpenBudget {
146 t.Fatalf("Open %.1f, want OpenBudget %.1f", def.Open, OpenBudget)
147 }
148}
149
150// An unusable input prices the cut at infinity: refused, never admitted. A
151// negative rate would otherwise price a phase below zero and let a move in.
152func TestEstimateInvalidInputsRefuse(t *testing.T) {
153 nan, inf := math.NaN(), math.Inf(1)
154 good := ExampleRates
155 with := func(f func(r *Rates)) Rates { r := good; f(&r); return r }
156 cases := map[string]struct {
157 db float64
158 r Rates
159 }{
160 "cross zero": {1, with(func(r *Rates) { r.Cross = 0 })},
161 "cross negative": {1, with(func(r *Rates) { r.Cross = -10 })},
162 "cross NaN": {1, with(func(r *Rates) { r.Cross = nan })},
163 "cross Inf": {1, with(func(r *Rates) { r.Cross = inf })},
164 "s3 zero": {1, with(func(r *Rates) { r.S3 = 0 })},
165 "s3 negative": {1, with(func(r *Rates) { r.S3 = -250 })},
166 "s3 NaN": {1, with(func(r *Rates) { r.S3 = nan })},
167 "s3 Inf": {1, with(func(r *Rates) { r.S3 = inf })},
168 "disk zero": {1, with(func(r *Rates) { r.Disk = 0 })},
169 "disk negative": {1, with(func(r *Rates) { r.Disk = -500 })},
170 "disk NaN": {1, with(func(r *Rates) { r.Disk = nan })},
171 "disk Inf": {1, with(func(r *Rates) { r.Disk = inf })},
172 "bar negative": {1, with(func(r *Rates) { r.MirrorBar = -16 })},
173 "bar NaN": {1, with(func(r *Rates) { r.MirrorBar = nan })},
174 "bar Inf": {1, with(func(r *Rates) { r.MirrorBar = inf })},
175 "db negative": {-2048, good},
176 "db NaN": {nan, good},
177 "db Inf": {inf, good},
178 // a negative database against a negative rate: the quotient is
179 // positive, and must still be refused
180 "db and s3 negative": {-1, with(func(r *Rates) { r.S3 = -250 })},
181 }
182 for name, c := range cases {
183 e := Estimate(c.db, 0, c.r)
184 if n := e.AttemptsAdmitted(CutWindowDefault); n != 0 {
185 t.Errorf("%s admits %d attempts, want 0", name, n)
186 }
187 if n := e.AttemptsAdmitted(1e9); n != 0 {
188 t.Errorf("%s admits %d attempts in a huge window, want 0", name, n)
189 }
190 if f := e.Fixed(); !math.IsInf(f, 1) {
191 t.Errorf("%s fixed cost %v, want +Inf", name, f)
192 }
193 }
194 // the boundary the refusals must not touch: an empty database is valid
195 if n := Estimate(0, 0, ExampleRates).AttemptsAdmitted(CutWindowDefault); n != 2 {
196 t.Errorf("an empty database admits %d attempts, want 2", n)
197 }
198}
199
200// Ruling T7-budget: the landing's fsync scales with the archive's entry
201// count, so the Archive term is max(2, entries/FsyncRate) seconds. Every
202// new input keeps the invalid-input refusal.
203func TestEstimateArchiveScalesWithEntries(t *testing.T) {
204 r := ExampleRates
205 if got := Estimate(2048, 0, r).Archive; got != 2 {
206 t.Fatalf("no entries: Archive %v, want 2", got)
207 }
208 if got := Estimate(2048, 100, r).Archive; got != 2 {
209 t.Fatalf("a few entries: Archive %v, want the 2 s floor", got)
210 }
211 want := 32000 / r.FsyncRate
212 if got := Estimate(2048, 32000, r).Archive; got != want || got <= 2 {
213 t.Fatalf("32000 entries: Archive %v, want %v", got, want)
214 }
215 if Estimate(2048, 32000, r).Fixed() <= Estimate(2048, 0, r).Fixed() {
216 t.Fatal("entries did not reach the fixed cost")
217 }
218 // No entries needs no fsync rate.
219 noRate := r
220 noRate.FsyncRate = 0
221 if got := Estimate(2048, 0, noRate).Archive; got != 2 {
222 t.Fatalf("no entries, no rate: Archive %v, want 2", got)
223 }
224 nan, inf := math.NaN(), math.Inf(1)
225 for name, c := range map[string]struct {
226 entries int
227 fsync float64
228 }{
229 "negative entries": {-1, r.FsyncRate},
230 "entries and no rate": {10, 0},
231 "negative rate": {10, -1600},
232 "negative rate, no files": {0, -1600},
233 "NaN rate": {10, nan},
234 "NaN rate, no files": {0, nan},
235 "Inf rate": {10, inf},
236 } {
237 rr := r
238 rr.FsyncRate = c.fsync
239 e := Estimate(2048, c.entries, rr)
240 if !math.IsInf(e.Fixed(), 1) || e.AttemptsAdmitted(1e9) != 0 {
241 t.Errorf("%s: fixed %v, %d attempts; want refused", name, e.Fixed(), e.AttemptsAdmitted(1e9))
242 }
243 }
244}
245