Bind the have bucket once per pass, bound by the queue's timeout, and stop the pass at the first error (issue 1132)
This commit is contained in:
@@ -18,11 +18,14 @@ import (
|
||||
// haveLink is a link with a bucket behind it that keeps each key's revision as the bus does.
|
||||
type haveLink struct {
|
||||
*quietLink
|
||||
hmu sync.Mutex
|
||||
kv map[string]haveEntry
|
||||
sets []string // keys written, in order
|
||||
events []string // "report" and "have <key>", in the order they happened
|
||||
failing bool
|
||||
hmu sync.Mutex
|
||||
kv map[string]haveEntry
|
||||
sets []string // keys written, in order
|
||||
events []string // "report" and "have <key>", in the order they happened
|
||||
failing bool
|
||||
binds int
|
||||
setTries int
|
||||
bindFails, bindStuck bool
|
||||
}
|
||||
|
||||
type haveEntry struct {
|
||||
@@ -44,7 +47,22 @@ func (l *haveLink) Report(ctx context.Context, node string, body []byte) error {
|
||||
|
||||
func (l *haveLink) Health(context.Context, string, []byte) error { return nil }
|
||||
|
||||
func (l *haveLink) HaveGet(_ context.Context, key string) ([]byte, uint64, error) {
|
||||
func (l *haveLink) HaveBind(ctx context.Context) (HaveBucket, error) {
|
||||
l.hmu.Lock()
|
||||
l.binds++
|
||||
stuck, refuse := l.bindStuck, l.bindFails
|
||||
l.hmu.Unlock()
|
||||
if stuck {
|
||||
<-ctx.Done()
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
if refuse {
|
||||
return nil, errors.New("no grant for the have bucket")
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *haveLink) Get(_ context.Context, key string) ([]byte, uint64, error) {
|
||||
l.hmu.Lock()
|
||||
defer l.hmu.Unlock()
|
||||
e, ok := l.kv[key]
|
||||
@@ -54,9 +72,10 @@ func (l *haveLink) HaveGet(_ context.Context, key string) ([]byte, uint64, error
|
||||
return e.value, e.rev, nil
|
||||
}
|
||||
|
||||
func (l *haveLink) HaveSet(_ context.Context, key string, value []byte, revision uint64) error {
|
||||
func (l *haveLink) Set(_ context.Context, key string, value []byte, revision uint64) error {
|
||||
l.hmu.Lock()
|
||||
defer l.hmu.Unlock()
|
||||
l.setTries++
|
||||
if l.failing {
|
||||
return errors.New("the bucket refused")
|
||||
}
|
||||
@@ -289,3 +308,34 @@ func TestASilentReconcileStillWritesTheHaves(t *testing.T) {
|
||||
t.Errorf("an unchanged reconcile wrote %d keys", l.setCount()-before)
|
||||
}
|
||||
}
|
||||
|
||||
// 1132/AC7: a bind that fails, or never answers, writes nothing and returns within the queue's timeout; a write that
|
||||
// fails ends the pass, and is said once.
|
||||
func TestAFailedBindOrWriteEndsThePassAndReturnsInTime(t *testing.T) {
|
||||
for name, set := range map[string]func(*haveLink){
|
||||
"a bind refused": func(l *haveLink) { l.bindFails = true },
|
||||
"a bind that never answers": func(l *haveLink) { l.bindStuck = true },
|
||||
} {
|
||||
l := newHaveLink()
|
||||
set(l)
|
||||
q, said := aHaveQueue(l)
|
||||
q.Timeout = 100 * time.Millisecond
|
||||
start := time.Now()
|
||||
if !q.tell(context.Background(), appliedModules(), applied) {
|
||||
t.Fatalf("%s: the report did not go out", name)
|
||||
}
|
||||
if took := time.Since(start); took > time.Second {
|
||||
t.Errorf("%s: the pass took %s", name, took)
|
||||
}
|
||||
if l.binds != 1 || l.setTries != 0 || len(*said) != 1 {
|
||||
t.Errorf("%s: binds %d, writes tried %d, said %v; want one bind, no write, one line", name, l.binds, l.setTries, *said)
|
||||
}
|
||||
}
|
||||
l := newHaveLink()
|
||||
l.failing = true
|
||||
q, said := aHaveQueue(l)
|
||||
q.tell(context.Background(), appliedModules(), applied)
|
||||
if l.binds != 1 || l.setTries != 1 || len(*said) != 1 {
|
||||
t.Errorf("a failed write: binds %d, writes tried %d, said %v; want one of each and one line", l.binds, l.setTries, *said)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user