Skip to content

Commit d9ab56a

Browse files
Ian Cottrellianthehat
authored andcommitted
internal/telemetry: change concurrency model
This changes to use a mutex and directly execute the less performance sensitive telemetry calls (tracing and logging) and then uses a submission queue only for stats adjustments as those are much more sensitive (but it should also be easier to keep up with them in bursts) Fixes golang/go#33692 Change-Id: Ia59a8975f21dfbfcf115be1f1d11b097be8dd9c8 Reviewed-on: https://go-review.googlesource.com/c/tools/+/190737 Run-TryBot: Ian Cottrell <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]> Reviewed-by: Emmanuel Odeke <[email protected]>
1 parent 15fda70 commit d9ab56a

File tree

7 files changed

+177
-180
lines changed

7 files changed

+177
-180
lines changed

internal/lsp/debug/serve.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -248,18 +248,15 @@ func Serve(ctx context.Context, addr string) error {
248248

249249
func Render(tmpl *template.Template, fun func(*http.Request) interface{}) func(http.ResponseWriter, *http.Request) {
250250
return func(w http.ResponseWriter, r *http.Request) {
251-
done := make(chan struct{})
252-
export.Do(func() {
253-
defer close(done)
254-
var data interface{}
255-
if fun != nil {
256-
data = fun(r)
257-
}
258-
if err := tmpl.Execute(w, data); err != nil {
259-
log.Error(context.Background(), "", err)
260-
}
261-
})
262-
<-done
251+
mu.Lock()
252+
defer mu.Unlock()
253+
var data interface{}
254+
if fun != nil {
255+
data = fun(r)
256+
}
257+
if err := tmpl.Execute(w, data); err != nil {
258+
log.Error(context.Background(), "", err)
259+
}
263260
}
264261
}
265262

internal/telemetry/export/export.go

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package export
1010
import (
1111
"context"
1212
"os"
13+
"sync"
1314
"time"
1415

1516
"golang.org/x/tools/internal/telemetry"
@@ -27,68 +28,65 @@ type Exporter interface {
2728
Metric(context.Context, telemetry.MetricData)
2829
}
2930

30-
var exporter = LogWriter(os.Stderr, true)
31-
32-
func SetExporter(setter func(Exporter) Exporter) {
33-
Do(func() {
34-
exporter = setter(exporter)
35-
})
36-
}
31+
var (
32+
exporterMu sync.Mutex
33+
exporter = LogWriter(os.Stderr, true)
34+
)
3735

3836
func AddExporters(e ...Exporter) {
39-
Do(func() {
40-
exporter = Multi(append([]Exporter{exporter}, e...)...)
41-
})
37+
exporterMu.Lock()
38+
defer exporterMu.Unlock()
39+
exporter = Multi(append([]Exporter{exporter}, e...)...)
4240
}
4341

4442
func StartSpan(ctx context.Context, span *telemetry.Span, at time.Time) {
45-
Do(func() {
46-
span.Start = at
47-
exporter.StartSpan(ctx, span)
48-
})
43+
exporterMu.Lock()
44+
defer exporterMu.Unlock()
45+
span.Start = at
46+
exporter.StartSpan(ctx, span)
4947
}
5048

5149
func FinishSpan(ctx context.Context, span *telemetry.Span, at time.Time) {
52-
Do(func() {
53-
span.Finish = at
54-
exporter.FinishSpan(ctx, span)
55-
})
50+
exporterMu.Lock()
51+
defer exporterMu.Unlock()
52+
span.Finish = at
53+
exporter.FinishSpan(ctx, span)
5654
}
5755

5856
func Tag(ctx context.Context, at time.Time, tags telemetry.TagList) {
59-
Do(func() {
60-
// If context has a span we need to add the tags to it
61-
span := telemetry.GetSpan(ctx)
62-
if span == nil {
63-
return
64-
}
65-
if span.Start.IsZero() {
66-
// span still being created, tag it directly
67-
span.Tags = append(span.Tags, tags...)
68-
return
69-
}
70-
// span in progress, add an event to the span
71-
span.Events = append(span.Events, telemetry.Event{
72-
At: at,
73-
Tags: tags,
74-
})
57+
exporterMu.Lock()
58+
defer exporterMu.Unlock()
59+
// If context has a span we need to add the tags to it
60+
span := telemetry.GetSpan(ctx)
61+
if span == nil {
62+
return
63+
}
64+
if span.Start.IsZero() {
65+
// span still being created, tag it directly
66+
span.Tags = append(span.Tags, tags...)
67+
return
68+
}
69+
// span in progress, add an event to the span
70+
span.Events = append(span.Events, telemetry.Event{
71+
At: at,
72+
Tags: tags,
7573
})
7674
}
7775

7876
func Log(ctx context.Context, event telemetry.Event) {
79-
Do(func() {
80-
// If context has a span we need to add the event to it
81-
span := telemetry.GetSpan(ctx)
82-
if span != nil {
83-
span.Events = append(span.Events, event)
84-
}
85-
// and now also hand the event of to the current observer
86-
exporter.Log(ctx, event)
87-
})
77+
exporterMu.Lock()
78+
defer exporterMu.Unlock()
79+
// If context has a span we need to add the event to it
80+
span := telemetry.GetSpan(ctx)
81+
if span != nil {
82+
span.Events = append(span.Events, event)
83+
}
84+
// and now also hand the event of to the current observer
85+
exporter.Log(ctx, event)
8886
}
8987

9088
func Metric(ctx context.Context, data telemetry.MetricData) {
91-
Do(func() {
92-
exporter.Metric(ctx, data)
93-
})
89+
exporterMu.Lock()
90+
defer exporterMu.Unlock()
91+
exporter.Metric(ctx, data)
9492
}

internal/telemetry/export/ocagent/ocagent.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"fmt"
1515
"net/http"
1616
"os"
17+
"sync"
1718
"time"
1819

1920
"golang.org/x/tools/internal/telemetry"
@@ -26,6 +27,7 @@ const DefaultAddress = "http://localhost:55678"
2627
const exportRate = 2 * time.Second
2728

2829
type exporter struct {
30+
mu sync.Mutex
2931
address string
3032
node *wire.Node
3133
spans []*wire.Span
@@ -63,9 +65,7 @@ func Connect(service, address string) export.Exporter {
6365
}
6466
go func() {
6567
for _ = range time.Tick(exportRate) {
66-
export.Do(func() {
67-
exporter.flush()
68-
})
68+
exporter.flush()
6969
}
7070
}()
7171
return exporter
@@ -74,16 +74,22 @@ func Connect(service, address string) export.Exporter {
7474
func (e *exporter) StartSpan(ctx context.Context, span *telemetry.Span) {}
7575

7676
func (e *exporter) FinishSpan(ctx context.Context, span *telemetry.Span) {
77+
e.mu.Lock()
78+
defer e.mu.Unlock()
7779
e.spans = append(e.spans, convertSpan(span))
7880
}
7981

8082
func (e *exporter) Log(context.Context, telemetry.Event) {}
8183

8284
func (e *exporter) Metric(ctx context.Context, data telemetry.MetricData) {
85+
e.mu.Lock()
86+
defer e.mu.Unlock()
8387
e.metrics = append(e.metrics, convertMetric(data))
8488
}
8589

8690
func (e *exporter) flush() {
91+
e.mu.Lock()
92+
defer e.mu.Unlock()
8793
spans := e.spans
8894
e.spans = nil
8995
metrics := e.metrics

internal/telemetry/export/prometheus/prometheus.go

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import (
1010
"fmt"
1111
"net/http"
1212
"sort"
13+
"sync"
1314

1415
"golang.org/x/tools/internal/telemetry"
15-
"golang.org/x/tools/internal/telemetry/export"
1616
"golang.org/x/tools/internal/telemetry/metric"
1717
)
1818

@@ -21,13 +21,16 @@ func New() *Exporter {
2121
}
2222

2323
type Exporter struct {
24+
mu sync.Mutex
2425
metrics []telemetry.MetricData
2526
}
2627

2728
func (e *Exporter) StartSpan(ctx context.Context, span *telemetry.Span) {}
2829
func (e *Exporter) FinishSpan(ctx context.Context, span *telemetry.Span) {}
2930
func (e *Exporter) Log(ctx context.Context, event telemetry.Event) {}
3031
func (e *Exporter) Metric(ctx context.Context, data telemetry.MetricData) {
32+
e.mu.Lock()
33+
defer e.mu.Unlock()
3134
name := data.Handle()
3235
// We keep the metrics in name sorted order so the page is stable and easy
3336
// to read. We do this with an insertion sort rather than sorting the list
@@ -76,48 +79,45 @@ func (e *Exporter) row(w http.ResponseWriter, name string, group telemetry.TagLi
7679
}
7780

7881
func (e *Exporter) Serve(w http.ResponseWriter, r *http.Request) {
79-
done := make(chan struct{})
80-
export.Do(func() {
81-
defer close(done)
82-
for _, data := range e.metrics {
83-
switch data := data.(type) {
84-
case *metric.Int64Data:
85-
e.header(w, data.Info.Name, data.Info.Description, data.IsGauge, false)
86-
for i, group := range data.Groups() {
87-
e.row(w, data.Info.Name, group, "", data.Rows[i])
88-
}
82+
e.mu.Lock()
83+
defer e.mu.Unlock()
84+
for _, data := range e.metrics {
85+
switch data := data.(type) {
86+
case *metric.Int64Data:
87+
e.header(w, data.Info.Name, data.Info.Description, data.IsGauge, false)
88+
for i, group := range data.Groups() {
89+
e.row(w, data.Info.Name, group, "", data.Rows[i])
90+
}
8991

90-
case *metric.Float64Data:
91-
e.header(w, data.Info.Name, data.Info.Description, data.IsGauge, false)
92-
for i, group := range data.Groups() {
93-
e.row(w, data.Info.Name, group, "", data.Rows[i])
94-
}
92+
case *metric.Float64Data:
93+
e.header(w, data.Info.Name, data.Info.Description, data.IsGauge, false)
94+
for i, group := range data.Groups() {
95+
e.row(w, data.Info.Name, group, "", data.Rows[i])
96+
}
9597

96-
case *metric.HistogramInt64Data:
97-
e.header(w, data.Info.Name, data.Info.Description, false, true)
98-
for i, group := range data.Groups() {
99-
row := data.Rows[i]
100-
for j, b := range data.Info.Buckets {
101-
e.row(w, data.Info.Name+"_bucket", group, fmt.Sprintf(`le="%v"`, b), row.Values[j])
102-
}
103-
e.row(w, data.Info.Name+"_bucket", group, `le="+Inf"`, row.Count)
104-
e.row(w, data.Info.Name+"_count", group, "", row.Count)
105-
e.row(w, data.Info.Name+"_sum", group, "", row.Sum)
98+
case *metric.HistogramInt64Data:
99+
e.header(w, data.Info.Name, data.Info.Description, false, true)
100+
for i, group := range data.Groups() {
101+
row := data.Rows[i]
102+
for j, b := range data.Info.Buckets {
103+
e.row(w, data.Info.Name+"_bucket", group, fmt.Sprintf(`le="%v"`, b), row.Values[j])
106104
}
105+
e.row(w, data.Info.Name+"_bucket", group, `le="+Inf"`, row.Count)
106+
e.row(w, data.Info.Name+"_count", group, "", row.Count)
107+
e.row(w, data.Info.Name+"_sum", group, "", row.Sum)
108+
}
107109

108-
case *metric.HistogramFloat64Data:
109-
e.header(w, data.Info.Name, data.Info.Description, false, true)
110-
for i, group := range data.Groups() {
111-
row := data.Rows[i]
112-
for j, b := range data.Info.Buckets {
113-
e.row(w, data.Info.Name+"_bucket", group, fmt.Sprintf(`le="%v"`, b), row.Values[j])
114-
}
115-
e.row(w, data.Info.Name+"_bucket", group, `le="+Inf"`, row.Count)
116-
e.row(w, data.Info.Name+"_count", group, "", row.Count)
117-
e.row(w, data.Info.Name+"_sum", group, "", row.Sum)
110+
case *metric.HistogramFloat64Data:
111+
e.header(w, data.Info.Name, data.Info.Description, false, true)
112+
for i, group := range data.Groups() {
113+
row := data.Rows[i]
114+
for j, b := range data.Info.Buckets {
115+
e.row(w, data.Info.Name+"_bucket", group, fmt.Sprintf(`le="%v"`, b), row.Values[j])
118116
}
117+
e.row(w, data.Info.Name+"_bucket", group, `le="+Inf"`, row.Count)
118+
e.row(w, data.Info.Name+"_count", group, "", row.Count)
119+
e.row(w, data.Info.Name+"_sum", group, "", row.Sum)
119120
}
120121
}
121-
})
122-
<-done
122+
}
123123
}

0 commit comments

Comments
 (0)