Keep a question's slot until it expires while the router has not recorded it (issue 589, review 107)
mesh/merge-gate head c4eebce598cb merged into trunk 9c9a76d220f8 — pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes …
mesh/repo-check head c4eebce598cb merged into trunk 9c9a76d220f8 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/merge-gate head c4eebce598cb merged into trunk 9c9a76d220f8 — pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes …
mesh/repo-check head c4eebce598cb merged into trunk 9c9a76d220f8 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
Freeing the slot two minutes after asking could let a backlogged router still show the question after the session read it as ended, and let a third question in. The seat's queue offers work again after an error and the router drops a cancel of an ask it has not recorded, so a cancel cannot take it back; once expired, the router refuses it if taken late. The slot test now uses a stale revision, so a plain write in place of the compare-and-set fails it.
This commit is contained in:
@@ -43,10 +43,11 @@ const QuestionPrefix = "q-"
|
||||
// create or by compare-and-set, so two nodes asking at once cannot both take the last one.
|
||||
const MostQuestions = asks.MostOpen - 1
|
||||
|
||||
// RouterHoldsWithin is how long after a question is asked the router has recorded it: it records an ask before it
|
||||
// takes its work. A question it holds no record of after that was refused (and the refusal not heard here) or never
|
||||
// taken: it no longer holds a slot, and reads as ended unless a record of it appears after all.
|
||||
var RouterHoldsWithin = 2 * time.Minute
|
||||
// A question the router holds no record of, and whose refusal was not heard here, keeps its slot until it expires:
|
||||
// the router may still have it in its queue (down, or backlogged), and neither the queue's order nor a cancel can
|
||||
// take it back — the queue offers a piece of work again after an error, so a cancel may be taken before the ask it
|
||||
// cancels, and a cancel of an ask not yet recorded is dropped. Once the question has expired the router refuses it
|
||||
// when it is taken (asks.Ask.Check), so the slot is free and the question has ended.
|
||||
|
||||
// slotKey is the key of one of the MostQuestions slots: not a question's key, so no question is ever read from it.
|
||||
func slotKey(n int) string { return fmt.Sprintf("slot-%d", n) }
|
||||
@@ -381,8 +382,8 @@ func labelsOf(q Question) string {
|
||||
return strings.Join(out, ", ")
|
||||
}
|
||||
|
||||
// questionOpen says whether a question still waits: kept, asked, not ended, not expired, and held by the router or
|
||||
// asked less than RouterHoldsWithin ago. An unreadable record is open: a slot is never freed on a guess.
|
||||
// questionOpen says whether a question still waits: kept, asked, not ended, not expired, and open in the router's
|
||||
// record or not recorded yet. An unreadable record is open: a slot is never freed on a guess.
|
||||
func questionOpen(store QuestionStore, bus AskBus, id string, now time.Time) (bool, error) {
|
||||
q, ok, err := readQuestionErr(store, id)
|
||||
if err != nil {
|
||||
@@ -396,7 +397,7 @@ func questionOpen(store QuestionStore, bus AskBus, id string, now time.Time) (bo
|
||||
return true, err
|
||||
}
|
||||
if entry == nil {
|
||||
return now.Sub(q.At) < RouterHoldsWithin, nil
|
||||
return true, nil
|
||||
}
|
||||
var rec routerRecord
|
||||
if json.Unmarshal(entry.Value, &rec) != nil {
|
||||
@@ -559,9 +560,15 @@ func ReadAnswer(store QuestionStore, bus AskBus, key string, now time.Time) (map
|
||||
switch {
|
||||
case entry == nil && q.Ended != "":
|
||||
out["state"], out["words"] = AnswerEnded, q.Ended
|
||||
case entry == nil && now.Sub(q.At) >= RouterHoldsWithin:
|
||||
out["state"], out["words"] = AnswerEnded, "the router holds no record of it: refused (its refusal was not heard "+
|
||||
"here) or never taken; nothing waits for the operator's answer"
|
||||
case entry == nil && !now.Before(q.Expires):
|
||||
out["state"], out["words"] = AnswerEnded, "expired without the router recording it: refused (its refusal was not "+
|
||||
"heard here) or never taken; the router refuses it now if it is taken late"
|
||||
out["now"] = "do what you said: " + q.OnExpiry
|
||||
case entry == nil:
|
||||
out["state"] = AnswerWaiting
|
||||
out["words"] = "not yet recorded by the router: it may still be in the router's queue, or was refused without the " +
|
||||
"refusal being heard here. It holds its slot, and the operator may still be asked, until it expires at " +
|
||||
q.Expires.Format(time.RFC3339)
|
||||
case entry == nil, rec.State == "open", rec.Warrant == nil:
|
||||
out["state"] = AnswerWaiting
|
||||
if q.Ended != "" {
|
||||
|
||||
Reference in New Issue
Block a user