-
Notifications
You must be signed in to change notification settings - Fork 9.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
clientv3: process closed watcherStreams in watcherGrpcStream run loop #6487
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -759,3 +759,63 @@ func TestWatchCancelOnServer(t *testing.T) { | |
t.Fatalf("expected 0 watchers, got %q", watchers) | ||
} | ||
} | ||
|
||
// TestWatchOverlapContextCancel stresses the watcher stream teardown path by | ||
// creating/canceling watchers to ensure that new watchers are not taken down | ||
// by a torn down watch stream. The sort of race that's being detected: | ||
// 1. create w1 using a cancelable ctx with %v as "ctx" | ||
// 2. cancel ctx | ||
// 3. watcher client begins tearing down watcher grpc stream since no more watchers | ||
// 3. start creating watcher w2 using a new "ctx" (not canceled), attaches to old grpc stream | ||
// 4. watcher client finishes tearing down stream on "ctx" | ||
// 5. w2 comes back canceled | ||
func TestWatchOverlapContextCancel(t *testing.T) { | ||
defer testutil.AfterTest(t) | ||
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) | ||
defer clus.Terminate(t) | ||
|
||
cli := clus.RandClient() | ||
if _, err := cli.Put(context.TODO(), "abc", "def"); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
// each unique context "%v" has a unique grpc stream | ||
n := 100 | ||
ctxs, ctxc := make([]context.Context, 5), make([]chan struct{}, 5) | ||
for i := range ctxs { | ||
// make "%v" unique | ||
ctxs[i] = context.WithValue(context.TODO(), "key", i) | ||
// limits the maximum number of outstanding watchers per stream | ||
ctxc[i] = make(chan struct{}, 2) | ||
} | ||
ch := make(chan struct{}, n) | ||
// issue concurrent watches with cancel | ||
for i := 0; i < n; i++ { | ||
go func() { | ||
defer func() { ch <- struct{}{} }() | ||
idx := rand.Intn(len(ctxs)) | ||
ctx, cancel := context.WithCancel(ctxs[idx]) | ||
ctxc[idx] <- struct{}{} | ||
ch := cli.Watch(ctx, "abc", clientv3.WithRev(1)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change "ch" to "wch". Differentiate from existing var. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
if _, ok := <-ch; !ok { | ||
t.Fatalf("unexpected closed channel") | ||
} | ||
// randomize how cancel overlaps with watch creation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this could happen without overlapping cancel calls? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could happen, but this synchronization is important to stress the teardown path. Otherwise, watchers will pile up on the streams instead of oscillating between 0, 1, and 2 watchers. I'll add a comment. |
||
if rand.Intn(2) == 0 { | ||
<-ctxc[idx] | ||
cancel() | ||
} else { | ||
cancel() | ||
<-ctxc[idx] | ||
} | ||
}() | ||
} | ||
// join on watches | ||
for i := 0; i < n; i++ { | ||
select { | ||
case <-ch: | ||
case <-time.After(time.Second): | ||
t.Fatalf("timed out waiting for completed watch") | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make key "abc" a const in this test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when did we start writing tests like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the bad thing about this "abc" key is that the put req is not close to the watch req. reader might lose context. changing it to putkey or something is easier for the reader.