Skip to content

Commit

Permalink
Add trace datastore
Browse files Browse the repository at this point in the history
  • Loading branch information
dennis-tra committed Aug 23, 2023
1 parent 51ad837 commit 12ac85a
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 1 deletion.
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ require (
github.com/ipfs/go-detect-race v0.0.1
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8
github.com/jbenet/goprocess v0.1.4
go.opentelemetry.io/otel v1.16.0
go.opentelemetry.io/otel/trace v1.16.0
go.uber.org/multierr v1.5.0
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
)

require (
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/kr/pretty v0.2.0 // indirect
github.com/kr/text v0.1.0 // indirect
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.uber.org/atomic v1.6.0 // indirect
)

Expand Down
15 changes: 14 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Expand All @@ -24,8 +30,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
Expand Down Expand Up @@ -55,5 +67,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
228 changes: 228 additions & 0 deletions trace/trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// Package trace wraps a datastore where all datastore interactions are traced
// with open telemetry.
package trace

import (
"context"
"fmt"
"io"

ds "github.com/ipfs/go-datastore"
dsq "github.com/ipfs/go-datastore/query"
"go.opentelemetry.io/otel/attribute"
otel "go.opentelemetry.io/otel/trace"
)

// New returns a new traced datastore. All datastore interactions are traced.
func New(ds ds.Datastore, tracer otel.Tracer) *Datastore {
return &Datastore{ds: ds, tracer: tracer}
}

// Datastore is an adapter that traces inner datastore interactions.
type Datastore struct {
ds ds.Datastore
tracer otel.Tracer
}

var (
_ ds.Datastore = (*Datastore)(nil)
_ ds.Batching = (*Datastore)(nil)
_ ds.PersistentDatastore = (*Datastore)(nil)
_ ds.TxnDatastore = (*Datastore)(nil)
_ ds.CheckedDatastore = (*Datastore)(nil)
_ ds.ScrubbedDatastore = (*Datastore)(nil)
_ ds.GCDatastore = (*Datastore)(nil)
_ io.Closer = (*Datastore)(nil)
)

// Put implements the ds.Datastore interface.
func (t *Datastore) Put(ctx context.Context, key ds.Key, value []byte) (err error) {
ctx, span := t.tracer.Start(ctx, "Put", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.ds.Put(ctx, key, value)
}

// Sync implements Datastore.Sync
func (t *Datastore) Sync(ctx context.Context, key ds.Key) error {
ctx, span := t.tracer.Start(ctx, "Sync", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.ds.Sync(ctx, key)
}

// Get implements the ds.Datastore interface.
func (t *Datastore) Get(ctx context.Context, key ds.Key) (value []byte, err error) {
ctx, span := t.tracer.Start(ctx, "Get", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.ds.Get(ctx, key)
}

// Has implements the ds.Datastore interface.
func (t *Datastore) Has(ctx context.Context, key ds.Key) (exists bool, err error) {
ctx, span := t.tracer.Start(ctx, "Has", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.ds.Has(ctx, key)
}

// GetSize implements the ds.Datastore interface.
func (t *Datastore) GetSize(ctx context.Context, key ds.Key) (size int, err error) {
ctx, span := t.tracer.Start(ctx, "GetSize", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.ds.GetSize(ctx, key)
}

// Delete implements the ds.Datastore interface.
func (t *Datastore) Delete(ctx context.Context, key ds.Key) (err error) {
ctx, span := t.tracer.Start(ctx, "Delete", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.ds.Delete(ctx, key)
}

// Query implements the ds.Datastore interface.
func (t *Datastore) Query(ctx context.Context, q dsq.Query) (dsq.Results, error) {
ctx, span := t.tracer.Start(ctx, "Query", otel.WithAttributes(attribute.String("query", q.String())))
defer span.End()
return t.ds.Query(ctx, q)
}

// Batch implements the ds.Batching interface.
func (t *Datastore) Batch(ctx context.Context) (ds.Batch, error) {
ctx, span := t.tracer.Start(ctx, "Batch")
defer span.End()

if batch, ok := t.ds.(ds.Batching); ok {
return batch.Batch(ctx)
}

return ds.NewBasicBatch(t), nil

Check warning on line 96 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L96

Added line #L96 was not covered by tests
}

// DiskUsage implements the ds.PersistentDatastore interface.
func (t *Datastore) DiskUsage(ctx context.Context) (uint64, error) {
ctx, span := t.tracer.Start(ctx, "DiskUsage")
defer span.End()
return ds.DiskUsage(ctx, t.ds)

Check warning on line 103 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L100-L103

Added lines #L100 - L103 were not covered by tests
}

// Scrub implements the ds.ScrubbedDatastore interface.
func (t *Datastore) Scrub(ctx context.Context) error {
ctx, span := t.tracer.Start(ctx, "Scrub")
defer span.End()

if dstore, ok := t.tracer.(ds.ScrubbedDatastore); ok {
return dstore.Scrub(ctx)
}

Check warning on line 113 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L107-L113

Added lines #L107 - L113 were not covered by tests

return nil

Check warning on line 115 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L115

Added line #L115 was not covered by tests
}

// CollectGarbage implements the ds.GCDatastore interface.
func (t *Datastore) CollectGarbage(ctx context.Context) error {
ctx, span := t.tracer.Start(ctx, "CollectGarbage")
defer span.End()

if dstore, ok := t.tracer.(ds.GCDatastore); ok {
return dstore.CollectGarbage(ctx)
}

Check warning on line 125 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L119-L125

Added lines #L119 - L125 were not covered by tests

return nil

Check warning on line 127 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L127

Added line #L127 was not covered by tests
}

// Check implements the ds.CheckedDatastore interface.
func (t *Datastore) Check(ctx context.Context) error {
ctx, span := t.tracer.Start(ctx, "Check")
defer span.End()

if dstore, ok := t.tracer.(ds.CheckedDatastore); ok {
return dstore.Check(ctx)
}

Check warning on line 137 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L131-L137

Added lines #L131 - L137 were not covered by tests

return nil

Check warning on line 139 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L139

Added line #L139 was not covered by tests
}

// NewTransaction implements the ds.TxnDatastore interface.
func (t *Datastore) NewTransaction(ctx context.Context, readOnly bool) (ds.Txn, error) {
ctx, span := t.tracer.Start(ctx, "NewTransaction", otel.WithAttributes(attribute.Bool("readOnly", readOnly)))
defer span.End()

if txnDs, ok := t.ds.(ds.TxnDatastore); ok {
txn, err := txnDs.NewTransaction(ctx, readOnly)
if err != nil {
return nil, err
}
return &Txn{txn: txn, tracer: t.tracer}, nil

Check warning on line 152 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L143-L152

Added lines #L143 - L152 were not covered by tests
}

return nil, fmt.Errorf("transactions are unsupported by traced datastore")

Check warning on line 155 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L155

Added line #L155 was not covered by tests
}

// Close closes the inner datastore (if it implements the io.Closer interface).
func (t *Datastore) Close() error {
if closer, ok := t.ds.(io.Closer); ok {
return closer.Close()
}
return nil

Check warning on line 163 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L159-L163

Added lines #L159 - L163 were not covered by tests
}

// Txn is an adapter that traces datastore transactions
type Txn struct {
txn ds.Txn
tracer otel.Tracer
}

var _ ds.Txn = (*Txn)(nil)

// Put implements the ds.Txn interface.
func (t *Txn) Put(ctx context.Context, key ds.Key, value []byte) (err error) {
ctx, span := t.tracer.Start(ctx, "Put", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.txn.Put(ctx, key, value)

Check warning on line 178 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L175-L178

Added lines #L175 - L178 were not covered by tests
}

// Get implements the ds.Txn interface.
func (t *Txn) Get(ctx context.Context, key ds.Key) (value []byte, err error) {
ctx, span := t.tracer.Start(ctx, "Get", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.txn.Get(ctx, key)

Check warning on line 185 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L182-L185

Added lines #L182 - L185 were not covered by tests
}

// Has implements the ds.Txn interface.
func (t *Txn) Has(ctx context.Context, key ds.Key) (exists bool, err error) {
ctx, span := t.tracer.Start(ctx, "Has", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.txn.Has(ctx, key)

Check warning on line 192 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L189-L192

Added lines #L189 - L192 were not covered by tests
}

// GetSize implements the ds.Txn interface.
func (t *Txn) GetSize(ctx context.Context, key ds.Key) (size int, err error) {
ctx, span := t.tracer.Start(ctx, "GetSize", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.txn.GetSize(ctx, key)

Check warning on line 199 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L196-L199

Added lines #L196 - L199 were not covered by tests
}

// Delete implements the ds.Txn interface.
func (t *Txn) Delete(ctx context.Context, key ds.Key) (err error) {
ctx, span := t.tracer.Start(ctx, "Delete", otel.WithAttributes(attribute.String("key", key.String())))
defer span.End()
return t.txn.Delete(ctx, key)

Check warning on line 206 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L203-L206

Added lines #L203 - L206 were not covered by tests
}

// Query implements the ds.Txn interface.
func (t *Txn) Query(ctx context.Context, q dsq.Query) (dsq.Results, error) {
ctx, span := t.tracer.Start(ctx, "Query", otel.WithAttributes(attribute.String("query", q.String())))
defer span.End()
return t.txn.Query(ctx, q)

Check warning on line 213 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L210-L213

Added lines #L210 - L213 were not covered by tests
}

// Commit implements the ds.Txn interface.
func (t *Txn) Commit(ctx context.Context) error {
ctx, span := t.tracer.Start(ctx, "Commit")
defer span.End()
return t.txn.Commit(ctx)

Check warning on line 220 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L217-L220

Added lines #L217 - L220 were not covered by tests
}

// Discard implements the ds.Txn interface.
func (t *Txn) Discard(ctx context.Context) {
ctx, span := t.tracer.Start(ctx, "Discard")
defer span.End()
t.txn.Discard(ctx)

Check warning on line 227 in trace/trace.go

View check run for this annotation

Codecov / codecov/patch

trace/trace.go#L224-L227

Added lines #L224 - L227 were not covered by tests
}
14 changes: 14 additions & 0 deletions trace/trace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package trace

import (
"testing"

"github.com/ipfs/go-datastore"
dstest "github.com/ipfs/go-datastore/test"
"go.opentelemetry.io/otel"
)

func TestTraceAll(t *testing.T) {
tracer := otel.Tracer("tracer")
dstest.SubtestAll(t, New(datastore.NewMapDatastore(), tracer))
}

0 comments on commit 12ac85a

Please sign in to comment.