@@ -17,6 +17,7 @@ package trace
1717import (
1818 "context"
1919 "errors"
20+ "runtime"
2021 "sync"
2122 "sync/atomic"
2223 "time"
@@ -25,9 +26,9 @@ import (
2526)
2627
2728const (
28- defaultMaxQueueSize = 2048
29- defaultScheduledDelay = 5000 * time .Millisecond
30- defaultMaxExportBatchSize = 512
29+ DefaultMaxQueueSize = 2048
30+ DefaultScheduledDelay = 5000 * time .Millisecond
31+ DefaultMaxExportBatchSize = 512
3132)
3233
3334var (
@@ -70,6 +71,8 @@ type BatchSpanProcessor struct {
7071 queue chan * export.SpanData
7172 dropped uint32
7273
74+ batch []* export.SpanData
75+ timer * time.Timer
7376 stopWait sync.WaitGroup
7477 stopOnce sync.Once
7578 stopCh chan struct {}
@@ -87,26 +90,26 @@ func NewBatchSpanProcessor(e export.SpanBatcher, opts ...BatchSpanProcessorOptio
8790 }
8891
8992 o := BatchSpanProcessorOptions {
90- ScheduledDelayMillis : defaultScheduledDelay ,
91- MaxQueueSize : defaultMaxQueueSize ,
92- MaxExportBatchSize : defaultMaxExportBatchSize ,
93+ ScheduledDelayMillis : DefaultScheduledDelay ,
94+ MaxQueueSize : DefaultMaxQueueSize ,
95+ MaxExportBatchSize : DefaultMaxExportBatchSize ,
9396 }
9497 for _ , opt := range opts {
9598 opt (& o )
9699 }
97100 bsp := & BatchSpanProcessor {
98- e : e ,
99- o : o ,
101+ e : e ,
102+ o : o ,
103+ batch : make ([]* export.SpanData , 0 , o .MaxExportBatchSize ),
104+ timer : time .NewTimer (o .ScheduledDelayMillis ),
105+ queue : make (chan * export.SpanData , o .MaxQueueSize ),
106+ stopCh : make (chan struct {}),
100107 }
101-
102- bsp .queue = make (chan * export.SpanData , bsp .o .MaxQueueSize )
103-
104- bsp .stopCh = make (chan struct {})
105-
106108 bsp .stopWait .Add (1 )
109+
107110 go func () {
108- defer bsp .stopWait .Done ()
109111 bsp .processQueue ()
112+ bsp .drainQueue ()
110113 }()
111114
112115 return bsp , nil
@@ -127,6 +130,8 @@ func (bsp *BatchSpanProcessor) Shutdown() {
127130 bsp .stopOnce .Do (func () {
128131 close (bsp .stopCh )
129132 bsp .stopWait .Wait ()
133+ close (bsp .queue )
134+
130135 })
131136}
132137
@@ -154,90 +159,85 @@ func WithBlocking() BatchSpanProcessorOption {
154159 }
155160}
156161
162+ // exportSpans is a subroutine of processing and draining the queue.
163+ func (bsp * BatchSpanProcessor ) exportSpans () {
164+ bsp .timer .Reset (bsp .o .ScheduledDelayMillis )
165+
166+ if len (bsp .batch ) > 0 {
167+ bsp .e .ExportSpans (context .Background (), bsp .batch )
168+ bsp .batch = bsp .batch [:0 ]
169+ }
170+ }
171+
157172// processQueue removes spans from the `queue` channel until processor
158173// is shut down. It calls the exporter in batches of up to MaxExportBatchSize
159174// waiting up to ScheduledDelayMillis to form a batch.
160175func (bsp * BatchSpanProcessor ) processQueue () {
161- timer := time . NewTimer ( bsp .o . ScheduledDelayMillis )
162- defer timer .Stop ()
176+ defer bsp .stopWait . Done ( )
177+ defer bsp . timer .Stop ()
163178
164- batch := make ([]* export.SpanData , 0 , bsp .o .MaxExportBatchSize )
165-
166- exportSpans := func () {
167- timer .Reset (bsp .o .ScheduledDelayMillis )
168-
169- if len (batch ) > 0 {
170- bsp .e .ExportSpans (context .Background (), batch )
171- batch = batch [:0 ]
172- }
173- }
174-
175- loop:
176179 for {
177180 select {
178181 case <- bsp .stopCh :
179- break loop
180- case <- timer .C :
181- exportSpans ()
182+ return
183+ case <- bsp . timer .C :
184+ bsp . exportSpans ()
182185 case sd := <- bsp .queue :
183- batch = append (batch , sd )
184- if len (batch ) == bsp .o .MaxExportBatchSize {
185- if ! timer .Stop () {
186- <- timer .C
186+ bsp . batch = append (bsp . batch , sd )
187+ if len (bsp . batch ) == bsp .o .MaxExportBatchSize {
188+ if ! bsp . timer .Stop () {
189+ <- bsp . timer .C
187190 }
188- exportSpans ()
191+ bsp . exportSpans ()
189192 }
190193 }
191194 }
192-
193- for {
194- select {
195- case sd := <- bsp .queue :
196- if sd == nil { // queue is closed
197- go throwAwayFutureSends (bsp .queue )
198- exportSpans ()
199- return
200- }
201-
202- batch = append (batch , sd )
203- if len (batch ) == bsp .o .MaxExportBatchSize {
204- exportSpans ()
205- }
206- default :
207- // Send nil instead of closing to prevent "send on closed channel".
208- bsp .queue <- nil
209- }
210- }
211195}
212196
213- func throwAwayFutureSends (ch <- chan * export.SpanData ) {
214- for {
215- select {
216- case <- ch :
217- case <- time .After (time .Minute ):
218- return
197+ // drainQueue awaits the any caller that had added to bsp.stopWait
198+ // to finish the enqueue, then exports the final batch.
199+ func (bsp * BatchSpanProcessor ) drainQueue () {
200+ for sd := range bsp .queue {
201+ bsp .batch = append (bsp .batch , sd )
202+ if len (bsp .batch ) == bsp .o .MaxExportBatchSize {
203+ bsp .exportSpans ()
219204 }
220205 }
206+ bsp .exportSpans ()
221207}
222208
223209func (bsp * BatchSpanProcessor ) enqueue (sd * export.SpanData ) {
224210 if ! sd .SpanContext .IsSampled () {
225211 return
226212 }
227213
228- select {
229- case <- bsp .stopCh :
230- return
231- default :
232- }
214+ // This ensures the bsp.queue<- below does not panic as the
215+ // processor shuts down.
216+ defer func () {
217+ x := recover ()
218+ switch err := x .(type ) {
219+ case nil :
220+ return
221+ case runtime.Error :
222+ if err .Error () == "send on closed channel" {
223+ return
224+ }
225+ }
226+ panic (x )
227+ }()
233228
234229 if bsp .o .BlockOnQueueFull {
235- bsp .queue <- sd
236- } else {
237230 select {
238231 case bsp .queue <- sd :
239- default :
240- atomic .AddUint32 (& bsp .dropped , 1 )
232+ case <- bsp .stopCh :
241233 }
234+ return
235+ }
236+
237+ select {
238+ case bsp .queue <- sd :
239+ case <- bsp .stopCh :
240+ default :
241+ atomic .AddUint32 (& bsp .dropped , 1 )
242242 }
243243}
0 commit comments