Fix race between activeStreams and bdp window size#5494
Conversation
| if !checkForHeaderListSize(it) { | ||
| return false | ||
| } | ||
| if !checkForHeaderListSize(it) { | ||
| if !checkForStreamQuota(it) { | ||
| return false | ||
| } | ||
| return true |
There was a problem hiding this comment.
This can all be simply return checkForHeaderListSize(it) && checkForStreamQuota(it)
| if e, ok := status.FromError(err); ok { | ||
| if e.Code() != codes.ResourceExhausted { |
There was a problem hiding this comment.
This is simpler:
if code := status.Code(err); code != codes.ResourceExhausted {
...
}Note also that your test fails to detect if the returned error from UnaryCall is a non-status error (this isn't ever expected, of course, but is technically possible).
There was a problem hiding this comment.
Switched and wrote a comment about your note.
There was a problem hiding this comment.
wrote a comment about your note.
I don't understand. If you switched to my method instead of yours, my note doesn't apply anymore.
easwars
left a comment
There was a problem hiding this comment.
We also probably need a better release note. Else, we will get back to this when we make the release.
| _, err := ss.Client.UnaryCall(ctx, &testpb.SimpleRequest{ResponseSize: 4194304}) | ||
| if err != nil { |
There was a problem hiding this comment.
Nit: combine the assignment and the conditional statements.
| _, err = ss.Client.UnaryCall(ctx, &testpb.SimpleRequest{ResponseSize: 275075}) | ||
| if err != nil { |
There was a problem hiding this comment.
Nit: combine the assignment and the conditional statements.
| // this is testing for is an unexpected EOF with a status code | ||
| // INTERNAL so this is fine. | ||
| if code := status.Code(err); code != codes.ResourceExhausted { | ||
| t.Fatalf("unexpected err in UnaryCall: %v", err) |
There was a problem hiding this comment.
Could you mention the received and expected codes in the error string.
got, want := status.Code(err), codes.ResourceExhausted; got != want {
t.Fatalf("UnaryCall RPC returned status code: %v, want %v", got, want)
}There was a problem hiding this comment.
Printing the whole error is better than only printing the code, as there might be status message info that's useful:
t.Fatalf("UnaryCall RPC returned error: %v, want status code %v", err, want)There was a problem hiding this comment.
Switched to this:
if code := status.Code(err); code != codes.ResourceExhausted { t.Fatalf("UnaryCall RPC returned error: %v, want status code %v", err, codes.ResourceExhausted) }
| // should work normally. | ||
| _, err = ss.Client.UnaryCall(ctx, &testpb.SimpleRequest{ResponseSize: 275075}) | ||
| if err != nil { | ||
| t.Fatalf("unexpected err in UnaryCall: %v", err) |
There was a problem hiding this comment.
Prefer concise error messages:
t.Fatalf("UnaryCall RPC failed: %v", err)| s.id = h.streamID | ||
| s.fc = &inFlow{limit: uint32(t.initialWindowSize)} | ||
| t.mu.Lock() | ||
| if t.activeStreams != nil { // Can be niled from Close() |
There was a problem hiding this comment.
if t.activeStreams == nil {
return false
}
t.activeStreams[s.id] = s
zasweq
left a comment
There was a problem hiding this comment.
Thanks for the comments :D! I'm sure some tests will fail from this stream change though give it a second to look at again.
| // this is testing for is an unexpected EOF with a status code | ||
| // INTERNAL so this is fine. | ||
| if code := status.Code(err); code != codes.ResourceExhausted { | ||
| t.Fatalf("unexpected err in UnaryCall: %v", err) |
There was a problem hiding this comment.
Switched to this:
if code := status.Code(err); code != codes.ResourceExhausted { t.Fatalf("UnaryCall RPC returned error: %v, want status code %v", err, codes.ResourceExhausted) }
| s.id = h.streamID | ||
| s.fc = &inFlow{limit: uint32(t.initialWindowSize)} | ||
| t.mu.Lock() | ||
| if t.activeStreams != nil { // Can be niled from Close() |
| t.setGoAwayReason(f) | ||
| close(t.goAway) | ||
| t.controlBuf.put(&incomingGoAway{}) | ||
| defer t.controlBuf.put(&incomingGoAway{}) |
There was a problem hiding this comment.
Please add a comment explaining that we defer because t.mu is held
There was a problem hiding this comment.
Added "Defer as t.mu is currently held".
| // This check doesn't fail on a non status error. However, the main | ||
| // this is testing for is an unexpected EOF with a status code | ||
| // INTERNAL so this is fine. |
There was a problem hiding this comment.
Delete now. It does fail on a non-status error since code would be Unknown in that case.
There was a problem hiding this comment.
Whoops forgot deleted.
| for i := 0; i < 10; i++ { | ||
| // exceeds grpc.DefaultMaxRecvMessageSize, this should error with | ||
| // RESOURCE_EXHAUSTED error. | ||
| if _, err := ss.Client.UnaryCall(ctx, &testpb.SimpleRequest{ResponseSize: 4194304}); err != nil { |
There was a problem hiding this comment.
_, err := ss.Client.UnaryCall()
if got := status.Code(err); got != codes.ResourceExhausted {
t.Fatal
}This way you catch the case where the call doesn't fail (code will be OK), unexpectedly.
There was a problem hiding this comment.
Ah good point switched.
zasweq
left a comment
There was a problem hiding this comment.
Thanks for the comments :D!
| // This check doesn't fail on a non status error. However, the main | ||
| // this is testing for is an unexpected EOF with a status code | ||
| // INTERNAL so this is fine. |
There was a problem hiding this comment.
Whoops forgot deleted.
| for i := 0; i < 10; i++ { | ||
| // exceeds grpc.DefaultMaxRecvMessageSize, this should error with | ||
| // RESOURCE_EXHAUSTED error. | ||
| if _, err := ss.Client.UnaryCall(ctx, &testpb.SimpleRequest{ResponseSize: 4194304}); err != nil { |
There was a problem hiding this comment.
Ah good point switched.
| t.setGoAwayReason(f) | ||
| close(t.goAway) | ||
| t.controlBuf.put(&incomingGoAway{}) | ||
| defer t.controlBuf.put(&incomingGoAway{}) |
There was a problem hiding this comment.
Added "Defer as t.mu is currently held".
Fixes #5358.
The test case I added based off the issue test would successfully fail unexpected.EOF. This was caused by a race between when a stream was created and when updateFlowControl() was called. This fix makes sure any created streams has the correct window size on creation.
RELEASE NOTES: