Merge pull request 'compose-candidate reads the candidate by its group, as the indexer stores it' (#199) from fix/compose-candidate-key into main
This commit was merged in pull request #199.
This commit is contained in:
@@ -575,13 +575,16 @@ func h8candidate(t *testing.T, b *itBed, fp, running string) {
|
||||
heads, _ := delivery.HeadsHash([]delivery.Head{head})
|
||||
pulls := []delivery.CandidatePull{{Head: head, Modules: []string{"web"},
|
||||
Fps: map[string]delivery.CandidateModule{"web": {FP2: fpIt("c"), Manifest: digest}}}}
|
||||
craw, err := delivery.EncodeCandidate(delivery.Candidate{Group: "novox/web#3", HeadsHash: heads, Pulls: pulls,
|
||||
// The indexer keys the candidate by its group (a branch name, slash allowed); the checker asks by the composition key.
|
||||
const group = "fix/3-web"
|
||||
craw, err := delivery.EncodeCandidate(delivery.Candidate{Group: group, HeadsHash: heads, Pulls: pulls,
|
||||
FPSet: delivery.FingerprintSet(pulls)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key, _ := delivery.CompositionKey("novox/web#3", heads)
|
||||
if _, err := cands.Put(key, craw); err != nil {
|
||||
ckey, _ := delivery.CandidatesKey(group)
|
||||
key, _ := delivery.CompositionKey(group, heads)
|
||||
if _, err := cands.Put(ckey, craw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
before, _ := b.composed.Status()
|
||||
|
||||
@@ -16,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
// **A check's candidate, composed in shadow on a call** (novox/hq ADR 0593 §7): the node seat's verb compose-candidate.
|
||||
// Given the key of the indexer's candidate (delivery.CandidatesBucket, `<group>.<heads hash>`), the node composes itself
|
||||
// Given the check's composition key (`<group>.<heads hash>`), it reads the group's candidate from the indexer
|
||||
// (delivery.CandidatesBucket, keyed by the group alone, delivery.CandidatesKey) at those heads, and composes itself
|
||||
// twice from what its shadow holds (placement, records, the indexer's modules, its config values), with its own sealed
|
||||
// values: once as it is, and once with the candidate's modules laid over the indexer's (each at the candidate's
|
||||
// fingerprint, its manifest by digest and the indexer's verdict on it, ADR 0578 §3; a module the candidate removes left
|
||||
@@ -47,7 +48,14 @@ func (s *Shadow) ComposeCandidate(ctx context.Context, ask delivery.ComposeCandi
|
||||
a.Verdict = delivery.ComposeCandidateUnready
|
||||
return a
|
||||
}
|
||||
if _, _, err := delivery.ParseCompositionKey(ask.Candidate); err != nil {
|
||||
group, heads, err := delivery.ParseCompositionKey(ask.Candidate)
|
||||
if err != nil {
|
||||
return refuse(err.Error())
|
||||
}
|
||||
// The ask names the check (the composition key, `<group>.<heads hash>`); the indexer keys the group's candidate by
|
||||
// the group alone (delivery.CandidatesKey) and holds its heads hash in the value.
|
||||
ckey, err := delivery.CandidatesKey(group)
|
||||
if err != nil {
|
||||
return refuse(err.Error())
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, delivery.ComposeAnswerBound)
|
||||
@@ -56,7 +64,7 @@ func (s *Shadow) ComposeCandidate(ctx context.Context, ask delivery.ComposeCandi
|
||||
return unready(errNotReady.Error())
|
||||
}
|
||||
base := c.scratch()
|
||||
raw, err := base.get(ctx, delivery.CandidatesBucket, ask.Candidate)
|
||||
raw, err := base.get(ctx, delivery.CandidatesBucket, ckey)
|
||||
if err != nil {
|
||||
return unready(fmt.Sprintf("the indexer's candidate %s cannot be read: %v", ask.Candidate, err))
|
||||
}
|
||||
@@ -67,6 +75,11 @@ func (s *Shadow) ComposeCandidate(ctx context.Context, ask delivery.ComposeCandi
|
||||
if err != nil {
|
||||
return refuse(fmt.Sprintf("the indexer's candidate %s cannot be read: %v", ask.Candidate, err))
|
||||
}
|
||||
if cand.HeadsHash != heads {
|
||||
// The group moved to other heads: the check asked is replaced, and this node composes not a candidate it was
|
||||
// not asked about. No answer (ADR 0593 §9), never a verdict on the wrong heads.
|
||||
return unready(fmt.Sprintf("the indexer's candidate of the group %s is at heads %s, not %s: the check asked is replaced", group, cand.HeadsHash, heads))
|
||||
}
|
||||
laid := c.scratch()
|
||||
touched, err := laid.layCandidate(ctx, cand)
|
||||
if err != nil {
|
||||
|
||||
@@ -29,21 +29,24 @@ func TestTheNodeComposesACandidateOnAsk(t *testing.T) {
|
||||
key, _ := delivery.ManifestsKey(d)
|
||||
b.kv[delivery.ManifestsBucket+"/"+key] = man
|
||||
}
|
||||
candidateOf := func(manifest string) string {
|
||||
// The indexer keys a candidate by its group alone (delivery.CandidatesKey, the branch name escaped, a slash
|
||||
// allowed), its heads hash inside the value; the checker asks by the composition key, `<group>.<heads hash>`.
|
||||
candidateOf := func(group, manifest string) string {
|
||||
head := delivery.Head{Repository: "novox/x", Number: 7, Head: "c0ffee" + manifest[len(manifest)-6:]}
|
||||
heads, _ := delivery.HeadsHash([]delivery.Head{head})
|
||||
pulls := []delivery.CandidatePull{{Head: head, Modules: []string{"lemurs"},
|
||||
Fps: map[string]delivery.CandidateModule{"lemurs": {FP2: fpNew, Manifest: manifest}}}}
|
||||
raw, err := delivery.EncodeCandidate(delivery.Candidate{Group: "novox/x#7", HeadsHash: heads, Pulls: pulls,
|
||||
raw, err := delivery.EncodeCandidate(delivery.Candidate{Group: group, HeadsHash: heads, Pulls: pulls,
|
||||
FPSet: delivery.FingerprintSet(pulls)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key, _ := delivery.CompositionKey("novox/x#7", heads)
|
||||
b.kv[delivery.CandidatesBucket+"/"+key] = raw
|
||||
ckey, _ := delivery.CandidatesKey(group)
|
||||
b.kv[delivery.CandidatesBucket+"/"+ckey] = raw
|
||||
key, _ := delivery.CompositionKey(group, heads)
|
||||
return key
|
||||
}
|
||||
accepted, refusedKey := candidateOf(digest2), ""
|
||||
accepted, refusedKey := candidateOf("fix/940-no-retire-on-partial-read", digest2), ""
|
||||
{
|
||||
// The same change, its manifest refused by the indexer's judge.
|
||||
refusedMan := strings.Replace(lemurs2, `"version":"2"`, `"version":"3"`, 1)
|
||||
@@ -58,7 +61,7 @@ func TestTheNodeComposesACandidateOnAsk(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b.kv[delivery.VerdictsBucket+"/"+vkey] = v
|
||||
refusedKey = candidateOf(d3)
|
||||
refusedKey = candidateOf("fix/941-refused", d3)
|
||||
}
|
||||
|
||||
env := b.env()
|
||||
@@ -114,6 +117,13 @@ func TestTheNodeComposesACandidateOnAsk(t *testing.T) {
|
||||
if m := s.ComposeCandidate(ctx, delivery.ComposeCandidateAsk{Candidate: missing}); !strings.Contains(m.Refused, "holds no candidate") {
|
||||
t.Errorf("a candidate the indexer does not hold answered %+v", m)
|
||||
}
|
||||
// The group's candidate moved to other heads since the ask: the node composes not the one asked, and answers
|
||||
// unready (no answer), naming the heads it holds.
|
||||
moved, _ := delivery.CompositionKey("fix/940-no-retire-on-partial-read", "0123456789ab")
|
||||
if m := s.ComposeCandidate(ctx, delivery.ComposeCandidateAsk{Candidate: moved}); m.Verdict != delivery.ComposeCandidateUnready ||
|
||||
!strings.Contains(m.Refused, "heads") {
|
||||
t.Errorf("a candidate at other heads answered %+v", m)
|
||||
}
|
||||
ended, end := context.WithCancel(ctx)
|
||||
end()
|
||||
if e := s.ComposeCandidate(ended, delivery.ComposeCandidateAsk{Candidate: accepted}); e.Verdict != delivery.ComposeCandidateUnready {
|
||||
|
||||
Reference in New Issue
Block a user