The operator, 2026-10-25: only if a module has a new version in commit x is a new value written naming commit x. A merge wrote every module of the repository at the new commit, fingerprint unchanged (about 186 keys per mesh-catalog merge), and the controller's take-in took each in again: twice tonight it flooded. Now a merge that gives a module the value it holds but for the commit writes nothing, and the value keeps the commit its fingerprint first appeared at. The kind: a write that differs only in where it was computed. The other instance, a source revision re-stamping Src on every module at its repository's head after any merge anywhere, now stamps Src once. The trunk head stays the gate's source.main.
90 lines
3.2 KiB
Go
90 lines
3.2 KiB
Go
package index
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"maps"
|
|
"slices"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// The gate's source revision (novox/hq ADR 0437 §5, ADR 0450 §1): source.main names every repository's merged head, and its
|
|
// revision in the bucket is the mesh's source revision. The indexer keeps the latest it has seen. A module is stamped
|
|
// with it (Module.Src) when the head the source names for its repository is the merge commit the module's fingerprint
|
|
// was computed at: from the modules bucket when the merge was heard first, and when the merge is heard after the source.
|
|
|
|
// sourceNow is the latest source revision seen.
|
|
type sourceNow struct {
|
|
rev uint64
|
|
heads map[string]string
|
|
}
|
|
|
|
// srcOf is the source revision that names this merge commit as the repository's head, or 0 when the latest source seen
|
|
// names another head or none has been seen.
|
|
func (ix *Indexer) srcOf(repository, merge string) uint64 {
|
|
ix.mu.Lock()
|
|
defer ix.mu.Unlock()
|
|
if ix.source.rev != 0 && merge != "" && ix.source.heads[repository] == merge {
|
|
return ix.source.rev
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// ApplySource takes a revision of the gate's source.main in. An older revision than the latest seen is left. For each
|
|
// repository the source names, each module built from it whose fingerprint was computed at the head the source names, and
|
|
// that carries no Src yet, is written again with Src the revision (once: a later revision leaves it); a module computed at another commit is left (a merge heard later is stamped
|
|
// when it is written, applyMerged), and a repository none of the indexer's modules is built from writes nothing. The
|
|
// modules of a repository are those the modules bucket holds of it (ADR 0501 clause 6).
|
|
func (ix *Indexer) ApplySource(ctx context.Context, rev uint64, s delivery.Source) error {
|
|
ix.mu.Lock()
|
|
if rev < ix.source.rev {
|
|
ix.mu.Unlock()
|
|
return nil
|
|
}
|
|
ix.source = sourceNow{rev: rev, heads: maps.Clone(s.Heads)}
|
|
ix.mu.Unlock()
|
|
if ix.Modules == nil {
|
|
return ix.RecomputeCandidates(ctx)
|
|
}
|
|
held, err := ix.heldModules(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, key := range slices.Sorted(maps.Keys(held)) {
|
|
repository := ""
|
|
for r := range s.Heads {
|
|
if delivery.SameRepository(r, held[key].Repository) {
|
|
repository = r
|
|
}
|
|
}
|
|
if repository == "" {
|
|
continue
|
|
}
|
|
head := s.Heads[repository]
|
|
err = write(ctx, ix.Modules, key, func(value []byte, found bool) ([]byte, bool, error) {
|
|
if !found {
|
|
return nil, false, nil
|
|
}
|
|
cur, err := delivery.DecodeModule(value)
|
|
if err != nil {
|
|
ix.logf("modules.%s holds what is not a module, left: %v", key, err)
|
|
return nil, false, nil
|
|
}
|
|
// Stamped once, with the first revision naming the commit as the head: a later revision (a merge of another
|
|
// repository) leaves the value as it is, so no revision of the source rewrites a module that did not change.
|
|
if cur.Repository != repository || cur.Commit != head || cur.Src != 0 {
|
|
return nil, false, nil
|
|
}
|
|
cur.Src = rev
|
|
data, err := delivery.EncodeModule(cur)
|
|
return data, false, err
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("modules.%s: %w", key, err)
|
|
}
|
|
}
|
|
// A trunk that moved makes the previews made onto the old one out of date.
|
|
return ix.RecomputeCandidates(ctx)
|
|
}
|