@@ -14,6 +14,7 @@ import (
1414 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase"
1515 "github.com/cockroachdb/cockroach/pkg/settings"
1616 "github.com/cockroachdb/cockroach/pkg/settings/cluster"
17+ "github.com/cockroachdb/cockroach/pkg/util/buildutil"
1718 "github.com/cockroachdb/cockroach/pkg/util/retry"
1819 "github.com/cockroachdb/cockroach/pkg/util/stop"
1920 "github.com/cockroachdb/cockroach/pkg/util/syncutil"
@@ -147,47 +148,50 @@ func (bs *BufferedSender) sendBuffered(
147148 // registration. This error should be the next event that is sent to
148149 // stream.
149150 //
150- // NB: The zero-value of streamStatus is the valid state of a newly seen
151- // stream.
152- status := bs .queueMu .byStream [ev .StreamID ]
153- switch status .state {
154- case streamActive :
155- if bs .queueMu .perStreamCapacity > 0 && status .queueItems == bs .queueMu .perStreamCapacity {
151+ // NB: We don't error if the stream status is not found as this may be an
152+ // event for an already closed stream. Such events are possible while the
153+ // registration publishes the catch up scan buffer.
154+ status , ok := bs .queueMu .byStream [ev .StreamID ]
155+ if ok {
156+ switch status .state {
157+ case streamActive :
158+ if bs .queueMu .perStreamCapacity > 0 && status .queueItems == bs .queueMu .perStreamCapacity {
159+ if ev .Error != nil {
160+ // If _this_ event is an error, no use sending another error. This stream
161+ // is going down. Admit this error and mark the stream as overflowed.
162+ status .state = streamOverflowed
163+ } else {
164+ // This stream is at capacity, return an error to the registration that it
165+ // should send back to us after cleaning up.
166+ status .state = streamOverflowing
167+ return newRetryErrBufferCapacityExceeded ()
168+ }
169+ }
170+ case streamOverflowing :
171+ // The unbufferedRegistration is the only component that sends non-error
172+ // events to our stream. In response to the error we return when moving to
173+ // stateOverflowing, it should immediately send us an error and mark itself
174+ // as disconnected.
175+ //
176+ // The only unfortunate exception is if we get disconnected while flushing
177+ // the catch-up scan buffer.
156178 if ev .Error != nil {
157- // If _this_ event is an error, no use sending another error. This stream
158- // is going down. Admit this error and mark the stream as overflowed.
159179 status .state = streamOverflowed
160- } else {
161- // This stream is at capacity, return an error to the registration that it
162- // should send back to us after cleaning up.
163- status .state = streamOverflowing
164- return newRetryErrBufferCapacityExceeded ()
165180 }
181+ case streamOverflowed :
182+ // If we are overflowed, we don't expect any further events because the
183+ // registration should have disconnected in response to the error.
184+ //
185+ // TODO(ssd): Consider adding an assertion here.
186+ return nil
187+ default :
188+ panic (fmt .Sprintf ("unhandled stream state: %v" , status .state ))
166189 }
167- case streamOverflowing :
168- // The unbufferedRegistration is the only component that sends non-error
169- // events to our stream. In response to the error we return when moving to
170- // stateOverflowing, it should immediately send us an error and mark itself
171- // as disconnected.
172- //
173- // The only unfortunate exception is if we get disconnected while flushing
174- // the catch-up scan buffer.
175- if ev .Error != nil {
176- status .state = streamOverflowed
177- }
178- case streamOverflowed :
179- // If we are overflowed, we don't expect any further events because the
180- // registration should have disconnected in response to the error.
181- //
182- // TODO(ssd): Consider adding an assertion here.
183- return nil
184- default :
185- panic (fmt .Sprintf ("unhandled stream state: %v" , status .state ))
186- }
190+ // We are admitting this event.
191+ status .queueItems ++
192+ bs .queueMu .byStream [ev .StreamID ] = status
187193
188- // We are admitting this event.
189- status .queueItems ++
190- bs .queueMu .byStream [ev .StreamID ] = status
194+ }
191195
192196 // TODO(wenyihu6): pass an actual context here
193197 alloc .Use (context .Background ())
@@ -259,6 +263,18 @@ func (bs *BufferedSender) popFront() (e sharedMuxEvent, success bool) {
259263 return event , ok
260264}
261265
266+ func (bs * BufferedSender ) addStream (streamID int64 ) {
267+ bs .queueMu .Lock ()
268+ defer bs .queueMu .Unlock ()
269+ if _ , ok := bs .queueMu .byStream [streamID ]; ! ok {
270+ bs .queueMu .byStream [streamID ] = streamStatus {}
271+ } else {
272+ if buildutil .CrdbTestBuild {
273+ panic (fmt .Sprintf ("stream %d already exists in buffered sender" , streamID ))
274+ }
275+ }
276+ }
277+
262278func (bs * BufferedSender ) removeStream (streamID int64 ) {
263279 bs .queueMu .Lock ()
264280 defer bs .queueMu .Unlock ()
0 commit comments