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
xdsclient: fix LRS stream leaks when errors are encountered #5505
Changes from 1 commit
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 |
---|---|---|
|
@@ -99,10 +99,10 @@ func (t *Controller) run(ctx context.Context) { | |
// new requests to send on the stream. | ||
// | ||
// For each new request (watchAction), it's | ||
// - processed and added to the watch map | ||
// - so resend will pick them up when there are new streams | ||
// - sent on the current stream if there's one | ||
// - the current stream is cleared when any send on it fails | ||
// - processed and added to the watch map | ||
// so, resend will pick them up when there are new streams | ||
// - sent on the current stream if there's one | ||
// the current stream is cleared when any send on it fails | ||
// | ||
// For each new stream, all the existing requests will be resent. | ||
// | ||
|
@@ -388,26 +388,34 @@ func (t *Controller) reportLoad(ctx context.Context, cc *grpc.ClientConn, opts c | |
|
||
retries++ | ||
lastStreamStartTime = time.Now() | ||
stream, err := t.vClient.NewLoadStatsStream(ctx, cc) | ||
// streamCtx is created and canceled in case we terminate the stream | ||
// early for any reason, to avoid gRPC-Go leaking the RPC's monitoring | ||
// goroutine. | ||
streamCtx, cancel := context.WithCancel(ctx) | ||
stream, err := t.vClient.NewLoadStatsStream(streamCtx, cc) | ||
if err != nil { | ||
t.logger.Warningf("lrs: failed to create stream: %v", err) | ||
cancel() | ||
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. Should we do a 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 have mixed feelings about this sort of thing. Done; let me know what you think. |
||
continue | ||
} | ||
t.logger.Infof("lrs: created LRS stream") | ||
|
||
if err := t.vClient.SendFirstLoadStatsRequest(stream); err != nil { | ||
t.logger.Warningf("lrs: failed to send first request: %v", err) | ||
cancel() | ||
continue | ||
} | ||
|
||
clusters, interval, err := t.vClient.HandleLoadStatsResponse(stream) | ||
if err != nil { | ||
t.logger.Warningf("%v", err) | ||
t.logger.Warningf("lrs: error from stream: %v", err) | ||
cancel() | ||
continue | ||
} | ||
|
||
retries = 0 | ||
t.sendLoads(ctx, stream, opts.LoadStore, clusters, interval) | ||
t.sendLoads(streamCtx, stream, opts.LoadStore, clusters, interval) | ||
cancel() | ||
} | ||
} | ||
|
||
|
@@ -421,7 +429,7 @@ func (t *Controller) sendLoads(ctx context.Context, stream grpc.ClientStream, st | |
return | ||
} | ||
if err := t.vClient.SendLoadStatsRequest(stream, store.Stats(clusterNames)); err != nil { | ||
t.logger.Warningf("%v", err) | ||
t.logger.Warningf("lrs: error from stream: %v", err) | ||
return | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -59,7 +59,10 @@ func (v2c *client) SendFirstLoadStatsRequest(s grpc.ClientStream) error { | |||||||
|
||||||||
req := &lrspb.LoadStatsRequest{Node: node} | ||||||||
v2c.logger.Infof("lrs: sending init LoadStatsRequest: %v", pretty.ToJSON(req)) | ||||||||
return stream.Send(req) | ||||||||
if err := stream.Send(req); err != nil { | ||||||||
return getStreamError(stream) | ||||||||
} | ||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
func (v2c *client) HandleLoadStatsResponse(s grpc.ClientStream) ([]string, time.Duration, error) { | ||||||||
|
@@ -149,5 +152,17 @@ func (v2c *client) SendLoadStatsRequest(s grpc.ClientStream, loads []*load.Data) | |||||||
|
||||||||
req := &lrspb.LoadStatsRequest{ClusterStats: clusterStats} | ||||||||
v2c.logger.Infof("lrs: sending LRS loads: %+v", pretty.ToJSON(req)) | ||||||||
return stream.Send(req) | ||||||||
if err := stream.Send(req); err != nil { | ||||||||
return getStreamError(stream) | ||||||||
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. s/getStreamError/getStreamRecvError/? 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. What is trying to achieve? 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. The error returned from Lines 108 to 110 in 6417495
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. Is 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. My question is whether we should do this only when 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. Ah I see. Yes, that's a good idea: done. |
||||||||
} | ||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
func getStreamError(stream lrsStream) error { | ||||||||
for { | ||||||||
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. Should there be a way for this for loop to terminate other than when 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.
|
||||||||
_, err := stream.Recv() | ||||||||
if err != nil { | ||||||||
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. Combine these two lines. 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. Done. |
||||||||
return err | ||||||||
} | ||||||||
} | ||||||||
} |
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.
RPC's monitoring goroutine? Which goroutine are you taking about here?
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.
This one:
grpc-go/stream.go
Lines 354 to 361 in 6417495