Skip to content

Commit 295baf0

Browse files
committed
perf(promproxy): implement query recording
* Add query recording schema * Add promproxy to ch-bench-read
1 parent f608d3e commit 295baf0

File tree

14 files changed

+1499
-19
lines changed

14 files changed

+1499
-19
lines changed

.dockerignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
/.github
33
/docs
44
*.md
5-
*.Dockerfile
5+
*.Dockerfile
6+
7+
# remore write queries
8+
*.rwq

cmd/otelproxy/main.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,17 @@ type service struct {
3030
name string
3131
handler http.Handler
3232
findRoute httpmiddleware.RouteFinder
33+
cleanup func() error
3334
}
3435

3536
func (s service) Run(ctx context.Context, lg *zap.Logger, m *app.Metrics) error {
37+
if s.cleanup != nil {
38+
defer func() {
39+
if err := s.cleanup(); err != nil {
40+
lg.Error("Cleanup failed", zap.Error(err))
41+
}
42+
}()
43+
}
3644
httpServer := &http.Server{
3745
Addr: s.addr,
3846
Handler: ServiceMiddleware(s, lg, m),
@@ -108,9 +116,25 @@ func (s *services) Prometheus(m *app.Metrics) error {
108116
if err != nil {
109117
return errors.Wrap(err, "create client")
110118
}
111-
119+
var (
120+
rec *promproxy.Recorder
121+
cleanup func() error
122+
)
123+
if fName := os.Getenv(prefix + "_RECORD"); fName != "" {
124+
f, err := os.Create(fName) // #nosec G304
125+
if err != nil {
126+
return errors.Wrap(err, "create record file")
127+
}
128+
rec = promproxy.NewRecorder(f)
129+
cleanup = func() error {
130+
if err := f.Close(); err != nil {
131+
return errors.Wrap(err, "close record file")
132+
}
133+
return nil
134+
}
135+
}
112136
server, err := promapi.NewServer(
113-
promproxy.NewServer(client),
137+
promproxy.NewServer(client, rec),
114138
promapi.WithTracerProvider(m.TracerProvider()),
115139
promapi.WithMeterProvider(m.MeterProvider()),
116140
)
@@ -127,6 +151,7 @@ func (s *services) Prometheus(m *app.Metrics) error {
127151
name: strings.ToLower(prefix),
128152
handler: server,
129153
findRoute: httpmiddleware.MakeRouteFinder[promapi.Route](server),
154+
cleanup: cleanup,
130155
})
131156
}
132157

dev/local/ch-bench-read/.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
record

dev/local/ch-bench-read/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
record

dev/local/ch-bench-read/docker-compose.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ services:
2424
environment:
2525
- OTELDB_STORAGE=ch
2626
- CH_DSN=clickhouse://clickhouse:9000
27-
- OTEL_LOG_LEVEL=debug
27+
- OTEL_LOG_LEVEL=info
2828
- OTEL_METRICS_EXPORTER=none
2929
- OTEL_LOGS_EXPORTER=none
3030
- OTEL_TRACES_EXPORTER=none
@@ -40,6 +40,21 @@ services:
4040
depends_on:
4141
- clickhouse
4242

43+
otelproxy:
44+
build:
45+
context: ../../../
46+
dockerfile: otelproxy.Dockerfile
47+
volumes:
48+
- ./record:/record
49+
environment:
50+
- PROMETHEUS_URL=http://oteldb:9090
51+
- PROMETHEUS_RECORD=/record/queries.jsonl
52+
- OTEL_LOG_LEVEL=info
53+
- OTEL_METRICS_EXPORTER=none
54+
- OTEL_LOGS_EXPORTER=none
55+
- OTEL_TRACES_EXPORTER=none
56+
- OTEL_RESOURCE_ATTRIBUTES=service.name=go-faster.otelproxy
57+
4358
grafana:
4459
image: "grafana/grafana:10.0.0"
4560
environment:

dev/local/ch-bench-read/grafana/dashboards/node-exporter.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23636,7 +23636,7 @@
2363623636
"type": "row"
2363723637
}
2363823638
],
23639-
"refresh": "5s",
23639+
"refresh": false,
2364023640
"revision": 1,
2364123641
"schemaVersion": 38,
2364223642
"style": "dark",
@@ -23648,8 +23648,8 @@
2364823648
{
2364923649
"current": {
2365023650
"selected": false,
23651-
"text": "Prometheus",
23652-
"value": "Prometheus"
23651+
"text": "PromQL",
23652+
"value": "PromQL"
2365323653
},
2365423654
"hide": 0,
2365523655
"includeAll": false,
@@ -23667,8 +23667,8 @@
2366723667
{
2366823668
"current": {
2366923669
"selected": false,
23670-
"text": "node-exporter",
23671-
"value": "node-exporter"
23670+
"text": "node_exporter",
23671+
"value": "node_exporter"
2367223672
},
2367323673
"datasource": {
2367423674
"type": "prometheus",
@@ -23685,7 +23685,7 @@
2368523685
"query": "label_values(node_uname_info, job)",
2368623686
"refId": "Prometheus-job-Variable-Query"
2368723687
},
23688-
"refresh": 1,
23688+
"refresh": 2,
2368923689
"regex": "",
2369023690
"skipUrlSync": false,
2369123691
"sort": 1,
@@ -23697,8 +23697,8 @@
2369723697
{
2369823698
"current": {
2369923699
"selected": false,
23700-
"text": "node-exporter:9100",
23701-
"value": "node-exporter:9100"
23700+
"text": "host-0",
23701+
"value": "host-0"
2370223702
},
2370323703
"datasource": {
2370423704
"type": "prometheus",
@@ -23715,7 +23715,7 @@
2371523715
"query": "label_values(node_uname_info{job=\"$job\"}, instance)",
2371623716
"refId": "Prometheus-node-Variable-Query"
2371723717
},
23718-
"refresh": 1,
23718+
"refresh": 2,
2371923719
"regex": "",
2372023720
"skipUrlSync": false,
2372123721
"sort": 1,
@@ -23748,8 +23748,8 @@
2374823748
]
2374923749
},
2375023750
"time": {
23751-
"from": "now-5m",
23752-
"to": "now"
23751+
"from": "2024-01-06T14:05:29.481Z",
23752+
"to": "2024-01-06T15:01:09.313Z"
2375323753
},
2375423754
"timepicker": {
2375523755
"refresh_intervals": [
@@ -23770,6 +23770,6 @@
2377023770
"timezone": "browser",
2377123771
"title": "Node Exporter Full",
2377223772
"uid": "node-exporter",
23773-
"version": 2,
23773+
"version": 1,
2377423774
"weekStart": ""
23775-
}
23775+
}

dev/local/ch-bench-read/grafana/datasources.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ datasources:
1616
type: prometheus
1717
access: proxy
1818
orgId: 1
19-
url: http://oteldb:9090
19+
url: http://otelproxy:9090
2020
uid: promoteldb
2121
isDefault: true
2222

internal/promproxy/generate.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package promproxy
2+
3+
//go:generate go run github.com/ogen-go/ogen/cmd/jschemagen -typename Record --target record_gen.go --package promproxy schema.yml

internal/promproxy/promproxy.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ import (
1515
var _ promapi.Handler = &Server{}
1616

1717
// NewServer initializes new proxy Server from openapi client.
18-
func NewServer(api *promapi.Client) *Server {
18+
func NewServer(api *promapi.Client, rec *Recorder) *Server {
1919
return &Server{
2020
api: api,
21+
rec: rec,
2122
}
2223
}
2324

2425
// Server implement proxy server.
2526
type Server struct {
2627
api *promapi.Client
28+
rec *Recorder
2729
}
2830

2931
// GetLabelValues implements getLabelValues operation.
@@ -70,6 +72,9 @@ func (s *Server) GetQueryExemplars(ctx context.Context, params promapi.GetQueryE
7072
//
7173
// GET /api/v1/query_range
7274
func (s *Server) GetQueryRange(ctx context.Context, params promapi.GetQueryRangeParams) (*promapi.QueryResponse, error) {
75+
if err := s.rec.RecordGetQueryRange(params); err != nil {
76+
return nil, errors.Wrap(err, "record")
77+
}
7378
return s.api.GetQueryRange(ctx, params)
7479
}
7580

@@ -85,6 +90,9 @@ func (s *Server) GetRules(ctx context.Context, params promapi.GetRulesParams) (*
8590
//
8691
// GET /api/v1/series
8792
func (s *Server) GetSeries(ctx context.Context, params promapi.GetSeriesParams) (*promapi.SeriesResponse, error) {
93+
if err := s.rec.RecordSeriesQuery(params); err != nil {
94+
return nil, errors.Wrap(err, "record")
95+
}
8896
return s.api.GetSeries(ctx, params)
8997
}
9098

internal/promproxy/record.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package promproxy
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"strconv"
7+
"time"
8+
9+
"github.com/go-faster/errors"
10+
11+
"github.com/go-faster/oteldb/internal/promapi"
12+
)
13+
14+
// Recorder of prometheus queries.
15+
type Recorder struct {
16+
encoder *json.Encoder
17+
}
18+
19+
// NewRecorder returns new Recorder.
20+
func NewRecorder(writer io.Writer) *Recorder {
21+
e := json.NewEncoder(writer)
22+
return &Recorder{encoder: e}
23+
}
24+
25+
func (r *Recorder) encode(v any) error {
26+
if r == nil {
27+
return nil
28+
}
29+
var q Query
30+
switch t := v.(type) {
31+
case RangeQuery:
32+
q.SetRangeQuery(t)
33+
case InstantQuery:
34+
q.SetInstantQuery(t)
35+
case SeriesQuery:
36+
q.SetSeriesQuery(t)
37+
default:
38+
return errors.Errorf("unknown type %T", t)
39+
}
40+
if err := r.encoder.Encode(q); err != nil {
41+
return errors.Wrap(err, "encode")
42+
}
43+
return nil
44+
}
45+
46+
func ts(v promapi.PrometheusTimestamp) time.Time {
47+
seconds, err := strconv.ParseInt(string(v), 10, 64)
48+
if err != nil {
49+
return time.Time{}
50+
}
51+
return time.Unix(seconds, 0)
52+
}
53+
54+
func step(v string) int {
55+
seconds, err := strconv.Atoi(v)
56+
if err != nil {
57+
return 0
58+
}
59+
return seconds
60+
}
61+
62+
func optTS(v promapi.OptPrometheusTimestamp) OptDateTime {
63+
var dt OptDateTime
64+
if t, ok := v.Get(); ok {
65+
dt.SetTo(ts(t))
66+
}
67+
return dt
68+
}
69+
70+
func (r *Recorder) RecordSeriesQuery(v promapi.GetSeriesParams) error {
71+
return r.encode(SeriesQuery{
72+
Start: optTS(v.Start),
73+
End: optTS(v.End),
74+
Matchers: v.Match,
75+
})
76+
}
77+
78+
func (r *Recorder) RecordGetQuery(v promapi.GetQueryParams) error {
79+
return r.encode(InstantQuery{
80+
Query: v.Query,
81+
Time: optTS(v.Time),
82+
})
83+
}
84+
85+
// RecordGetQueryRange records range query.
86+
func (r *Recorder) RecordGetQueryRange(v promapi.GetQueryRangeParams) error {
87+
return r.encode(RangeQuery{
88+
Query: v.Query,
89+
Start: ts(v.Start),
90+
End: ts(v.End),
91+
Step: step(v.Step),
92+
})
93+
}

0 commit comments

Comments
 (0)