package host import ( "os" "strings" "testing" "github.com/carlosframework/platform/internal/lease" "github.com/carlosframework/platform/internal/moves" "github.com/carlosframework/platform/internal/provision" ) // Final review finding 2, end to end: the target's fresh report priced // T11 and checked T6 for the database it saw, 16 KiB here. The database // then grows to 2.5 GiB before the target reports again, as the source's // local file or as its replica's newest LTX. The source's next round must // not promote on the old report, and the source's report must carry the // size it saw. The target's next report prices the new size. func TestPromotionWaitsForAReportCoveringTheGrownDatabase(t *testing.T) { const grown = 2560 << 20 for name, grow := range map[string]func(t *testing.T, h *phase1Harness){ "local file": func(t *testing.T, h *phase1Harness) { db, err := os.Create(provision.DefaultDBPath(h.cfg.DataDir, p1Host)) if err != nil { t.Fatal(err) } if err := db.Truncate(grown); err != nil { // sparse t.Fatal(err) } db.Close() }, "replica": func(t *testing.T, h *phase1Harness) { newest := ltxForTest(t, 4096, grown/4096, 3, 3) key := "instances/" + p1Host + "/" + h.inc + "/g-cur/0000/0000000000000003-0000000000000003.ltx" h.f.store("data-ie", key, fakeMoveObject{lease.MirrorObject{ETag: "grown", Size: int64(len(newest))}, string(newest)}) }, } { t.Run(name, func(t *testing.T) { h := newPhase1Harness(t) h.start(t, "manual") h.pass(t) h.targetPass(t, h.targetCfg()) tr := h.targetReport(t) if ok, why := tr.Passing(); !ok || tr.DBMiB*(1<<20) != 4*4096 { t.Fatalf("the target's first report: %v %s, priced %v MiB", ok, why, tr.DBMiB) } grow(t, h) h.pass(t) if got := h.read(t, p1Host).Move.State; got != provision.MovePreparing { t.Fatalf("promoted on a report that priced %v MiB after the database grew to 2.5 GiB: %s", tr.DBMiB, got) } sr := h.sourceReport(t) if max(sr.DBBytes, sr.ReplicaBytes) != grown { t.Fatalf("the source report carries DBBytes %d and ReplicaBytes %d, want %d", sr.DBBytes, sr.ReplicaBytes, int64(grown)) } _, why := moves.Ready(0, h.cfg.ReadyDelta, h.targetReport(t), sr, h.clock.Now()) if !strings.HasPrefix(why, "T11: the database has grown to 2560 MiB") { t.Fatalf("Ready's reason: %q", why) } h.targetPass(t, h.targetCfg()) if r := h.targetReport(t); r.DBMiB != 2560 { t.Fatalf("the target's next report priced %v MiB", r.DBMiB) } }) } } // Without growth the same sequence promotes: the control for the test // above, and the replica's size is what the source reports. func TestSourceReportCarriesTheReplicaSize(t *testing.T) { h := newPhase1Harness(t) h.start(t, "manual") h.pass(t) if sr := h.sourceReport(t); sr.ReplicaBytes != 4*4096 { t.Fatalf("ReplicaBytes %d, want the 16 KiB the newest header names", sr.ReplicaBytes) } h.targetPass(t, h.targetCfg()) h.pass(t) if got := h.read(t, p1Host).Move.State; got != provision.MoveReady { t.Fatalf("state %s, want ready", got) } }