1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-06-27 00:21:15 +02:00

Fix BatchSpanProcessor.Shutdown to wait until all spans are processed (#766)

* Fix BatchSpanProcessor.Shutdown to wait until all spans are processed

Currently it exits too soon - before drainQueue is finished

* Check bsp.stopCh to reliably drop span when batcher is stopped

* Enable tests

* Always use WithBlocking

Co-authored-by: Joshua MacDonald <jmacd@users.noreply.github.com>
This commit is contained in:
Vladimir Mihailenco
2020-06-10 01:20:48 +03:00
committed by GitHub
parent c58680a772
commit 7ebd7b5ffa
2 changed files with 38 additions and 23 deletions

View File

@ -105,9 +105,10 @@ func NewBatchSpanProcessor(e export.SpanBatcher, opts ...BatchSpanProcessorOptio
queue: make(chan *export.SpanData, o.MaxQueueSize), queue: make(chan *export.SpanData, o.MaxQueueSize),
stopCh: make(chan struct{}), stopCh: make(chan struct{}),
} }
bsp.stopWait.Add(1)
bsp.stopWait.Add(1)
go func() { go func() {
defer bsp.stopWait.Done()
bsp.processQueue() bsp.processQueue()
bsp.drainQueue() bsp.drainQueue()
}() }()
@ -130,8 +131,6 @@ func (bsp *BatchSpanProcessor) Shutdown() {
bsp.stopOnce.Do(func() { bsp.stopOnce.Do(func() {
close(bsp.stopCh) close(bsp.stopCh)
bsp.stopWait.Wait() bsp.stopWait.Wait()
close(bsp.queue)
}) })
} }
@ -173,7 +172,6 @@ func (bsp *BatchSpanProcessor) exportSpans() {
// is shut down. It calls the exporter in batches of up to MaxExportBatchSize // is shut down. It calls the exporter in batches of up to MaxExportBatchSize
// waiting up to BatchTimeout to form a batch. // waiting up to BatchTimeout to form a batch.
func (bsp *BatchSpanProcessor) processQueue() { func (bsp *BatchSpanProcessor) processQueue() {
defer bsp.stopWait.Done()
defer bsp.timer.Stop() defer bsp.timer.Stop()
for { for {
@ -197,13 +195,22 @@ func (bsp *BatchSpanProcessor) processQueue() {
// drainQueue awaits the any caller that had added to bsp.stopWait // drainQueue awaits the any caller that had added to bsp.stopWait
// to finish the enqueue, then exports the final batch. // to finish the enqueue, then exports the final batch.
func (bsp *BatchSpanProcessor) drainQueue() { func (bsp *BatchSpanProcessor) drainQueue() {
for sd := range bsp.queue { for {
select {
case sd := <-bsp.queue:
if sd == nil {
bsp.exportSpans()
return
}
bsp.batch = append(bsp.batch, sd) bsp.batch = append(bsp.batch, sd)
if len(bsp.batch) == bsp.o.MaxExportBatchSize { if len(bsp.batch) == bsp.o.MaxExportBatchSize {
bsp.exportSpans() bsp.exportSpans()
} }
default:
close(bsp.queue)
}
} }
bsp.exportSpans()
} }
func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) { func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) {
@ -226,17 +233,19 @@ func (bsp *BatchSpanProcessor) enqueue(sd *export.SpanData) {
panic(x) panic(x)
}() }()
if bsp.o.BlockOnQueueFull {
select { select {
case bsp.queue <- sd:
case <-bsp.stopCh: case <-bsp.stopCh:
return
default:
} }
if bsp.o.BlockOnQueueFull {
bsp.queue <- sd
return return
} }
select { select {
case bsp.queue <- sd: case bsp.queue <- sd:
case <-bsp.stopCh:
default: default:
atomic.AddUint32(&bsp.dropped, 1) atomic.AddUint32(&bsp.dropped, 1)
} }

View File

@ -117,7 +117,6 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) {
sdktrace.WithBatchTimeout(schDelay), sdktrace.WithBatchTimeout(schDelay),
sdktrace.WithMaxQueueSize(200), sdktrace.WithMaxQueueSize(200),
sdktrace.WithMaxExportBatchSize(20), sdktrace.WithMaxExportBatchSize(20),
sdktrace.WithBlocking(),
}, },
wantNumSpans: 205, wantNumSpans: 205,
wantBatchCount: 11, wantBatchCount: 11,
@ -139,7 +138,6 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) {
o: []sdktrace.BatchSpanProcessorOption{ o: []sdktrace.BatchSpanProcessorOption{
sdktrace.WithBatchTimeout(schDelay), sdktrace.WithBatchTimeout(schDelay),
sdktrace.WithMaxExportBatchSize(200), sdktrace.WithMaxExportBatchSize(200),
sdktrace.WithBlocking(),
}, },
wantNumSpans: 2000, wantNumSpans: 2000,
wantBatchCount: 10, wantBatchCount: 10,
@ -162,18 +160,26 @@ func TestNewBatchSpanProcessorWithOptions(t *testing.T) {
tp.UnregisterSpanProcessor(ssp) tp.UnregisterSpanProcessor(ssp)
// TODO(https://github.com/open-telemetry/opentelemetry-go/issues/741) gotNumOfSpans := te.len()
// Restore some sort of test here. if option.wantNumSpans != gotNumOfSpans {
_ = option.wantNumSpans t.Errorf("number of exported span: got %+v, want %+v\n",
_ = option.wantBatchCount gotNumOfSpans, option.wantNumSpans)
_ = te.len() // gotNumOfSpans }
_ = te.getBatchCount() // gotBatchCount
gotBatchCount := te.getBatchCount()
if gotBatchCount < option.wantBatchCount {
t.Errorf("number batches: got %+v, want >= %+v\n",
gotBatchCount, option.wantBatchCount)
t.Errorf("Batches %v\n", te.sizes)
}
}) })
} }
} }
func createAndRegisterBatchSP(t *testing.T, option testOption, te *testBatchExporter) *sdktrace.BatchSpanProcessor { func createAndRegisterBatchSP(t *testing.T, option testOption, te *testBatchExporter) *sdktrace.BatchSpanProcessor {
ssp, err := sdktrace.NewBatchSpanProcessor(te, option.o...) // Always use blocking queue to avoid flaky tests.
options := append(option.o, sdktrace.WithBlocking())
ssp, err := sdktrace.NewBatchSpanProcessor(te, options...)
if ssp == nil { if ssp == nil {
t.Errorf("%s: Error creating new instance of BatchSpanProcessor, error: %v\n", option.name, err) t.Errorf("%s: Error creating new instance of BatchSpanProcessor, error: %v\n", option.name, err)
} }