Skip to content

Commit 12ac85a

Browse files
committed
Add trace datastore
1 parent 51ad837 commit 12ac85a

File tree

4 files changed

+261
-1
lines changed

4 files changed

+261
-1
lines changed

Diff for: go.mod

+5
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@ require (
55
github.com/ipfs/go-detect-race v0.0.1
66
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8
77
github.com/jbenet/goprocess v0.1.4
8+
go.opentelemetry.io/otel v1.16.0
9+
go.opentelemetry.io/otel/trace v1.16.0
810
go.uber.org/multierr v1.5.0
911
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
1012
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
1113
)
1214

1315
require (
16+
github.com/go-logr/logr v1.2.4 // indirect
17+
github.com/go-logr/stdr v1.2.2 // indirect
1418
github.com/kr/pretty v0.2.0 // indirect
1519
github.com/kr/text v0.1.0 // indirect
20+
go.opentelemetry.io/otel/metric v1.16.0 // indirect
1621
go.uber.org/atomic v1.6.0 // indirect
1722
)
1823

Diff for: go.sum

+14-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
33
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
44
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
55
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
7+
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
8+
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
9+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
10+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
11+
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
612
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
713
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
814
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -24,8 +30,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
2430
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2531
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
2632
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
27-
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
2833
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
34+
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
35+
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
36+
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
37+
go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo=
38+
go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4=
39+
go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs=
40+
go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0=
2941
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
3042
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
3143
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
@@ -55,5 +67,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
5567
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
5668
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
5769
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
70+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
5871
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
5972
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

Diff for: trace/trace.go

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// Package trace wraps a datastore where all datastore interactions are traced
2+
// with open telemetry.
3+
package trace
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"io"
9+
10+
ds "github.com/ipfs/go-datastore"
11+
dsq "github.com/ipfs/go-datastore/query"
12+
"go.opentelemetry.io/otel/attribute"
13+
otel "go.opentelemetry.io/otel/trace"
14+
)
15+
16+
// New returns a new traced datastore. All datastore interactions are traced.
17+
func New(ds ds.Datastore, tracer otel.Tracer) *Datastore {
18+
return &Datastore{ds: ds, tracer: tracer}
19+
}
20+
21+
// Datastore is an adapter that traces inner datastore interactions.
22+
type Datastore struct {
23+
ds ds.Datastore
24+
tracer otel.Tracer
25+
}
26+
27+
var (
28+
_ ds.Datastore = (*Datastore)(nil)
29+
_ ds.Batching = (*Datastore)(nil)
30+
_ ds.PersistentDatastore = (*Datastore)(nil)
31+
_ ds.TxnDatastore = (*Datastore)(nil)
32+
_ ds.CheckedDatastore = (*Datastore)(nil)
33+
_ ds.ScrubbedDatastore = (*Datastore)(nil)
34+
_ ds.GCDatastore = (*Datastore)(nil)
35+
_ io.Closer = (*Datastore)(nil)
36+
)
37+
38+
// Put implements the ds.Datastore interface.
39+
func (t *Datastore) Put(ctx context.Context, key ds.Key, value []byte) (err error) {
40+
ctx, span := t.tracer.Start(ctx, "Put", otel.WithAttributes(attribute.String("key", key.String())))
41+
defer span.End()
42+
return t.ds.Put(ctx, key, value)
43+
}
44+
45+
// Sync implements Datastore.Sync
46+
func (t *Datastore) Sync(ctx context.Context, key ds.Key) error {
47+
ctx, span := t.tracer.Start(ctx, "Sync", otel.WithAttributes(attribute.String("key", key.String())))
48+
defer span.End()
49+
return t.ds.Sync(ctx, key)
50+
}
51+
52+
// Get implements the ds.Datastore interface.
53+
func (t *Datastore) Get(ctx context.Context, key ds.Key) (value []byte, err error) {
54+
ctx, span := t.tracer.Start(ctx, "Get", otel.WithAttributes(attribute.String("key", key.String())))
55+
defer span.End()
56+
return t.ds.Get(ctx, key)
57+
}
58+
59+
// Has implements the ds.Datastore interface.
60+
func (t *Datastore) Has(ctx context.Context, key ds.Key) (exists bool, err error) {
61+
ctx, span := t.tracer.Start(ctx, "Has", otel.WithAttributes(attribute.String("key", key.String())))
62+
defer span.End()
63+
return t.ds.Has(ctx, key)
64+
}
65+
66+
// GetSize implements the ds.Datastore interface.
67+
func (t *Datastore) GetSize(ctx context.Context, key ds.Key) (size int, err error) {
68+
ctx, span := t.tracer.Start(ctx, "GetSize", otel.WithAttributes(attribute.String("key", key.String())))
69+
defer span.End()
70+
return t.ds.GetSize(ctx, key)
71+
}
72+
73+
// Delete implements the ds.Datastore interface.
74+
func (t *Datastore) Delete(ctx context.Context, key ds.Key) (err error) {
75+
ctx, span := t.tracer.Start(ctx, "Delete", otel.WithAttributes(attribute.String("key", key.String())))
76+
defer span.End()
77+
return t.ds.Delete(ctx, key)
78+
}
79+
80+
// Query implements the ds.Datastore interface.
81+
func (t *Datastore) Query(ctx context.Context, q dsq.Query) (dsq.Results, error) {
82+
ctx, span := t.tracer.Start(ctx, "Query", otel.WithAttributes(attribute.String("query", q.String())))
83+
defer span.End()
84+
return t.ds.Query(ctx, q)
85+
}
86+
87+
// Batch implements the ds.Batching interface.
88+
func (t *Datastore) Batch(ctx context.Context) (ds.Batch, error) {
89+
ctx, span := t.tracer.Start(ctx, "Batch")
90+
defer span.End()
91+
92+
if batch, ok := t.ds.(ds.Batching); ok {
93+
return batch.Batch(ctx)
94+
}
95+
96+
return ds.NewBasicBatch(t), nil
97+
}
98+
99+
// DiskUsage implements the ds.PersistentDatastore interface.
100+
func (t *Datastore) DiskUsage(ctx context.Context) (uint64, error) {
101+
ctx, span := t.tracer.Start(ctx, "DiskUsage")
102+
defer span.End()
103+
return ds.DiskUsage(ctx, t.ds)
104+
}
105+
106+
// Scrub implements the ds.ScrubbedDatastore interface.
107+
func (t *Datastore) Scrub(ctx context.Context) error {
108+
ctx, span := t.tracer.Start(ctx, "Scrub")
109+
defer span.End()
110+
111+
if dstore, ok := t.tracer.(ds.ScrubbedDatastore); ok {
112+
return dstore.Scrub(ctx)
113+
}
114+
115+
return nil
116+
}
117+
118+
// CollectGarbage implements the ds.GCDatastore interface.
119+
func (t *Datastore) CollectGarbage(ctx context.Context) error {
120+
ctx, span := t.tracer.Start(ctx, "CollectGarbage")
121+
defer span.End()
122+
123+
if dstore, ok := t.tracer.(ds.GCDatastore); ok {
124+
return dstore.CollectGarbage(ctx)
125+
}
126+
127+
return nil
128+
}
129+
130+
// Check implements the ds.CheckedDatastore interface.
131+
func (t *Datastore) Check(ctx context.Context) error {
132+
ctx, span := t.tracer.Start(ctx, "Check")
133+
defer span.End()
134+
135+
if dstore, ok := t.tracer.(ds.CheckedDatastore); ok {
136+
return dstore.Check(ctx)
137+
}
138+
139+
return nil
140+
}
141+
142+
// NewTransaction implements the ds.TxnDatastore interface.
143+
func (t *Datastore) NewTransaction(ctx context.Context, readOnly bool) (ds.Txn, error) {
144+
ctx, span := t.tracer.Start(ctx, "NewTransaction", otel.WithAttributes(attribute.Bool("readOnly", readOnly)))
145+
defer span.End()
146+
147+
if txnDs, ok := t.ds.(ds.TxnDatastore); ok {
148+
txn, err := txnDs.NewTransaction(ctx, readOnly)
149+
if err != nil {
150+
return nil, err
151+
}
152+
return &Txn{txn: txn, tracer: t.tracer}, nil
153+
}
154+
155+
return nil, fmt.Errorf("transactions are unsupported by traced datastore")
156+
}
157+
158+
// Close closes the inner datastore (if it implements the io.Closer interface).
159+
func (t *Datastore) Close() error {
160+
if closer, ok := t.ds.(io.Closer); ok {
161+
return closer.Close()
162+
}
163+
return nil
164+
}
165+
166+
// Txn is an adapter that traces datastore transactions
167+
type Txn struct {
168+
txn ds.Txn
169+
tracer otel.Tracer
170+
}
171+
172+
var _ ds.Txn = (*Txn)(nil)
173+
174+
// Put implements the ds.Txn interface.
175+
func (t *Txn) Put(ctx context.Context, key ds.Key, value []byte) (err error) {
176+
ctx, span := t.tracer.Start(ctx, "Put", otel.WithAttributes(attribute.String("key", key.String())))
177+
defer span.End()
178+
return t.txn.Put(ctx, key, value)
179+
}
180+
181+
// Get implements the ds.Txn interface.
182+
func (t *Txn) Get(ctx context.Context, key ds.Key) (value []byte, err error) {
183+
ctx, span := t.tracer.Start(ctx, "Get", otel.WithAttributes(attribute.String("key", key.String())))
184+
defer span.End()
185+
return t.txn.Get(ctx, key)
186+
}
187+
188+
// Has implements the ds.Txn interface.
189+
func (t *Txn) Has(ctx context.Context, key ds.Key) (exists bool, err error) {
190+
ctx, span := t.tracer.Start(ctx, "Has", otel.WithAttributes(attribute.String("key", key.String())))
191+
defer span.End()
192+
return t.txn.Has(ctx, key)
193+
}
194+
195+
// GetSize implements the ds.Txn interface.
196+
func (t *Txn) GetSize(ctx context.Context, key ds.Key) (size int, err error) {
197+
ctx, span := t.tracer.Start(ctx, "GetSize", otel.WithAttributes(attribute.String("key", key.String())))
198+
defer span.End()
199+
return t.txn.GetSize(ctx, key)
200+
}
201+
202+
// Delete implements the ds.Txn interface.
203+
func (t *Txn) Delete(ctx context.Context, key ds.Key) (err error) {
204+
ctx, span := t.tracer.Start(ctx, "Delete", otel.WithAttributes(attribute.String("key", key.String())))
205+
defer span.End()
206+
return t.txn.Delete(ctx, key)
207+
}
208+
209+
// Query implements the ds.Txn interface.
210+
func (t *Txn) Query(ctx context.Context, q dsq.Query) (dsq.Results, error) {
211+
ctx, span := t.tracer.Start(ctx, "Query", otel.WithAttributes(attribute.String("query", q.String())))
212+
defer span.End()
213+
return t.txn.Query(ctx, q)
214+
}
215+
216+
// Commit implements the ds.Txn interface.
217+
func (t *Txn) Commit(ctx context.Context) error {
218+
ctx, span := t.tracer.Start(ctx, "Commit")
219+
defer span.End()
220+
return t.txn.Commit(ctx)
221+
}
222+
223+
// Discard implements the ds.Txn interface.
224+
func (t *Txn) Discard(ctx context.Context) {
225+
ctx, span := t.tracer.Start(ctx, "Discard")
226+
defer span.End()
227+
t.txn.Discard(ctx)
228+
}

Diff for: trace/trace_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package trace
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ipfs/go-datastore"
7+
dstest "github.com/ipfs/go-datastore/test"
8+
"go.opentelemetry.io/otel"
9+
)
10+
11+
func TestTraceAll(t *testing.T) {
12+
tracer := otel.Tracer("tracer")
13+
dstest.SubtestAll(t, New(datastore.NewMapDatastore(), tracer))
14+
}

0 commit comments

Comments
 (0)