From e648955a94bfdccb451377765c5e73382cc86772 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 15:03:35 +0100 Subject: [PATCH] Join item 3: the indexer's empty manifest-accepted takes its id from the event (EventID names module and revision) delivery.ManifestAccepted.EventID now names, for an event with no digest, the module and the event's revision (J3, ruling A5(d)), so the indexer drops its own left id. The revision is the modules state's revision of the value that left (write hands the revision it read), so two leaves of one module are two events; asked again by hand for a module no longer held it is 0, and the again suffix keeps it unique. Row in TestAMergeThatRemovesAModuleDeletesItsKey (failed first: Revision 0, id naming the last digest). Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- .../internal/index/accepted.go | 13 +++++++------ .../internal/index/candidates.go | 6 +++++- .../mesh-delivery-indexer/internal/index/modules.go | 5 +++-- .../internal/index/remove_test.go | 4 +++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/modules/mesh-delivery-indexer/internal/index/accepted.go b/modules/mesh-delivery-indexer/internal/index/accepted.go index 096a921e..c15c1606 100644 --- a/modules/mesh-delivery-indexer/internal/index/accepted.go +++ b/modules/mesh-delivery-indexer/internal/index/accepted.go @@ -39,11 +39,12 @@ func acceptedOf(digest string, v delivery.ManifestVerdict, revision uint64, raw return a } -// leftOf is the empty manifest-accepted for module, which left the trunk, judged by judgeID; its event id names the module -// and the manifest it last held, since its body names no digest. -func leftOf(module, lastDigest, judgeID string) (delivery.ManifestAccepted, string) { - a := delivery.ManifestAccepted{V: delivery.Version, Module: module, Judge: judgeID, BusState: []catalogue.BusStateEntry{}} - return a, delivery.ManifestAcceptedEvent + "-left-" + module + "-" + lastDigest +// leftOf is the empty manifest-accepted for module, which left the trunk, judged by judgeID; revision is the modules +// state's revision of the value that left (0 when none is held), so its event id (delivery.ManifestAccepted.EventID, the +// module and the revision, since its body names no digest) differs for each leave. +func leftOf(module string, revision uint64, judgeID string) (delivery.ManifestAccepted, string) { + a := delivery.ManifestAccepted{V: delivery.Version, Module: module, Judge: judgeID, BusState: []catalogue.BusStateEntry{}, Revision: revision} + return a, a.EventID() } // sayAccepted says a with the event id id, once more when the first publish fails; the module's finding is written when @@ -109,7 +110,7 @@ func (ix *Indexer) sayFor(ctx context.Context, module string, allowLeft, again b case !ok && !allowLeft: return "", fmt.Errorf("the indexer holds no module %s, so it has no accepted verdict to say", module) case !ok: - a, id = leftOf(module, "", ix.judgeID()) + a, id = leftOf(module, 0, ix.judgeID()) case m.Manifest == "" || ix.Verdicts == nil || ix.Manifests == nil: return "", fmt.Errorf("%s has no accepted verdict the indexer holds", module) default: diff --git a/modules/mesh-delivery-indexer/internal/index/candidates.go b/modules/mesh-delivery-indexer/internal/index/candidates.go index 123fa52c..2ba2e085 100644 --- a/modules/mesh-delivery-indexer/internal/index/candidates.go +++ b/modules/mesh-delivery-indexer/internal/index/candidates.go @@ -322,7 +322,8 @@ func (ix *Indexer) applyOpen(ctx context.Context, f delivery.Forge) error { // write changes one key of a store by compare-and-set: compute is given the value read (found says there is one) and // answers the new value, or del to remove the key, or neither for no change. A conflict reads again, up to writeTries. -func write(ctx context.Context, st Store, key string, compute func(value []byte, found bool) (next []byte, del bool, err error)) error { +// read, when given, is set to the revision of the value compute was last given. +func write(ctx context.Context, st Store, key string, compute func(value []byte, found bool) (next []byte, del bool, err error), read ...*uint64) error { for try := 0; try < writeTries; try++ { value, revision, found, err := st.Get(ctx, key) if err != nil { @@ -332,6 +333,9 @@ func write(ctx context.Context, st Store, key string, compute func(value []byte, if err != nil { return err } + for _, r := range read { + *r = revision + } switch { case next == nil && !del: return nil diff --git a/modules/mesh-delivery-indexer/internal/index/modules.go b/modules/mesh-delivery-indexer/internal/index/modules.go index 5c9de69a..3c8307e2 100644 --- a/modules/mesh-delivery-indexer/internal/index/modules.go +++ b/modules/mesh-delivery-indexer/internal/index/modules.go @@ -236,6 +236,7 @@ func (ix *Indexer) dropRemoved(ctx context.Context, repository string, held map[ elsewhere[key] = f continue } + var left uint64 // the revision of the value deleted, which names its leave (leftOf) err := write(ctx, ix.Modules, key, func(value []byte, found bool) ([]byte, bool, error) { if !found { return nil, false, nil @@ -244,13 +245,13 @@ func (ix *Indexer) dropRemoved(ctx context.Context, repository string, held map[ return nil, false, nil } return nil, true, nil - }) + }, &left) if err != nil { return fmt.Errorf("modules.%s: %w", key, err) } ix.logf("modules.%s deleted: %s holds no module of that name at its trunk (issue 1900)", key, repository) // The bus module hears the module asks nothing now, so its entries reach orphans (design 25 ยง 12.2, DLV-I9). - a, id := leftOf(key, cur.Manifest, ix.judgeID()) + a, id := leftOf(key, left, ix.judgeID()) _ = ix.sayAccepted(ctx, a, id) } for _, key := range handed { diff --git a/modules/mesh-delivery-indexer/internal/index/remove_test.go b/modules/mesh-delivery-indexer/internal/index/remove_test.go index 1b14c189..86475653 100644 --- a/modules/mesh-delivery-indexer/internal/index/remove_test.go +++ b/modules/mesh-delivery-indexer/internal/index/remove_test.go @@ -37,7 +37,9 @@ func TestAMergeThatRemovesAModuleDeletesItsKey(t *testing.T) { // 1900/AC1 for i, a := range said { if a.Digest == "" { left = append(left, a.Module) - if a.BusState == nil || len(a.BusState) != 0 || !strings.Contains(ids[i], "-left-b") { + // Join item 3: the id is the event's own (delivery.ManifestAccepted.EventID), naming the module and the + // modules state's revision of the value that left, so two leaves of one module are two events. + if a.BusState == nil || len(a.BusState) != 0 || a.Revision == 0 || ids[i] != a.EventID() || !strings.Contains(ids[i], "-left-b-") { t.Errorf("the empty manifest-accepted = %+v, id %s", a, ids[i]) } }