Skip to content

Commit

Permalink
feat(bunotel): add experimental bun.query.timing metric
Browse files Browse the repository at this point in the history
  • Loading branch information
vmihailenco committed Sep 25, 2021
1 parent 5bce7f4 commit 2cdb384
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 95 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ jobs:
- uses: ncipollo/release-action@v1
with:
body:
Check [CHANGELOG.md](https://github.com/uptrace/bun/blob/master/CHANGELOG.md) for
details
Please refer to [CHANGELOG.md](https://github.com/uptrace/bun/blob/master/CHANGELOG.md)
for details
5 changes: 3 additions & 2 deletions extra/bunotel/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ replace github.com/uptrace/bun => ../..

require (
github.com/uptrace/bun v1.0.8
go.opentelemetry.io/otel v1.0.0-RC1
go.opentelemetry.io/otel/trace v1.0.0-RC1
go.opentelemetry.io/otel v1.0.0-RC3
go.opentelemetry.io/otel/metric v0.23.0
go.opentelemetry.io/otel/trace v1.0.0-RC3
)
14 changes: 8 additions & 6 deletions extra/bunotel/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2el
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
go.opentelemetry.io/otel v1.0.0-RC1 h1:4CeoX93DNTWt8awGK9JmNXzF9j7TyOu9upscEdtcdXc=
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
go.opentelemetry.io/otel/oteltest v1.0.0-RC1 h1:G685iP3XiskCwk/z0eIabL55XUl2gk0cljhGk9sB0Yk=
go.opentelemetry.io/otel/oteltest v1.0.0-RC1/go.mod h1:+eoIG0gdEOaPNftuy1YScLr1Gb4mL/9lpDkZ0JjMRq4=
go.opentelemetry.io/otel/trace v1.0.0-RC1 h1:jrjqKJZEibFrDz+umEASeU3LvdVyWKlnTh7XEfwrT58=
go.opentelemetry.io/otel/trace v1.0.0-RC1/go.mod h1:86UHmyHWFEtWjfWPSbu0+d0Pf9Q6e1U+3ViBOc+NXAg=
go.opentelemetry.io/otel v1.0.0-RC3 h1:kvwiyEkiUT/JaadXzVLI/R1wDO934A7r3Bs2wEe6wqA=
go.opentelemetry.io/otel v1.0.0-RC3/go.mod h1:Ka5j3ua8tZs4Rkq4Ex3hwgBgOchyPVq5S6P2lz//nKQ=
go.opentelemetry.io/otel/internal/metric v0.23.0 h1:mPfzm9Iqhw7G2nDBmUAjFTfPqLZPbOW2k7QI57ITbaI=
go.opentelemetry.io/otel/internal/metric v0.23.0/go.mod h1:z+RPiDJe30YnCrOhFGivwBS+DU1JU/PiLKkk4re2DNY=
go.opentelemetry.io/otel/metric v0.23.0 h1:mYCcDxi60P4T27/0jchIDFa1WHEfQeU3zH9UEMpnj2c=
go.opentelemetry.io/otel/metric v0.23.0/go.mod h1:G/Nn9InyNnIv7J6YVkQfpc0JCfKBNJaERBGw08nqmVQ=
go.opentelemetry.io/otel/trace v1.0.0-RC3 h1:9F0ayEvlxv8BmNmPbU005WK7hC+7KbOazCPZjNa1yME=
go.opentelemetry.io/otel/trace v1.0.0-RC3/go.mod h1:VUt2TUYd8S2/ZRX09ZDFZQwn2RqfMB5MzO17jBojGxo=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
44 changes: 37 additions & 7 deletions extra/bunotel/otel.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,29 @@ import (
"database/sql"
"runtime"
"strings"
"time"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/global"
"go.opentelemetry.io/otel/trace"

"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/schema"
)

var tracer = otel.Tracer("github.com/uptrace/bun")
var (
tracer = otel.Tracer("github.com/uptrace/bun")
meter = metric.Must(global.Meter("github.com/uptrace/bun"))
queryHistogram = meter.NewInt64Histogram(
"bun.query.timing",
metric.WithDescription("Timing of processed queries"),
metric.WithUnit("milliseconds"),
)
)

type ConfigOption func(*QueryHook)

Expand All @@ -37,25 +48,35 @@ func (h *QueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) cont
return ctx
}

operation := event.Operation()
ctx, span := tracer.Start(ctx, operation)
span.SetAttributes(attribute.String("db.operation", operation))

ctx, _ = tracer.Start(ctx, "")
return ctx
}

func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
operation := event.Operation()
dbOperation := attribute.String("db.operation", operation)

labels := []attribute.KeyValue{dbOperation}
if tableName := tableName(event.QueryAppender); tableName != "" {
labels = append(labels, attribute.String("db.table", tableName))
}

queryHistogram.Record(ctx, time.Since(event.StartTime).Milliseconds(), labels...)

span := trace.SpanFromContext(ctx)
if !span.IsRecording() {
return
}

span.SetName(operation)
defer span.End()

query := eventQuery(event)
fn, file, line := funcFileLine("github.com/uptrace/bun")

attrs := make([]attribute.KeyValue, 0, 10)
attrs = append(attrs,
dbOperation,
attribute.String("db.statement", query),
attribute.String("code.function", fn),
attribute.String("code.filepath", file),
Expand Down Expand Up @@ -110,8 +131,8 @@ func funcFileLine(pkg string) (string, string, int) {
}

func eventQuery(event *bun.QueryEvent) string {
const softQueryLimit = 5000
const hardQueryLimit = 10000
const softQueryLimit = 8000
const hardQueryLimit = 16000

var query string

Expand Down Expand Up @@ -147,3 +168,12 @@ func dbSystem(db *bun.DB) string {
return ""
}
}

func tableName(query schema.Query) string {
if v, ok := query.(interface {
GetTableName() string
}); ok {
return v.GetTableName()
}
return ""
}
12 changes: 6 additions & 6 deletions join.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (

type relationJoin struct {
Parent *relationJoin
BaseModel tableModel
JoinModel tableModel
BaseModel TableModel
JoinModel TableModel
Relation *schema.Relation

apply func(*SelectQuery) *SelectQuery
Expand Down Expand Up @@ -71,8 +71,8 @@ func (j *relationJoin) manyQuery(q *SelectQuery) *SelectQuery {
where = appendChildValues(
q.db.Formatter(),
where,
j.JoinModel.Root(),
j.JoinModel.ParentIndex(),
j.JoinModel.rootValue(),
j.JoinModel.parentIndex(),
j.Relation.BaseFields,
)
where = append(where, ")"...)
Expand Down Expand Up @@ -135,7 +135,7 @@ func (j *relationJoin) m2mQuery(q *SelectQuery) *SelectQuery {
}
q = q.Model(m2mModel)

index := j.JoinModel.ParentIndex()
index := j.JoinModel.parentIndex()
baseTable := j.BaseModel.Table()

//nolint
Expand All @@ -154,7 +154,7 @@ func (j *relationJoin) m2mQuery(q *SelectQuery) *SelectQuery {
join = append(join, col.SQLName...)
}
join = append(join, ") IN ("...)
join = appendChildValues(fmter, join, j.BaseModel.Root(), index, baseTable.PKs)
join = appendChildValues(fmter, join, j.BaseModel.rootValue(), index, baseTable.PKs)
join = append(join, ")"...)
q = q.Join(internal.String(join))

Expand Down
30 changes: 13 additions & 17 deletions model.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,8 @@ type rowScanner interface {
ScanRow(ctx context.Context, rows *sql.Rows) error
}

type model interface {
type TableModel interface {
Model
}

type tableModel interface {
model

schema.BeforeScanHook
schema.AfterScanHook
Expand All @@ -38,19 +34,19 @@ type tableModel interface {
Table() *schema.Table
Relation() *schema.Relation

Join(string) *relationJoin
GetJoin(string) *relationJoin
GetJoins() []relationJoin
AddJoin(relationJoin) *relationJoin
join(string) *relationJoin
getJoin(string) *relationJoin
getJoins() []relationJoin
addJoin(relationJoin) *relationJoin

Root() reflect.Value
ParentIndex() []int
Mount(reflect.Value)
rootValue() reflect.Value
parentIndex() []int
mount(reflect.Value)

updateSoftDeleteField(time.Time) error
}

func newModel(db *DB, dest []interface{}) (model, error) {
func newModel(db *DB, dest []interface{}) (Model, error) {
if len(dest) == 1 {
return _newModel(db, dest[0], true)
}
Expand All @@ -74,11 +70,11 @@ func newModel(db *DB, dest []interface{}) (model, error) {
return newSliceModel(db, dest, values), nil
}

func newSingleModel(db *DB, dest interface{}) (model, error) {
func newSingleModel(db *DB, dest interface{}) (Model, error) {
return _newModel(db, dest, false)
}

func _newModel(db *DB, dest interface{}, scan bool) (model, error) {
func _newModel(db *DB, dest interface{}, scan bool) (Model, error) {
switch dest := dest.(type) {
case nil:
return nil, errNilModel
Expand Down Expand Up @@ -150,7 +146,7 @@ func newTableModelIndex(
root reflect.Value,
index []int,
rel *schema.Relation,
) (tableModel, error) {
) (TableModel, error) {
typ := typeByIndex(table.Type, index)

if typ.Kind() == reflect.Struct {
Expand Down Expand Up @@ -195,7 +191,7 @@ func validMap(typ reflect.Type) error {

//------------------------------------------------------------------------------

func isSingleRowModel(m model) bool {
func isSingleRowModel(m Model) bool {
switch m.(type) {
case *mapModel,
*structTableModel,
Expand Down
2 changes: 1 addition & 1 deletion model_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type mapModel struct {
scanIndex int
}

var _ model = (*mapModel)(nil)
var _ Model = (*mapModel)(nil)

func newMapModel(db *DB, dest *map[string]interface{}) *mapModel {
m := &mapModel{
Expand Down
2 changes: 1 addition & 1 deletion model_map_slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type mapSliceModel struct {
keys []string
}

var _ model = (*mapSliceModel)(nil)
var _ Model = (*mapSliceModel)(nil)

func newMapSliceModel(db *DB, dest *[]map[string]interface{}) *mapSliceModel {
return &mapSliceModel{
Expand Down
2 changes: 1 addition & 1 deletion model_scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type scanModel struct {
scanIndex int
}

var _ model = (*scanModel)(nil)
var _ Model = (*scanModel)(nil)

func newScanModel(db *DB, dest []interface{}) *scanModel {
return &scanModel{
Expand Down
2 changes: 1 addition & 1 deletion model_slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type sliceModel struct {
info []sliceInfo
}

var _ model = (*sliceModel)(nil)
var _ Model = (*sliceModel)(nil)

func newSliceModel(db *DB, dest []interface{}, values []reflect.Value) *sliceModel {
return &sliceModel{
Expand Down
6 changes: 3 additions & 3 deletions model_table_has_many.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type hasManyModel struct {
structKey []interface{}
}

var _ tableModel = (*hasManyModel)(nil)
var _ TableModel = (*hasManyModel)(nil)

func newHasManyModel(j *relationJoin) *hasManyModel {
baseTable := j.BaseModel.Table()
Expand Down Expand Up @@ -129,11 +129,11 @@ func (m *hasManyModel) parkStruct() error {
return nil
}

func baseValues(model tableModel, fields []*schema.Field) map[internal.MapKey][]reflect.Value {
func baseValues(model TableModel, fields []*schema.Field) map[internal.MapKey][]reflect.Value {
fieldIndex := model.Relation().Field.Index
m := make(map[internal.MapKey][]reflect.Value)
key := make([]interface{}, 0, len(fields))
walk(model.Root(), model.ParentIndex(), func(v reflect.Value) {
walk(model.rootValue(), model.parentIndex(), func(v reflect.Value) {
key = modelKey(key[:0], v, fields)
mapKey := internal.NewMapKey(key)
m[mapKey] = append(m[mapKey], v.FieldByIndex(fieldIndex))
Expand Down
2 changes: 1 addition & 1 deletion model_table_m2m.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type m2mModel struct {
structKey []interface{}
}

var _ tableModel = (*m2mModel)(nil)
var _ TableModel = (*m2mModel)(nil)

func newM2MModel(j *relationJoin) *m2mModel {
baseTable := j.BaseModel.Table()
Expand Down
12 changes: 4 additions & 8 deletions model_table_slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type sliceTableModel struct {
nextElem func() reflect.Value
}

var _ tableModel = (*sliceTableModel)(nil)
var _ TableModel = (*sliceTableModel)(nil)

func newSliceTableModel(
db *DB, dest interface{}, slice reflect.Value, elemType reflect.Type,
Expand Down Expand Up @@ -46,19 +46,15 @@ func (m *sliceTableModel) init(sliceType reflect.Type) {
}
}

func (m *sliceTableModel) Join(name string) *relationJoin {
return m.join(m.slice, name)
}

func (m *sliceTableModel) Bind(bind reflect.Value) {
m.slice = bind.Field(m.index[len(m.index)-1])
func (m *sliceTableModel) join(name string) *relationJoin {
return m._join(m.slice, name)
}

func (m *sliceTableModel) SetCap(cap int) {
if cap > 100 {
cap = 100
}
if m.slice.Cap() < cap {
if m.slice.Cap() == 0 {
m.slice.Set(reflect.MakeSlice(m.slice.Type(), 0, cap))
}
}
Expand Down
Loading

0 comments on commit 2cdb384

Please sign in to comment.