Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ type config struct {
MeterProvider metric.MeterProvider
Meter metric.Meter

// metricMethodAttrs holds the precomputed base attributes per Method.
metricMethodAttrs map[Method]methodMetricAttributes

Instruments *instruments

SpanOptions SpanOptions
Expand Down Expand Up @@ -143,14 +146,14 @@ func defaultSpanNameFormatter(_ context.Context, method Method, _ string) string
}

// newConfig returns a config with all Options set.
func newConfig(options ...Option) config {
cfg := config{
func newConfig(options ...Option) *config {
cfg := &config{
TracerProvider: otel.GetTracerProvider(),
MeterProvider: otel.GetMeterProvider(),
SpanNameFormatter: defaultSpanNameFormatter,
}
for _, opt := range options {
opt.Apply(&cfg)
opt.Apply(cfg)
}

cfg.Tracer = cfg.TracerProvider.Tracer(
Expand All @@ -169,5 +172,22 @@ func newConfig(options ...Option) config {
otel.Handle(err)
}

cfg.metricMethodAttrs = buildMethodAttributes(cfg.Attributes)

return cfg
}

func buildMethodAttributes(base []attribute.KeyValue) map[Method]methodMetricAttributes {
out := make(map[Method]methodMetricAttributes, len(allMethods))

for _, m := range allMethods {
attrs := getMethodAttributes(m, base)

out[m] = methodMetricAttributes{
attrs: attrs,
set: attribute.NewSet(attrs...),
}
}

return out
}
6 changes: 4 additions & 2 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestNewConfig(t *testing.T) {
// Ignore function compares for test equality check
cfg.SpanNameFormatter = nil

assert.Equal(t, config{
assert.Equal(t, &config{
TracerProvider: otel.GetTracerProvider(),
Tracer: otel.GetTracerProvider().Tracer(
instrumentationName,
Expand All @@ -52,7 +52,9 @@ func TestNewConfig(t *testing.T) {
),
// No need to check values of instruments in this part.
Instruments: cfg.Instruments,
SpanOptions: SpanOptions{Ping: true},
// No need to check values of metricBase in this part.
metricMethodAttrs: cfg.metricMethodAttrs,
SpanOptions: SpanOptions{Ping: true},
Attributes: []attribute.KeyValue{
semconv.DBSystemNameMySQL,
},
Expand Down
50 changes: 16 additions & 34 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ var (

type otConn struct {
driver.Conn
cfg config
cfg *config
}

func newConn(conn driver.Conn, cfg config) *otConn {
func newConn(conn driver.Conn, cfg *config) *otConn {
return &otConn{
Conn: conn,
cfg: cfg,
Expand All @@ -57,25 +57,17 @@ func (c *otConn) Ping(ctx context.Context) (err error) {
}

method := MethodConnPing
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg, method, "", nil)

defer func() {
onDefer(err)
}()
metric := startDurationMetric(ctx)
defer metric.Record(c.cfg, method, &err)

if c.cfg.SpanOptions.Ping {
if filterSpan(ctx, c.cfg.SpanOptions, method, "", nil) {
var span trace.Span

ctx, span = createSpan(ctx, c.cfg, method, false, "", nil)

defer func() {
if err != nil {
recordSpanError(span, c.cfg.SpanOptions, err)
}

span.End()
}()
defer span.End()
defer recordSpanErrorDeferred(span, c.cfg.SpanOptions, &err)
}
}

Expand All @@ -102,11 +94,9 @@ func (c *otConn) ExecContext(
}

method := MethodConnExec
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg, method, query, args)

defer func() {
onDefer(err)
}()
metric := startDurationMetric(ctx)
defer metric.Record(c.cfg, method, &err)

var span trace.Span
if filterSpan(ctx, c.cfg.SpanOptions, method, query, args) {
Expand Down Expand Up @@ -141,11 +131,9 @@ func (c *otConn) QueryContext(
}

method := MethodConnQuery
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg, method, query, args)

defer func() {
onDefer(err)
}()
metric := startDurationMetric(ctx)
defer metric.RecordQuery(c.cfg, method, query, args, &err)

var span trace.Span

Expand All @@ -166,11 +154,9 @@ func (c *otConn) QueryContext(

func (c *otConn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) {
method := MethodConnPrepare
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg, method, query, nil)

defer func() {
onDefer(err)
}()
metric := startDurationMetric(ctx)
defer metric.RecordQuery(c.cfg, method, query, nil, &err)

var span trace.Span
if !c.cfg.SpanOptions.OmitConnPrepare && filterSpan(ctx, c.cfg.SpanOptions, method, query, nil) {
Expand Down Expand Up @@ -203,11 +189,9 @@ func (c *otConn) PrepareContext(ctx context.Context, query string) (stmt driver.

func (c *otConn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) {
method := MethodConnBeginTx
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg, method, "", nil)

defer func() {
onDefer(err)
}()
metric := startDurationMetric(ctx)
defer metric.Record(c.cfg, method, &err)

var beginTxCtx context.Context

Expand Down Expand Up @@ -265,11 +249,9 @@ func (c *otConn) ResetSession(ctx context.Context) (err error) {
}

method := MethodConnResetSession
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg, method, "", nil)

defer func() {
onDefer(err)
}()
metric := startDurationMetric(ctx)
defer metric.Record(c.cfg, method, &err)

var span trace.Span
if !c.cfg.SpanOptions.OmitConnResetSession && filterSpan(ctx, c.cfg.SpanOptions, method, "", nil) {
Expand Down
111 changes: 111 additions & 0 deletions conn_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright Sam Xie
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package otelsql

import (
"context"
"database/sql/driver"
"testing"
)

func BenchmarkConnQueryContext(b *testing.B) {
for name, newCfg := range benchMarkConfigs {
b.Run(name, func(b *testing.B) {
cfg := newCfg()
conn := newConn(benchConn{}, cfg)
ctx := context.Background()

b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
rows, err := conn.QueryContext(ctx, "SELECT 1", nil)
if err != nil {
b.Fatal(err)
}

if err := rows.Close(); err != nil {
b.Fatal(err)
}
}
})
})
}
}

func BenchmarkConnExecContext(b *testing.B) {
for name, newCfg := range benchMarkConfigs {
b.Run(name, func(b *testing.B) {
cfg := newCfg()
conn := newConn(benchConn{}, cfg)
ctx := context.Background()

b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if _, err := conn.ExecContext(ctx, "UPDATE x SET y=1", nil); err != nil {
b.Fatal(err)
}
}
})
})
}
}

func BenchmarkConnResetSession(b *testing.B) {
for name, newCfg := range benchMarkConfigs {
b.Run(name, func(b *testing.B) {
cfg := newCfg()
conn := newConn(benchConn{}, cfg)
ctx := context.Background()

b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if err := conn.ResetSession(ctx); err != nil {
b.Fatal(err)
}
}
})
})
}
}

func BenchmarkConnBeginTx(b *testing.B) {
for name, newCfg := range benchMarkConfigs {
b.Run(name, func(b *testing.B) {
cfg := newCfg()
conn := newConn(benchConn{}, cfg)
ctx := context.Background()

b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
tx, err := conn.BeginTx(ctx, driver.TxOptions{})
if err != nil {
b.Fatal(err)
}

if err := tx.Commit(); err != nil {
b.Fatal(err)
}
}
})
})
}
}
6 changes: 3 additions & 3 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ func TestOtConn_ResetSession(t *testing.T) {
func TestOtConn_IsValid(t *testing.T) {
t.Run("delegates to underlying conn", func(t *testing.T) {
mc := &mockValidatorConn{valid: true}
conn := newConn(mc, config{})
conn := newConn(mc, &config{})
assert.True(t, conn.IsValid())

mc.valid = false
Expand All @@ -872,7 +872,7 @@ func TestOtConn_IsValid(t *testing.T) {

t.Run("returns true when underlying conn does not implement Validator", func(t *testing.T) {
mc := newMockConn(false)
conn := newConn(mc, config{})
conn := newConn(mc, &config{})
assert.True(t, conn.IsValid())
})
}
Expand All @@ -890,7 +890,7 @@ var _ driver.Validator = (*mockValidatorConn)(nil)

func TestOtConn_Raw(t *testing.T) {
raw := newMockConn(false)
conn := newConn(raw, config{})
conn := newConn(raw, &config{})

assert.Equal(t, raw, conn.Raw())
}
8 changes: 3 additions & 5 deletions connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var (
type otConnector struct {
driver.Connector
otDriver *otDriver
cfg config
cfg *config
}

func newConnector(connector driver.Connector, otDriver *otDriver) *otConnector {
Expand All @@ -43,11 +43,9 @@ func newConnector(connector driver.Connector, otDriver *otDriver) *otConnector {

func (c *otConnector) Connect(ctx context.Context) (connection driver.Conn, err error) {
method := MethodConnectorConnect
onDefer := recordMetric(ctx, c.cfg.Instruments, c.cfg, method, "", nil)

defer func() {
onDefer(err)
}()
metric := startDurationMetric(ctx)
defer metric.Record(c.cfg, method, &err)

var span trace.Span
if !c.cfg.SpanOptions.OmitConnectorConnect && filterSpan(ctx, c.cfg.SpanOptions, method, "", nil) {
Expand Down
6 changes: 3 additions & 3 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ var (

type otDriver struct {
driver driver.Driver
cfg config
cfg *config
}

func newDriver(dri driver.Driver, cfg config) driver.Driver {
func newDriver(dri driver.Driver, cfg *config) driver.Driver {
if _, ok := dri.(driver.DriverContext); ok {
return newOtDriver(dri, cfg)
}
// Only implements driver.Driver
return struct{ driver.Driver }{newOtDriver(dri, cfg)}
}

func newOtDriver(dri driver.Driver, cfg config) *otDriver {
func newOtDriver(dri driver.Driver, cfg *config) *otDriver {
return &otDriver{driver: dri, cfg: cfg}
}

Expand Down
Loading
Loading