Skip to content

Commit 79a0e11

Browse files
itizirhongalex
andauthored
fix(pubsub): close grpc streams on retry (#10624)
follow-up as 66581c4 is not enough to plug all stream leaks Co-authored-by: Alex Hong <9397363+hongalex@users.noreply.github.com>
1 parent 02b2d12 commit 79a0e11

File tree

1 file changed

+18
-11
lines changed

1 file changed

+18
-11
lines changed

pubsub/pullstream.go

+18-11
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ import (
3131
// the stream on a retryable error.
3232
type pullStream struct {
3333
ctx context.Context
34-
open func() (pb.Subscriber_StreamingPullClient, error)
35-
cancel context.CancelFunc
34+
cancel context.CancelFunc // cancel function of the context above
35+
open func() (pb.Subscriber_StreamingPullClient, context.CancelFunc, error)
36+
close context.CancelFunc // cancel function to close down the currently open stream
3637

3738
mu sync.Mutex
3839
spc *pb.Subscriber_StreamingPullClient
@@ -50,8 +51,9 @@ func newPullStream(ctx context.Context, streamingPull streamingPullFunc, subName
5051
return &pullStream{
5152
ctx: ctx,
5253
cancel: cancel,
53-
open: func() (pb.Subscriber_StreamingPullClient, error) {
54-
spc, err := streamingPull(ctx, gax.WithGRPCOptions(grpc.MaxCallRecvMsgSize(maxSendRecvBytes)))
54+
open: func() (pb.Subscriber_StreamingPullClient, context.CancelFunc, error) {
55+
sctx, close := context.WithCancel(ctx)
56+
spc, err := streamingPull(sctx, gax.WithGRPCOptions(grpc.MaxCallRecvMsgSize(maxSendRecvBytes)))
5557
if err == nil {
5658
recordStat(ctx, StreamRequestCount, 1)
5759
streamAckDeadline := int32(maxDurationPerLeaseExtension / time.Second)
@@ -69,9 +71,10 @@ func newPullStream(ctx context.Context, streamingPull streamingPullFunc, subName
6971
})
7072
}
7173
if err != nil {
72-
return nil, err
74+
close()
75+
return nil, nil, err
7376
}
74-
return spc, nil
77+
return spc, close, nil
7578
},
7679
}
7780
}
@@ -100,29 +103,33 @@ func (s *pullStream) get(spc *pb.Subscriber_StreamingPullClient) (*pb.Subscriber
100103
if spc != s.spc {
101104
return s.spc, nil
102105
}
106+
// we are about to open a new stream: if necessary, make sure the previous one is closed
107+
if s.close != nil {
108+
s.close()
109+
}
103110
// Either this is the very first call on this stream (s.spc == nil), or we have a valid
104111
// retry request. Either way, open a new stream.
105112
// The lock is held here for a long time, but it doesn't matter because no callers could get
106113
// anything done anyway.
107114
s.spc = new(pb.Subscriber_StreamingPullClient)
108-
*s.spc, s.err = s.openWithRetry() // Any error from openWithRetry is permanent.
115+
*s.spc, s.close, s.err = s.openWithRetry() // Any error from openWithRetry is permanent.
109116
return s.spc, s.err
110117
}
111118

112-
func (s *pullStream) openWithRetry() (pb.Subscriber_StreamingPullClient, error) {
119+
func (s *pullStream) openWithRetry() (pb.Subscriber_StreamingPullClient, context.CancelFunc, error) {
113120
r := defaultRetryer{}
114121
for {
115122
recordStat(s.ctx, StreamOpenCount, 1)
116-
spc, err := s.open()
123+
spc, close, err := s.open()
117124
bo, shouldRetry := r.Retry(err)
118125
if err != nil && shouldRetry {
119126
recordStat(s.ctx, StreamRetryCount, 1)
120127
if err := gax.Sleep(s.ctx, bo); err != nil {
121-
return nil, err
128+
return nil, nil, err
122129
}
123130
continue
124131
}
125-
return spc, err
132+
return spc, close, err
126133
}
127134
}
128135

0 commit comments

Comments
 (0)