A layout run again sets no pointer back and skips records already moved (issue 1121)

The live start recorded its layout in chain_moved, so the marker is missing and the layout runs once more over the old keys. It wrote newest, worked, held, commit and source over the pointers of every build made since. Pointers are now made only where missing, and a record already under its new key is not read again.
This commit is contained in:
2026-10-18 13:41:25 +02:00
parent e76ba966b3
commit 7f074d9c94
2 changed files with 56 additions and 1 deletions
+23 -1
View File
@@ -1206,6 +1206,28 @@ func (i *Inventory) layoutBuilds(ctx context.Context, c *chain.Chain, say func(s
if r.Records%1000 == 0 {
say("laying the builds out: %d of %d records, %d keys written so far", r.Records, len(ids), r.Written)
}
// A record already under its new key is moved: a run again (the start before this one laid it out) neither reads its
// manifest nor writes it, and **no pointer is set back**: they are only made where missing.
own := chain.BuildKey(rec.Module, rec.ID)
if rec.State == chain.BuildAsked {
own = chain.RequestKey(rec.ID)
}
if _, there, err := c.Builds.GetRaw(ctx, own); err != nil {
return r, err
} else if there {
r.Present++
if rec.State != chain.BuildAsked {
if wrote, err := i.pointTo(ctx, c, chain.IDKey(rec.ID), idPointer(rec.Module), false); err != nil {
return r, err
} else if wrote {
r.Written++
}
}
if rec.State != chain.BuildAsked && rec.Module != "" {
byModule[rec.Module] = append(byModule[rec.Module], rec)
}
continue
}
// A manifest already written in this run is not written again.
digest := ""
if body := manifestBytes([]byte(rec.Manifest)); body != nil && c.Kept {
@@ -1265,7 +1287,7 @@ func (i *Inventory) layoutBuilds(ctx context.Context, c *chain.Chain, say func(s
return r, err
}
for _, p := range points {
wrote, err := i.pointTo(ctx, c, p.key, p.value, true)
wrote, err := i.pointTo(ctx, c, p.key, p.value, false)
if err != nil {
return r, err
}
@@ -426,3 +426,36 @@ func TestTheChainIsRefusedUntilTheBuildsAreLaidOutAndTheLayoutIsDoneOnce(t *test
t.Fatalf("the second layout changed the bucket: %v", err)
}
}
// A layout run again — the live start recorded the first in chain_moved, not in the marker — never sets a pointer back to an
// older build, and does not read a manifest of a record already under its new key.
func TestALayoutRunAgainLeavesThePointersOfTheBuildsMadeSince(t *testing.T) {
counting := &readCounting{Backend: chain.NewMemory()}
inv, c := layoutChain(counting)
ctx := context.Background()
for n := 1; n <= 2; n++ {
old := legacyBuild(n, "alpha", fmt.Sprintf("c%d", n))
if _, err := c.Builds.Create(ctx, old.ID, old); err != nil {
t.Fatal(err)
}
}
if _, err := inv.layoutBuilds(ctx, c, nil); err != nil {
t.Fatal(err)
}
if err := inv.chainRecordBuild(ctx, c, layoutBuild("alpha", 3, "c3", "s3")); err != nil {
t.Fatal(err)
}
counting.gets.Store(0)
r, err := inv.layoutBuilds(ctx, c, nil)
if err != nil || r.Written != 0 {
t.Fatalf("the second layout %+v: %v", r, err)
}
for _, key := range []string{"alpha.newest", "alpha.worked", "alpha.held"} {
if e, _, _ := counting.Get(ctx, key); string(e.Value) != "build-3" {
t.Errorf("%s = %q after the second layout; want build-3", key, e.Value)
}
}
if e, _, _ := counting.Get(ctx, "alpha.commit.c3"); pointedID(e.Value) != "build-3" {
t.Errorf("commit c3 = %s", e.Value)
}
}