@@ -10,6 +10,7 @@ import (
10
10
"time"
11
11
12
12
"github.com/cockroachdb/cockroach/pkg/kv/kvpb"
13
+ "github.com/cockroachdb/cockroach/pkg/util/log"
13
14
"github.com/cockroachdb/cockroach/pkg/util/retry"
14
15
"github.com/cockroachdb/cockroach/pkg/util/stop"
15
16
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
@@ -56,8 +57,10 @@ type BufferedSender struct {
56
57
// queueMu protects the buffer queue.
57
58
queueMu struct {
58
59
syncutil.Mutex
59
- stopped bool
60
- buffer * eventQueue
60
+ stopped bool
61
+ buffer * eventQueue
62
+ capacity int64
63
+ overflow bool
61
64
}
62
65
63
66
// notifyDataC is used to notify the BufferedSender.run goroutine that there
@@ -72,14 +75,16 @@ type BufferedSender struct {
72
75
}
73
76
74
77
func NewBufferedSender (
75
- sender ServerStreamSender , bsMetrics * BufferedSenderMetrics ,
78
+ sender ServerStreamSender , bsMetrics * BufferedSenderMetrics , maxQueueSize int64 ,
76
79
) * BufferedSender {
77
80
bs := & BufferedSender {
78
81
sender : sender ,
79
82
metrics : bsMetrics ,
80
83
}
81
84
bs .queueMu .buffer = newEventQueue ()
82
85
bs .notifyDataC = make (chan struct {}, 1 )
86
+ bs .queueMu .buffer = newEventQueue ()
87
+ bs .queueMu .capacity = maxQueueSize
83
88
return bs
84
89
}
85
90
@@ -95,6 +100,15 @@ func (bs *BufferedSender) sendBuffered(
95
100
if bs .queueMu .stopped {
96
101
return errors .New ("stream sender is stopped" )
97
102
}
103
+ if bs .queueMu .overflow {
104
+ // Is this too spammy
105
+ log .Dev .Error (context .Background (), "buffer capacity exceeded" )
106
+ return newRetryErrBufferCapacityExceeded ()
107
+ }
108
+ if bs .queueMu .buffer .len () >= bs .queueMu .capacity {
109
+ bs .queueMu .overflow = true
110
+ return newRetryErrBufferCapacityExceeded ()
111
+ }
98
112
// TODO(wenyihu6): pass an actual context here
99
113
alloc .Use (context .Background ())
100
114
bs .queueMu .buffer .pushBack (sharedMuxEvent {ev , alloc })
@@ -130,7 +144,7 @@ func (bs *BufferedSender) run(
130
144
return nil
131
145
case <- bs .notifyDataC :
132
146
for {
133
- e , success := bs .popFront ()
147
+ e , success , overflowed , remains := bs .popFront ()
134
148
if ! success {
135
149
break
136
150
}
@@ -143,18 +157,26 @@ func (bs *BufferedSender) run(
143
157
if err != nil {
144
158
return err
145
159
}
160
+ if overflowed && remains == int64 (0 ) {
161
+ return newRetryErrBufferCapacityExceeded ()
162
+ }
146
163
}
147
164
}
148
165
}
149
166
}
150
167
151
168
// popFront pops the front event from the buffer queue. It returns the event and
152
169
// a boolean indicating if the event was successfully popped.
153
- func (bs * BufferedSender ) popFront () (e sharedMuxEvent , success bool ) {
170
+ func (bs * BufferedSender ) popFront () (
171
+ e sharedMuxEvent ,
172
+ success bool ,
173
+ overflowed bool ,
174
+ remains int64 ,
175
+ ) {
154
176
bs .queueMu .Lock ()
155
177
defer bs .queueMu .Unlock ()
156
178
event , ok := bs .queueMu .buffer .popFront ()
157
- return event , ok
179
+ return event , ok , bs . queueMu . overflow , bs . queueMu . buffer . len ()
158
180
}
159
181
160
182
// cleanup is called when the sender is stopped. It is expected to free up
0 commit comments