mesh-issues: an open whose write may have landed never says nothing was opened, and a module must itself be registered
mesh/merge-gate pass: builds new: modules/mesh-issues, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered

From the review: a taken key that cannot be read back, or a write never
confirmed, now tells the caller to get the number before opening again; an
owner entry naming a module is owned only by that module registered; link
targets get the secret guard; line separators are refused in one-line fields;
the acceptance test now proves the counter's own updates were contended.
This commit is contained in:
jochen
2026-10-10 12:56:16 +02:00
parent 2493e3e048
commit 6f2c76ee71
5 changed files with 100 additions and 23 deletions
+18 -8
View File
@@ -40,7 +40,7 @@ const (
// caller is told, and nothing was written.
const (
// counterRounds × counterAttempts is how many times one open tries to advance the counter.
counterRounds = 12
counterRounds = 20
counterAttempts = 8
// issueAttempts is how many times a change to one issue is read and written again.
issueAttempts = 16
@@ -129,9 +129,6 @@ func (t *Tracker) Open(a OpenAsk) (*Opened, error) {
if record == "" {
is.Status = StatusOpen
}
if size, _ := json.Marshal(is); len(size) > MostIssueBytes-1024 {
return nil, fmt.Errorf("the issue is %d bytes; one issue is kept in at most %d", len(size), MostIssueBytes)
}
n, err := t.next()
if err != nil {
@@ -161,13 +158,22 @@ func (t *Tracker) create(is *Issue) error {
key := keyOf(is.Number)
var err error
for i := 0; i < createAttempts; i++ {
if i > 0 {
t.Sleep(jitter(time.Duration(i) * 50 * time.Millisecond))
}
_, err = t.Store.Create(IssuesState, key, is)
if err == nil {
return nil
}
if errors.Is(err, stdio.ErrChanged) {
// Ours, written by an attempt whose answer was lost?
if there, _, rerr := read(t.Store, is.Number); rerr == nil && there.Opening == is.Opening {
there, _, rerr := read(t.Store, is.Number)
if rerr != nil {
t.Logf("[mesh-issues] issue %d is kept and could not be read back: %v", is.Number, rerr)
return fmt.Errorf("issue %d may have been written by this open: the key is taken and could not be read "+
"back (%v). Call get %d before opening it again, so it is not opened twice", is.Number, rerr, is.Number)
}
if there.Opening == is.Opening {
return nil
}
t.Logf("[mesh-issues] the counter handed out %d and an issue %d is already kept: not overwritten", is.Number, is.Number)
@@ -176,9 +182,13 @@ func (t *Tracker) create(is *Issue) error {
"and tell the operator the counter is behind the issues kept", is.Number, is.Number, is.Number)
}
}
t.Logf("[mesh-issues] number %d was taken and the issue could not be written: %v", is.Number, err)
return fmt.Errorf("number %d was taken and the issue could not be written (%v); nothing was opened and %d "+
"stays unused. Open it again", is.Number, err, is.Number)
// A write that timed out may still have landed: say so, never "nothing was opened".
t.Logf("[mesh-issues] number %d was taken and the issue's write was not confirmed: %v", is.Number, err)
if there, _, rerr := read(t.Store, is.Number); rerr == nil && there.Opening == is.Opening {
return nil
}
return fmt.Errorf("number %d was taken and the issue's write was not confirmed (%v): it may have been written. "+
"Call get %d before opening it again; if there is no issue %d, it stays unused", is.Number, err, is.Number, is.Number)
}
// next takes the next issue number: the counter advanced only at the revision read, so two openers — in