diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/catalog.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/catalog.go index f7e12494..c1470990 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/catalog.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/catalog.go @@ -10,6 +10,7 @@ import ( "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/views" "mesh-delivery-builder/internal/build" @@ -184,6 +185,29 @@ func (c *catalogHalf) dropped(state string) { c.checkReady() } +// pictured are the build keys of the fingerprints the adopted halves' pictures hold, whether or not their entries were told yet. +func (c *catalogHalf) pictured() []string { + c.mu.Lock() + halves := make([]*views.Follower, 0, len(c.halves)) + for _, f := range c.halves { + halves = append(halves, f) + } + c.mu.Unlock() + var keys []string + for _, f := range halves { + for module, raw := range f.Values() { + var e meshcatalog.CatalogEntry + if json.Unmarshal(raw, &e) != nil || e.Fp == "" { + continue + } + if k, err := builds.BuilderBuildKey(module, e.Fp); err == nil { + keys = append(keys, k) + } + } + } + return keys +} + // checkReady calls ready once every adopted half's first view is in. func (c *catalogHalf) checkReady() { c.mu.Lock() diff --git a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go index 6fd473c4..47b89d0e 100644 --- a/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go +++ b/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/main.go @@ -433,7 +433,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro // a holder of the seat that is down holds no other part of it). half.b = b half.ready = func() { - if err := b.Sweep(ctx); err != nil { + if err := b.Sweep(ctx, half.pictured()...); err != nil { errs <- err } } diff --git a/modules/mesh-delivery-builder/internal/build/followups_test.go b/modules/mesh-delivery-builder/internal/build/followups_test.go index ac03518e..b2bfdd0b 100644 --- a/modules/mesh-delivery-builder/internal/build/followups_test.go +++ b/modules/mesh-delivery-builder/internal/build/followups_test.go @@ -46,3 +46,22 @@ func TestSweepLeavesAKeyOfTheModulesCurrentFingerprintForTheBuildToTakeOver(t *t t.Errorf("build = %+v, want done by novox.builder", v) } } + +// A key of a fingerprint a followed half's picture holds is not closed at start, though its entry is not taken in yet: the +// follower is ready before it tells its entries (design 47 G A-107's restart case: the builder put its asks a second time). +func TestSweepLeavesAKeyOfAPicturedFingerprintBuildingBeforeItsEntryIsTakenIn(t *testing.T) { + r := newRig(t, goBundle) + seed(t, r.st, builds.BuilderBuild{State: builds.BuildBuilding, By: "ace.builder", Manifest: manifestD}) + if err := r.b.Sweep(context.Background(), r.key); err != nil { + t.Fatal(err) + } + if v := valueOf(t, r.st, r.key); v.State != builds.BuildBuilding { + t.Errorf("build = %+v, want it left building for the entry to take over", v) + } + if err := r.b.Sweep(context.Background()); err != nil { + t.Fatal(err) + } + if v := valueOf(t, r.st, r.key); v.State != builds.BuildFailed { + t.Errorf("without a picture naming it the key is closed: %+v", v) + } +} diff --git a/modules/mesh-delivery-builder/internal/build/run.go b/modules/mesh-delivery-builder/internal/build/run.go index 3ec4d6f3..e37f226f 100644 --- a/modules/mesh-delivery-builder/internal/build/run.go +++ b/modules/mesh-delivery-builder/internal/build/run.go @@ -321,7 +321,11 @@ func (b *Builder) NodesChanged(ctx context.Context) { b.Reconsider(ctx) } // module no longer has (issue 1660, ADR 0478 clause 6): the key can never be built, since nothing asks for it, and would // stay building for ever. It fails the key naming who left it. A key of the fingerprint the module has now is not // touched: the build takes it over. Call it once the modules bucket has been read, so every module is known. -func (b *Builder) Sweep(ctx context.Context) error { +// +// pictured are the build keys the followed halves' pictures name as current: a follower is ready before it has told its +// entries (views.Follower closes Ready, then calls Changed), so at ready the entries may not be taken in yet, and a key of +// a fingerprint a picture holds is never closed for want of the entry's arrival (the restart that put its asks a second time). +func (b *Builder) Sweep(ctx context.Context, pictured ...string) error { b.init() keys, err := b.Claimer.Store.Keys(ctx) if err != nil { @@ -329,6 +333,9 @@ func (b *Builder) Sweep(ctx context.Context) error { } b.mu.Lock() current := map[string]bool{} + for _, k := range pictured { + current[k] = true + } // What every followed branch carries now is current, not only the first branch's (design 47 G ยง348). for _, held := range []map[string]meshcatalog.ModulesByModuleValue{b.known, b.heard} { for hk, m := range held {