The indexer binds its taken state when it appears, as issue 1790 asks of every state a delivery adds: an indexer started before the bus holds it still takes merges in and says the key it did not write (issue 1280, ADR 0556 clause 1)
mesh/check checking c12b870104e8 on bd107d20154c | novox/mesh-delivery#80 in group fix/delivery-records-true
mesh/delivery merged: source.main moved to novox/mesh-delivery@4af36c69a761
mesh/delivery-group group fix/delivery-records-true merged: every member is merged

This commit is contained in:
2026-10-26 22:25:39 +01:00
parent 368204115c
commit c12b870104
4 changed files with 27 additions and 7 deletions
@@ -305,10 +305,9 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
if err != nil {
return err
}
taken, err := process.EssentialBucket(ctx, js, "indexer", delivery.TakenBucket, "say the merge a repository's modules were taken in at")
if err != nil {
return err
}
// Its taken state (ADR 0556 clause 1) is bound when it appears, as every state a delivery adds (issue 1790): until then
// a merge's taken key is said and not written, and the rest of the indexer runs.
taken := process.AwaitBucket(ctx, js, conn, delivery.TakenBucket, "no repository's taken key is written", logger.Printf)
// Its placement state (ADR 0549 §2) is bound when it appears, as every state a delivery adds (issue 1790): until then a
// catalogue merge's placement is said and not written, and the rest of the indexer runs.
placement := process.AwaitBucket(ctx, js, conn, delivery.PlacementBucket, "no catalogue merge's placement is written", logger.Printf)
@@ -345,7 +344,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
return err
}
defer calls.Close()
ix := &index.Indexer{Store: kvStore{candidates}, Modules: kvStore{modules}, Manifests: kvStore{manifests}, Taken: kvStore{taken}, Builds: known, Logf: logger.Printf,
ix := &index.Indexer{Store: kvStore{candidates}, Modules: kvStore{modules}, Manifests: kvStore{manifests}, Taken: awaitedStore{taken}, Builds: known, Logf: logger.Printf,
Named: namedVia(calls.Call), Trunk: trunkVia(calls.Call), Contains: containsVia(calls.Call), Read: readVia(calls.Call),
Placement: awaitedStore{placement}, Say: func(event string, body any) error { return calls.Emit(event, body) }}
// The gate's source bucket (mesh-delivery-gate_source) comes with the gate (release 3e, novox/hq ADR 0450 §1): its key
@@ -17,8 +17,9 @@ const (
// memStore is the candidates bucket's compare-and-set behaviour in memory: a revision per write, a conflict when the
// revision read is no longer the key's. Writes counts the writes that went through; beforeWrite lets a test change the
// key under the indexer.
// key under the indexer; unbound, when set, is every read's error (a state not yet on the bus).
type memStore struct {
unbound error
vals map[string][]byte
revs map[string]uint64
next uint64
@@ -29,6 +30,9 @@ type memStore struct {
func newMem() *memStore { return &memStore{vals: map[string][]byte{}, revs: map[string]uint64{}} }
func (m *memStore) Get(_ context.Context, key string) ([]byte, uint64, bool, error) {
if m.unbound != nil {
return nil, 0, false, m.unbound
}
v, ok := m.vals[key]
return v, m.revs[key], ok, nil
}
@@ -8,6 +8,7 @@ import (
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/process"
)
const merge = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@@ -111,6 +112,13 @@ func TestAnUnchangedModuleWritesNothing(t *testing.T) { // 1239 (unchanged value
m.Merge = next
return ix.Apply(context.Background(), m)
}},
{name: "a merge while the taken state is not on the bus yet is still taken in", writes: 1, commit: map[string]string{"a": next, "b": merge, "c": merge}, taken: merge,
then: func(ix *Indexer) error { // issue 1790: the state is bound when it appears, so the indexer runs before it does
ix.Taken = &memStore{unbound: process.ErrNotYet}
m := mergedMsg(repo, map[string]string{"modules/a": "treeA2", "modules/b": "treeB", "modules/c": "treeC"})
m.Merge = next
return ix.Apply(context.Background(), m)
}},
{name: "an earlier merge heard again after a later one leaves taken", writes: 0, commit: map[string]string{"a": merge, "b": merge, "c": merge}, taken: merge,
then: func(ix *Indexer) error {
ix.Contains = func(_ context.Context, _, commit, in string) (bool, error) { return commit == next && in == merge, nil }
@@ -2,9 +2,11 @@ package index
import (
"context"
"errors"
"time"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/process"
)
// What the indexer took in (novox/hq ADR 0556 clause 1, issue 1280): after a merged message is applied whole, the key of
@@ -23,7 +25,7 @@ func (ix *Indexer) writeTaken(ctx context.Context, f delivery.Forge) error {
ix.logf("%s at %s: no taken key is written: %v", f.Repository, f.Merge, err)
return nil
}
return write(ctx, ix.Taken, key, func(value []byte, found bool) ([]byte, bool, error) {
err = write(ctx, ix.Taken, key, func(value []byte, found bool) ([]byte, bool, error) {
if found {
if cur, err := delivery.DecodeTaken(value); err == nil {
if cur.Merge == f.Merge {
@@ -48,4 +50,11 @@ func (ix *Indexer) writeTaken(ctx context.Context, f delivery.Forge) error {
data, err := delivery.EncodeTaken(delivery.Taken{Repository: delivery.RepositoryName(f.Repository), Merge: f.Merge, At: now().UTC()})
return data, false, err
})
if errors.Is(err, process.ErrNotYet) {
// The state is not on the bus yet (a delivery that adds it, bound when it appears as issue 1790 says): said, and
// the merge's modules are still taken in; the next merge of the repository writes its key once the state appears.
ix.logf("%s at %s: its taken key is not written: %v", f.Repository, f.Merge, err)
return nil
}
return err
}