Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add traced datastore #209

Merged
merged 2 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're not tracing errors anywhere. We probably want to call span.RecordError(err) for all errors here (it'll ignore nil errors).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good point, I guess it also makes sense to set the status then:

	// RecordError will record err as an exception span event for this span. An
	// additional call to SetStatus is required if the Status of the Span should
	// be set to Error, as this method does not change the Span status. If this
	// span is not being recorded or err is nil then this method does nothing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that API is bonkers. But ok.

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()
dennis-tra marked this conversation as resolved.
Show resolved Hide resolved

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))
}