mesh/delivery-group group fix/1966-call-made-once rejected: composed together: the checker's verdict at the members' heads (c54b617d200d): mesh-delivery-checker
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: mesh-delivery-checker: no published of the tagged version of go-meshconditions of mesh-health-and-re… | 13998c2362d3 on 5b63c67ce852
The node-engine's natsLink publishes through the old client, which says no responders as nats.ErrNoStreamResponse: sayOnce gave up at once. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
71 lines
3.6 KiB
Go
71 lines
3.6 KiB
Go
package busretry
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
"github.com/nats-io/nats.go/jetstream"
|
|
)
|
|
|
|
// Issue 774 (bus blackouts at every user-list reload): only a request that reached no one is asked again, within the
|
|
// bound; a blackout probe that says the bus answers ends the wait at once.
|
|
func TestABusThatAnswersNothingIsWaitedOut(t *testing.T) {
|
|
for _, row := range []struct {
|
|
name string
|
|
errs []error // what each try answers; the last repeats
|
|
blackout func() bool
|
|
tries int // at least
|
|
atMost int
|
|
delivered bool
|
|
repeatable bool // DoRepeatable, a call marked safe to make again (ADR 0668)
|
|
}{
|
|
{"no responders, then an answer", []error{nats.ErrNoResponders, nats.ErrNoResponders, nil}, nil, 3, 3, true, false},
|
|
{"jetstream not enabled, then an answer", []error{jetstream.ErrJetStreamNotEnabled, nil}, nil, 2, 2, true, false},
|
|
{"said in words by a runtime", []error{errors.New("nats: no responders available for request"), nil}, nil, 2, 2, true, false},
|
|
{"a timeout may have been delivered: never asked again", []error{context.DeadlineExceeded}, nil, 1, 1, false, false},
|
|
{"an answer that refuses is an answer", []error{errors.New("refused")}, nil, 1, 1, false, false},
|
|
{"nobody answers on a bus that answers: the callee is not running", []error{nats.ErrNoResponders}, func() bool { return false }, 1, 1, false, false},
|
|
{"blacked out past the bound: said", []error{jetstream.ErrJetStreamNotEnabledForAccount}, func() bool { return true }, 2, 1000, false, false},
|
|
// Issue 1966, ADR 0668: a call kept and safe to make again is asked again on a timeout, and on no responders
|
|
// from a callee that restarts on a bus that answers; Do still asks neither.
|
|
{"repeatable: timed out twice, then an answer", []error{context.DeadlineExceeded, nats.ErrTimeout, nil}, nil, 3, 3, true, true},
|
|
{"repeatable: no responders with no probe, then an answer", []error{nats.ErrNoResponders, nil}, nil, 2, 2, true, true},
|
|
{"repeatable: a callee's answer is an answer", []error{&Answered{Words: "context deadline exceeded"}}, nil, 1, 1, false, true},
|
|
{"repeatable: nobody answers and the probe says the bus answers", []error{nats.ErrNoResponders}, func() bool { return false }, 1, 1, false, true},
|
|
{"repeatable: no response from the stream, either client's, then an answer", []error{nats.ErrNoStreamResponse, jetstream.ErrNoStreamResponse, nil}, nil, 3, 3, true, true},
|
|
{"timed out past the bound: said", []error{context.DeadlineExceeded}, nil, 2, 1000, false, true},
|
|
} {
|
|
// A row that ends in an answer waits on a bound of seconds, which no slow machine reaches; only the row that
|
|
// runs past it has a short one, and asks only that it was asked again at least once.
|
|
bound := 10 * time.Second
|
|
if strings.Contains(row.name, "past the bound") {
|
|
bound = 100 * time.Millisecond
|
|
}
|
|
t.Cleanup(Waits(bound, time.Millisecond, 4*time.Millisecond))
|
|
tries := 0
|
|
do := Do[int]
|
|
if row.repeatable {
|
|
do = DoRepeatable[int]
|
|
}
|
|
_, err := do("the test", func() (int, error) {
|
|
e := row.errs[min(tries, len(row.errs)-1)]
|
|
tries++
|
|
return tries, e
|
|
}, row.blackout)
|
|
if tries < row.tries || tries > row.atMost || (err == nil) != row.delivered {
|
|
t.Errorf("%s: %d tries, %v", row.name, tries, err)
|
|
}
|
|
if strings.HasPrefix(row.name, "blacked out") && (!Undelivered(err) || !strings.Contains(fmt.Sprint(err), "the bus answered nothing")) {
|
|
t.Errorf("%s: %v", row.name, err)
|
|
}
|
|
if strings.HasPrefix(row.name, "timed out") && (!TimedOut(err) || !strings.Contains(fmt.Sprint(err), "the test: no answer within its deadline")) {
|
|
t.Errorf("%s: %v", row.name, err)
|
|
}
|
|
}
|
|
}
|