diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 915560d58..c749ae017 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -102,6 +102,14 @@ updates: directory: "/instrumentation/github.com/syndtr/goleveldb/leveldb/splunkleveldb/test" schedule: interval: "daily" + - package-ecosystem: "gomod" + directory: "/instrumentation/github.com/tidwall/buntdb/splunkbuntdb" + schedule: + interval: "daily" + - package-ecosystem: "gomod" + directory: "/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test" + schedule: + interval: "daily" - package-ecosystem: "gomod" directory: "/instrumentation/k8s.io/client-go/splunkclient-go" schedule: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2112c525c..ab66b9b69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm `github.com/signalfx/splunk-otel-go/instrumentation/github.com/syndtr/goleveldb/leveldb/splunkleveldb` instrumentation for the `github.com/syndtr/goleveldb/leveldb` package. (#186) +- Add the + `github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb` + instrumentation for the `github.com/tidwall/buntdb` + package. (#228) - Add the `github.com/signalfx/splunk-otel-go/instrumentation/k8s.io/client-go/splunkclient-go` instrumentation for the `k8s.io/client-go` package. (#224) diff --git a/README.md b/README.md index e40f79431..598694e05 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Supported libraries are listed Additional recommended Splunk specific instrumentations: +- [`splunkbuntdb`](./instrumentation/github.com/tidwall/buntdb/splunkbuntdb) - [`splunkchi`](./instrumentation/github.com/go-chi/chi/splunkchi) - [`splunkclient-go`](./instrumentation/k8s.io/client-go/splunkclient-go) - [`splunkdns`](./instrumentation/github.com/miekg/dns/splunkdns) diff --git a/build/pipeline.go b/build/pipeline.go index 9274c45c0..ea538e2ab 100644 --- a/build/pipeline.go +++ b/build/pipeline.go @@ -205,7 +205,7 @@ func taskGolangciLint() goyek.Task { } ForGoModules(tf, func(tf *goyek.TF) { - lint := tf.Cmd("golangci-lint", "run", "--timeout", "2m0s") + lint := tf.Cmd("golangci-lint", "run", "--timeout", "4m0s") if err := lint.Run(); err != nil { tf.Error(err) } diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/README.md b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/README.md new file mode 100644 index 000000000..5335517fc --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/README.md @@ -0,0 +1,11 @@ +# Splunk instrumentation for `github.com/tidwall/buntdb` + +This package provides OpenTelemetry instrumentation for the +[github.com/tidwall/buntdb](https://github.com/tidwall/buntdb) +package. + +## Getting Started + +This package is designed to be used as a drop-in replacement for the use of the +`github.com/tidwall/buntdb` package. See +[example_test.go](./example_test.go) for more information. diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/buntdb.go b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/buntdb.go new file mode 100644 index 000000000..35e779cb2 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/buntdb.go @@ -0,0 +1,329 @@ +// Copyright Splunk Inc. +// +// 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 splunkbuntdb provides instrumentation for the github.com/tidwall/buntdb +// package. +package splunkbuntdb + +import ( + "context" + "time" + + "github.com/tidwall/buntdb" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "go.opentelemetry.io/otel/trace" +) + +// A DB wraps a buntdb.DB, automatically tracing any transactions. +type DB struct { + *buntdb.DB + cfg *config +} + +// Open calls buntdb.Open and wraps the result. +func Open(path string, opts ...Option) (*DB, error) { + db, err := buntdb.Open(path) + if err != nil { + return nil, err + } + return WrapDB(db, opts...), nil +} + +// WrapDB wraps a buntdb.DB so it can be traced. +func WrapDB(db *buntdb.DB, opts ...Option) *DB { + return &DB{ + DB: db, + cfg: newConfig(opts...), + } +} + +// Begin calls the underlying DB.Begin and traces the transaction. +func (db *DB) Begin(writable bool) (*Tx, error) { + tx, err := db.DB.Begin(writable) + if err != nil { + return nil, err + } + return WrapTx(tx, optionFunc(func(c *config) { + newCopy := db.cfg.copy() + *c = *newCopy + })), nil +} + +// Update calls the underlying DB.Update and traces the transaction. +func (db *DB) Update(fn func(tx *Tx) error) error { + return db.DB.Update(func(tx *buntdb.Tx) error { + return fn(WrapTx(tx, optionFunc(func(c *config) { + newCopy := db.cfg.copy() + *c = *newCopy + }))) + }) +} + +// View calls the underlying DB.View and traces the transaction. +func (db *DB) View(fn func(tx *Tx) error) error { + return db.DB.View(func(tx *buntdb.Tx) error { + return fn(WrapTx(tx, optionFunc(func(c *config) { + newCopy := db.cfg.copy() + *c = *newCopy + }))) + }) +} + +// WithContext sets the context for the DB. +func (db *DB) WithContext(ctx context.Context) *DB { + newdb := WrapDB(db.DB, optionFunc(func(c *config) { + newCopy := db.cfg.copy() + newCopy.ctx = ctx + *c = *newCopy + })) + return newdb +} + +// A Tx wraps a buntdb.Tx, automatically tracing any queries. +type Tx struct { + *buntdb.Tx + cfg *config +} + +// WrapTx wraps a buntdb.Tx so it can be traced. +func WrapTx(tx *buntdb.Tx, opts ...Option) *Tx { + return &Tx{ + Tx: tx, + cfg: newConfig(opts...), + } +} + +// WithContext sets the context for the Tx. +func (tx *Tx) WithContext(ctx context.Context) *Tx { + newCfg := tx.cfg.copy() + newCfg.ctx = ctx + return &Tx{ + Tx: tx.Tx, + cfg: newCfg, + } +} + +// Ascend calls the underlying Tx.Ascend and traces the query. +func (tx *Tx) Ascend(index string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("Ascend", func() error { + return tx.Tx.Ascend(index, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("Ascend"))) +} + +// AscendEqual calls the underlying Tx.AscendEqual and traces the query. +func (tx *Tx) AscendEqual(index, pivot string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("AscendEqual", func() error { + return tx.Tx.AscendEqual(index, pivot, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("AscendEqual"))) +} + +// AscendGreaterOrEqual calls the underlying Tx.AscendGreaterOrEqual and traces the query. +func (tx *Tx) AscendGreaterOrEqual(index, pivot string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("AscendGreaterOrEqual", func() error { + return tx.Tx.AscendGreaterOrEqual(index, pivot, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("AscendGreaterOrEqual"))) +} + +// AscendKeys calls the underlying Tx.AscendKeys and traces the query. +func (tx *Tx) AscendKeys(pattern string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("AscendKeys", func() error { + return tx.Tx.AscendKeys(pattern, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("AscendKeys"))) +} + +// AscendLessThan calls the underlying Tx.AscendLessThan and traces the query. +func (tx *Tx) AscendLessThan(index, pivot string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("AscendLessThan", func() error { + return tx.Tx.AscendLessThan(index, pivot, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("AscendLessThan"))) +} + +// AscendRange calls the underlying Tx.AscendRange and traces the query. +func (tx *Tx) AscendRange(index, greaterOrEqual, lessThan string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("AscendRange", func() error { + return tx.Tx.AscendRange(index, greaterOrEqual, lessThan, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("AscendRange"))) +} + +// CreateIndex calls the underlying Tx.CreateIndex and traces the query. +func (tx *Tx) CreateIndex(name, pattern string, less ...func(a, b string) bool) error { + return tx.cfg.withSpan("CreateIndex", func() error { + return tx.Tx.CreateIndex(name, pattern, less...) + }, trace.WithAttributes(semconv.DBOperationKey.String("CreateIndex"))) +} + +// CreateIndexOptions calls the underlying Tx.CreateIndexOptions and traces the query. +func (tx *Tx) CreateIndexOptions(name, pattern string, opts *buntdb.IndexOptions, less ...func(a, b string) bool) error { + return tx.cfg.withSpan("CreateIndexOptions", func() error { + return tx.Tx.CreateIndexOptions(name, pattern, opts, less...) + }, trace.WithAttributes(semconv.DBOperationKey.String("CreateIndexOptions"))) +} + +// CreateSpatialIndex calls the underlying Tx.CreateSpatialIndex and traces the query. +func (tx *Tx) CreateSpatialIndex(name, pattern string, rect func(item string) (min, max []float64)) error { + return tx.cfg.withSpan("CreateSpatialIndex", func() error { + return tx.Tx.CreateSpatialIndex(name, pattern, rect) + }, trace.WithAttributes(semconv.DBOperationKey.String("CreateSpatialIndex"))) +} + +// CreateSpatialIndexOptions calls the underlying Tx.CreateSpatialIndexOptions and traces the query. +func (tx *Tx) CreateSpatialIndexOptions(name, pattern string, opts *buntdb.IndexOptions, rect func(item string) (min, max []float64)) error { + return tx.cfg.withSpan("CreateSpatialIndexOptions", func() error { + return tx.Tx.CreateSpatialIndexOptions(name, pattern, opts, rect) + }, trace.WithAttributes(semconv.DBOperationKey.String("CreateSpatialIndexOptions"))) +} + +// Delete calls the underlying Tx.Delete and traces the query. +func (tx *Tx) Delete(key string) (val string, err error) { + err = tx.cfg.withSpan("Delete", func() error { + var iErr error + val, iErr = tx.Tx.Delete(key) + return iErr + }, trace.WithAttributes(semconv.DBOperationKey.String("Delete"))) + return +} + +// DeleteAll calls the underlying Tx.DeleteAll and traces the query. +func (tx *Tx) DeleteAll() error { + return tx.cfg.withSpan("DeleteAll", func() error { + return tx.Tx.DeleteAll() + }, trace.WithAttributes(semconv.DBOperationKey.String("DeleteAll"))) +} + +// Descend calls the underlying Tx.Descend and traces the query. +func (tx *Tx) Descend(index string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("Descend", func() error { + return tx.Tx.Descend(index, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("Descend"))) +} + +// DescendEqual calls the underlying Tx.DescendEqual and traces the query. +func (tx *Tx) DescendEqual(index, pivot string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("DescendEqual", func() error { + return tx.Tx.DescendEqual(index, pivot, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("DescendEqual"))) +} + +// DescendGreaterThan calls the underlying Tx.DescendGreaterThan and traces the query. +func (tx *Tx) DescendGreaterThan(index, pivot string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("DescendGreaterThan", func() error { + return tx.Tx.DescendGreaterThan(index, pivot, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("DescendGreaterThan"))) +} + +// DescendKeys calls the underlying Tx.DescendKeys and traces the query. +func (tx *Tx) DescendKeys(pattern string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("DescendKeys", func() error { + return tx.Tx.DescendKeys(pattern, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("DescendKeys"))) +} + +// DescendLessOrEqual calls the underlying Tx.DescendLessOrEqual and traces the query. +func (tx *Tx) DescendLessOrEqual(index, pivot string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("DescendLessOrEqual", func() error { + return tx.Tx.DescendLessOrEqual(index, pivot, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("DescendLessOrEqual"))) +} + +// DescendRange calls the underlying Tx.DescendRange and traces the query. +func (tx *Tx) DescendRange(index, lessOrEqual, greaterThan string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("DescendRange", func() error { + return tx.Tx.DescendRange(index, lessOrEqual, greaterThan, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("DescendRange"))) +} + +// DropIndex calls the underlying Tx.DropIndex and traces the query. +func (tx *Tx) DropIndex(name string) error { + return tx.cfg.withSpan("DropIndex", func() error { + return tx.Tx.DropIndex(name) + }, trace.WithAttributes(semconv.DBOperationKey.String("DropIndex"))) +} + +// Get calls the underlying Tx.Get and traces the query. +func (tx *Tx) Get(key string, ignoreExpired ...bool) (val string, err error) { + err = tx.cfg.withSpan("Get", func() error { + var iErr error + val, iErr = tx.Tx.Get(key, ignoreExpired...) + return iErr + }, trace.WithAttributes(semconv.DBOperationKey.String("Get"))) + return +} + +// Indexes calls the underlying Tx.Indexes and traces the query. +func (tx *Tx) Indexes() (indexes []string, err error) { + err = tx.cfg.withSpan("Indexes", func() error { + var iErr error + indexes, iErr = tx.Tx.Indexes() + return iErr + }, trace.WithAttributes(semconv.DBOperationKey.String("Indexes"))) + return +} + +// Intersects calls the underlying Tx.Intersects and traces the query. +func (tx *Tx) Intersects(index, bounds string, iterator func(key, value string) bool) error { + return tx.cfg.withSpan("Intersects", func() error { + return tx.Tx.Intersects(index, bounds, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("Intersects"))) +} + +// Len calls the underlying Tx.Len and traces the query. +func (tx *Tx) Len() (n int, err error) { + err = tx.cfg.withSpan("Len", func() error { + var iErr error + n, iErr = tx.Tx.Len() + return iErr + }, trace.WithAttributes(semconv.DBOperationKey.String("Len"))) + return +} + +// Nearby calls the underlying Tx.Nearby and traces the query. +func (tx *Tx) Nearby(index, bounds string, iterator func(key, value string, dist float64) bool) error { + return tx.cfg.withSpan("Nearby", func() error { + return tx.Tx.Nearby(index, bounds, iterator) + }, trace.WithAttributes(semconv.DBOperationKey.String("Nearby"))) +} + +// Set calls the underlying Tx.Set and traces the query. +func (tx *Tx) Set(key, value string, opts *buntdb.SetOptions) (previousValue string, replaced bool, err error) { + err = tx.cfg.withSpan("Set", func() error { + var iErr error + previousValue, replaced, iErr = tx.Tx.Set(key, value, opts) + return iErr + }, trace.WithAttributes(semconv.DBOperationKey.String("Set"))) + return +} + +// TTL calls the underlying Tx.TTL and traces the query. +func (tx *Tx) TTL(key string) (duration time.Duration, err error) { + err = tx.cfg.withSpan("TTL", func() error { + var iErr error + duration, iErr = tx.Tx.TTL(key) + return iErr + }, trace.WithAttributes(semconv.DBOperationKey.String("TTL"))) + return +} + +// Commit calls the underlying Tx.Commit and traces the query. +func (tx *Tx) Commit() error { + return tx.cfg.withSpan("Commit", func() error { + return tx.Tx.Commit() + }, trace.WithAttributes(semconv.DBOperationKey.String("Commit"))) +} + +// Rollback calls the underlying Tx.Rollback and traces the query. +func (tx *Tx) Rollback() error { + return tx.cfg.withSpan("Rollback", func() error { + return tx.Tx.Rollback() + }, trace.WithAttributes(semconv.DBOperationKey.String("Rollback"))) +} diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/buntdb_test.go b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/buntdb_test.go new file mode 100644 index 000000000..a8010b919 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/buntdb_test.go @@ -0,0 +1,54 @@ +// Copyright Splunk Inc. +// +// 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 splunkbuntdb + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOpenError(t *testing.T) { + _, err := Open("/non/existing/file.db") + assert.Error(t, err) +} + +func TestOpenReturnType(t *testing.T) { + db, err := Open(":memory:") + require.NoError(t, err) + assert.IsType(t, &DB{}, db) +} + +type key string + +func TestDBWithContext(t *testing.T) { + db, err := Open(":memory:") + require.NoError(t, err) + ctx := context.WithValue(context.Background(), key("key"), "val") + db = db.WithContext(ctx) + assert.Equal(t, ctx, db.cfg.ctx) +} + +func TestTxWithContext(t *testing.T) { + db, err := Open(":memory:") + require.NoError(t, err) + ctx := context.WithValue(context.Background(), key("key"), "val") + tx, err := db.Begin(true) + require.NoError(t, err) + contextedTx := tx.WithContext(ctx) + assert.Equal(t, ctx, contextedTx.cfg.ctx) +} diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/config.go b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/config.go new file mode 100644 index 000000000..c9bd60f12 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/config.go @@ -0,0 +1,168 @@ +// Copyright Splunk Inc. +// +// 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 splunkbuntdb + +import ( + "context" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "go.opentelemetry.io/otel/trace" + + splunkotel "github.com/signalfx/splunk-otel-go" +) + +// instrumentationName is the instrumentation library identifier for a Tracer. +const instrumentationName = "github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb" + +type config struct { + ctx context.Context + tracer trace.Tracer + defaultStartOpts []trace.SpanStartOption +} + +func newConfig(options ...Option) *config { + c := config{ + defaultStartOpts: []trace.SpanStartOption{ + trace.WithSpanKind(trace.SpanKindClient), + trace.WithAttributes(semconv.DBSystemKey.String("buntdb")), + }, + } + + for _, o := range options { + if o != nil { + o.apply(&c) + } + } + + if c.tracer == nil { + c.tracer = otel.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(splunkotel.Version()), + trace.WithSchemaURL(semconv.SchemaURL), + ) + } + + if c.ctx == nil { + c.ctx = context.Background() + } + + return &c +} + +// copy returns a deep copy of c. +func (c *config) copy() *config { + copyOpts := make([]trace.SpanStartOption, len(c.defaultStartOpts)) + copy(copyOpts, c.defaultStartOpts) + + newCfg := &config{ + ctx: c.ctx, + tracer: c.tracer, + defaultStartOpts: copyOpts, + } + return newCfg +} + +// resolveTracer returns an OTel tracer from the appropriate TracerProvider. +// +// If the passed context contains a span, the TracerProvider that created the +// tracer that created that span will be used. Otherwise, the TracerProvider +// from c is used. +func (c *config) resolveTracer(ctx context.Context) trace.Tracer { + if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() { + return span.TracerProvider().Tracer( + instrumentationName, + trace.WithInstrumentationVersion(splunkotel.Version()), + trace.WithSchemaURL(semconv.SchemaURL), + ) + } + // There is a possibility that the config was not created with newConfig + // (i.e. new(Client)), try to handle this situation gracefully. + if c == nil || c.tracer == nil { + return otel.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(splunkotel.Version()), + trace.WithSchemaURL(semconv.SchemaURL), + ) + } + return c.tracer +} + +// withSpan wraps the function f with a span. +func (c *config) withSpan(spanName string, f func() error, opts ...trace.SpanStartOption) error { + var o []trace.SpanStartOption + if c == nil || len(c.defaultStartOpts) == 0 { + o = make([]trace.SpanStartOption, len(opts)) + copy(o, opts) + } else { + o = make([]trace.SpanStartOption, len(c.defaultStartOpts)+len(opts)) + copy(o, c.defaultStartOpts) + copy(o[len(c.defaultStartOpts):], opts) + } + + _, span := c.resolveTracer(c.ctx).Start(c.ctx, spanName, o...) + + err := f() + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + } + span.End() + + return err +} + +// Option applies options to a configuration. +type Option interface { + apply(*config) +} + +type optionFunc func(*config) + +func (o optionFunc) apply(c *config) { + o(c) +} + +// WithTracerProvider returns an Option that sets the TracerProvider used with +// this instrumentation library. +func WithTracerProvider(tp trace.TracerProvider) Option { + return optionFunc(func(c *config) { + c.tracer = tp.Tracer( + instrumentationName, + trace.WithInstrumentationVersion(splunkotel.Version()), + trace.WithSchemaURL(semconv.SchemaURL), + ) + }) +} + +// WithAttributes returns an Option that appends attr to the attributes set +// for every span created with this instrumentation library. +func WithAttributes(attr []attribute.KeyValue) Option { + return optionFunc(func(c *config) { + c.defaultStartOpts = append( + c.defaultStartOpts, + trace.WithAttributes(attr...), + ) + }) +} + +// WithContext sets the parent context for spans. +func WithContext(ctx context.Context) Option { + return optionFunc(func(c *config) { + c.ctx = ctx + }) +} diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/example_test.go b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/example_test.go new file mode 100644 index 000000000..af9981212 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/example_test.go @@ -0,0 +1,49 @@ +// Copyright Splunk Inc. +// +// 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 splunkbuntdb_test + +import ( + "fmt" + + "github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb" +) + +func Example() { + // Open the data.db file. It will be created if it doesn't exist. + db, err := splunkbuntdb.Open(":memory:") + if err != nil { + panic(err) + } + defer db.Close() + + err = db.Update(func(tx *splunkbuntdb.Tx) error { + _, _, errIn := tx.Set("mykey", "myvalue", nil) + return errIn + }) + if err != nil { + panic(err) + } + + err = db.View(func(tx *splunkbuntdb.Tx) error { + errIn := tx.Ascend("", func(key, value string) bool { + fmt.Printf("key: %s, value: %s\n", key, value) + return true + }) + return errIn + }) + if err != nil { + panic(err) + } +} diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/go.mod b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/go.mod new file mode 100644 index 000000000..31d77f1a6 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/go.mod @@ -0,0 +1,13 @@ +module github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb + +go 1.16 + +require ( + github.com/signalfx/splunk-otel-go v0.6.0 + github.com/stretchr/testify v1.7.0 + github.com/tidwall/buntdb v1.2.7 + go.opentelemetry.io/otel v1.2.0 + go.opentelemetry.io/otel/trace v1.2.0 +) + +replace github.com/signalfx/splunk-otel-go => ../../../../../ diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/go.sum b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/go.sum new file mode 100644 index 000000000..468a4d556 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/go.sum @@ -0,0 +1,42 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= +github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= +github.com/tidwall/btree v0.6.1 h1:75VVgBeviiDO+3g4U+7+BaNBNhNINxB0ULPT3fs9pMY= +github.com/tidwall/btree v0.6.1/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/buntdb v1.2.7 h1:SIyObKAymzLyGhDeIhVk2Yc1/EwfCC75Uyu77CHlVoA= +github.com/tidwall/buntdb v1.2.7/go.mod h1:b6KvZM27x/8JLI5hgRhRu60pa3q0Tz9c50TyD46OHUM= +github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/grect v0.1.3 h1:z9YwQAMUxVSBde3b7Sl8Da37rffgNfZ6Fq6h9t6KdXE= +github.com/tidwall/grect v0.1.3/go.mod h1:8GMjwh3gPZVpLBI/jDz9uslCe0dpxRpWDdtN0lWAS/E= +github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= +github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= +github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= +github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= +github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= +go.opentelemetry.io/contrib/propagators/b3 v1.2.0/go.mod h1:kO8hNKCfa1YmQJ0lM7pzfJGvbXEipn/S7afbOfaw2Kc= +go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ= +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= +go.opentelemetry.io/otel/exporters/jaeger v1.2.0/go.mod h1:KJLFbEMKTNPIfOxcg/WikIozEoKcPgJRz3Ce1vLlM8E= +go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= +go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0= +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/buntdb_test.go b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/buntdb_test.go new file mode 100644 index 000000000..c1c4c050f --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/buntdb_test.go @@ -0,0 +1,527 @@ +// Copyright Splunk Inc. +// +// 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 test + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/tidwall/buntdb" + + "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + traceapi "go.opentelemetry.io/otel/trace" + + "github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb" +) + +func TestAscend(t *testing.T) { + testView(t, "Ascend", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.Ascend("test-index", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:a", "1", + "regular:b", "2", + "regular:c", "3", + "regular:d", "4", + "regular:e", "5", + }, arr) + return nil + }) +} + +func TestAscendEqual(t *testing.T) { + testView(t, "AscendEqual", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.AscendEqual("test-index", "3", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{"regular:c", "3"}, arr) + return nil + }) +} + +func TestAscendGreaterOrEqual(t *testing.T) { + testView(t, "AscendGreaterOrEqual", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.AscendGreaterOrEqual("test-index", "3", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:c", "3", + "regular:d", "4", + "regular:e", "5", + }, arr) + return nil + }) +} + +func TestAscendKeys(t *testing.T) { + testView(t, "AscendKeys", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.AscendKeys("regular:*", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:a", "1", + "regular:b", "2", + "regular:c", "3", + "regular:d", "4", + "regular:e", "5", + }, arr) + return nil + }) +} + +func TestAscendLessThan(t *testing.T) { + testView(t, "AscendLessThan", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.AscendLessThan("test-index", "3", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:a", "1", + "regular:b", "2", + }, arr) + return nil + }) +} + +func TestAscendRange(t *testing.T) { + testView(t, "AscendRange", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.AscendRange("test-index", "2", "4", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:b", "2", + "regular:c", "3", + }, arr) + return nil + }) +} + +func TestCreateIndex(t *testing.T) { + testUpdate(t, "CreateIndex", func(tx *splunkbuntdb.Tx) error { + err := tx.CreateIndex("test-create-index", "*") + assert.NoError(t, err) + return nil + }) +} + +func TestCreateIndexOptions(t *testing.T) { + testUpdate(t, "CreateIndexOptions", func(tx *splunkbuntdb.Tx) error { + err := tx.CreateIndexOptions("test-create-index", "*", nil) + assert.NoError(t, err) + return nil + }) +} + +func TestCreateSpatialIndex(t *testing.T) { + testUpdate(t, "CreateSpatialIndex", func(tx *splunkbuntdb.Tx) error { + err := tx.CreateSpatialIndex("test-create-index", "*", buntdb.IndexRect) + assert.NoError(t, err) + return nil + }) +} + +func TestCreateSpatialIndexOptions(t *testing.T) { + testUpdate(t, "CreateSpatialIndexOptions", func(tx *splunkbuntdb.Tx) error { + err := tx.CreateSpatialIndexOptions("test-create-index", "*", nil, buntdb.IndexRect) + assert.NoError(t, err) + return nil + }) +} + +func TestDelete(t *testing.T) { + testUpdate(t, "Delete", func(tx *splunkbuntdb.Tx) error { + val, err := tx.Delete("regular:a") + assert.NoError(t, err) + assert.Equal(t, "1", val) + return nil + }) +} + +func TestDeleteAll(t *testing.T) { + testUpdate(t, "DeleteAll", func(tx *splunkbuntdb.Tx) error { + err := tx.DeleteAll() + assert.NoError(t, err) + return nil + }) +} + +func TestDescend(t *testing.T) { + testView(t, "Descend", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.Descend("test-index", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:e", "5", + "regular:d", "4", + "regular:c", "3", + "regular:b", "2", + "regular:a", "1", + }, arr) + return nil + }) +} + +func TestDescendEqual(t *testing.T) { + testView(t, "DescendEqual", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.DescendEqual("test-index", "3", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{"regular:c", "3"}, arr) + return nil + }) +} + +func TestDescendGreaterThan(t *testing.T) { + testView(t, "DescendGreaterThan", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.DescendGreaterThan("test-index", "3", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:e", "5", + "regular:d", "4", + }, arr) + return nil + }) +} + +func TestDescendKeys(t *testing.T) { + testView(t, "DescendKeys", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.DescendKeys("regular:*", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:e", "5", + "regular:d", "4", + "regular:c", "3", + "regular:b", "2", + "regular:a", "1", + }, arr) + return nil + }) +} + +func TestDescendLessOrEqual(t *testing.T) { + testView(t, "DescendLessOrEqual", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.DescendLessOrEqual("test-index", "3", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:c", "3", + "regular:b", "2", + "regular:a", "1", + }, arr) + return nil + }) +} + +func TestDescendRange(t *testing.T) { + testView(t, "DescendRange", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.DescendRange("test-index", "4", "2", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "regular:d", "4", + "regular:c", "3", + }, arr) + return nil + }) +} + +func TestDropIndex(t *testing.T) { + testUpdate(t, "DropIndex", func(tx *splunkbuntdb.Tx) error { + err := tx.DropIndex("test-index") + assert.NoError(t, err) + return nil + }) +} + +func TestGet(t *testing.T) { + testView(t, "Get", func(tx *splunkbuntdb.Tx) error { + val, err := tx.Get("regular:a") + assert.NoError(t, err) + assert.Equal(t, "1", val) + return nil + }) +} + +func TestIndexes(t *testing.T) { + testView(t, "Indexes", func(tx *splunkbuntdb.Tx) error { + indexes, err := tx.Indexes() + assert.NoError(t, err) + assert.Equal(t, []string{"test-index", "test-spatial-index"}, indexes) + return nil + }) +} + +func TestIntersects(t *testing.T) { + testView(t, "Intersects", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.Intersects("test-spatial-index", "[3 3],[4 4]", func(key, value string) bool { + arr = append(arr, key, value) + return true + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "spatial:c", "[3 3]", + "spatial:d", "[4 4]", + }, arr) + return nil + }) +} + +func TestLen(t *testing.T) { + testView(t, "Len", func(tx *splunkbuntdb.Tx) error { + n, err := tx.Len() + assert.NoError(t, err) + assert.Equal(t, 10, n) + return nil + }) +} + +func TestNearby(t *testing.T) { + testView(t, "Nearby", func(tx *splunkbuntdb.Tx) error { + var arr []string + err := tx.Nearby("test-spatial-index", "[3 3]", func(key, value string, distance float64) bool { + arr = append(arr, key, value) + return false + }) + assert.NoError(t, err) + assert.Equal(t, []string{ + "spatial:c", "[3 3]", + }, arr) + return nil + }) +} + +func TestSet(t *testing.T) { + testUpdate(t, "Set", func(tx *splunkbuntdb.Tx) error { + previousValue, replaced, err := tx.Set("regular:a", "11", nil) + assert.NoError(t, err) + assert.True(t, replaced) + assert.Equal(t, "1", previousValue) + return nil + }) +} + +func TestTTL(t *testing.T) { + testUpdate(t, "TTL", func(tx *splunkbuntdb.Tx) error { + duration, err := tx.TTL("regular:a") + assert.NoError(t, err) + assert.Equal(t, time.Duration(-1), duration) + return nil + }) +} + +func withTestingDeadline(ctx context.Context, t *testing.T) context.Context { + d, ok := t.Deadline() + if !ok { + d = time.Now().Add(10 * time.Second) + } else { + d = d.Add(-time.Millisecond) + } + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, d) + t.Cleanup(cancel) + return ctx +} + +func assertSpan(t *testing.T, name string, span trace.ReadOnlySpan) { + assert.Equal(t, span.SpanKind(), traceapi.SpanKindClient) + assert.Contains(t, span.Attributes(), semconv.DBSystemKey.String("buntdb")) + assert.Contains(t, span.Attributes(), semconv.DBOperationKey.String(name)) + assert.Equal(t, span.Name(), name) +} + +func testUpdate(t *testing.T, name string, f func(tx *splunkbuntdb.Tx) error) { + sr := tracetest.NewSpanRecorder() + tp := trace.NewTracerProvider(trace.WithSpanProcessor(sr)) + + db := getDatabase(t, splunkbuntdb.WithTracerProvider(tp)) + t.Cleanup(func() { require.NoError(t, db.Close()) }) + + ctx := withTestingDeadline(context.Background(), t) + + err := db.WithContext(ctx).Update(f) + assert.NoError(t, err) + + require.NoError(t, tp.Shutdown(ctx)) + spans := sr.Ended() + require.Len(t, spans, 1) + + assertSpan(t, name, spans[0]) +} + +func testView(t *testing.T, name string, f func(tx *splunkbuntdb.Tx) error) { + sr := tracetest.NewSpanRecorder() + tp := trace.NewTracerProvider(trace.WithSpanProcessor(sr)) + + db := getDatabase(t, splunkbuntdb.WithTracerProvider(tp)) + t.Cleanup(func() { require.NoError(t, db.Close()) }) + + ctx := withTestingDeadline(context.Background(), t) + + err := db.WithContext(ctx).View(f) + assert.NoError(t, err) + + require.NoError(t, tp.Shutdown(ctx)) + spans := sr.Ended() + require.Len(t, spans, 1) + + assertSpan(t, name, spans[0]) +} + +func TestCommit(t *testing.T) { + sr := tracetest.NewSpanRecorder() + tp := trace.NewTracerProvider(trace.WithSpanProcessor(sr)) + + db := getDatabase(t, splunkbuntdb.WithTracerProvider(tp)) + t.Cleanup(func() { require.NoError(t, db.Close()) }) + + tx, err := db.Begin(true) + assert.NoError(t, err) + + previousValue, replaced, err := tx.Set("regular:a", "7", nil) + assert.NoError(t, err) + assert.True(t, replaced) + assert.Equal(t, "1", previousValue) + + err = tx.Commit() + assert.NoError(t, err) + + err = db.View(func(tx *splunkbuntdb.Tx) error { + val, errIn := tx.Get("regular:a") + assert.NoError(t, errIn) + assert.Equal(t, "7", val) + return nil + }) + assert.NoError(t, err) + + ctx := withTestingDeadline(context.Background(), t) + require.NoError(t, tp.Shutdown(ctx)) + spans := sr.Ended() + require.Len(t, spans, 3) + + assertSpan(t, "Set", spans[0]) + assertSpan(t, "Commit", spans[1]) + assertSpan(t, "Get", spans[2]) +} + +func TestRollback(t *testing.T) { + sr := tracetest.NewSpanRecorder() + tp := trace.NewTracerProvider(trace.WithSpanProcessor(sr)) + + db := getDatabase(t, splunkbuntdb.WithTracerProvider(tp)) + t.Cleanup(func() { require.NoError(t, db.Close()) }) + + tx, err := db.Begin(true) + assert.NoError(t, err) + + previousValue, replaced, err := tx.Set("regular:a", "11", nil) + assert.NoError(t, err) + assert.True(t, replaced) + assert.Equal(t, "1", previousValue) + + err = tx.Rollback() + assert.NoError(t, err) + + err = db.View(func(tx *splunkbuntdb.Tx) error { + val, errIn := tx.Get("regular:a") + assert.NoError(t, errIn) + assert.Equal(t, "1", val) + return nil + }) + assert.NoError(t, err) + + ctx := withTestingDeadline(context.Background(), t) + require.NoError(t, tp.Shutdown(ctx)) + spans := sr.Ended() + require.Len(t, spans, 3) + + assertSpan(t, "Set", spans[0]) + assertSpan(t, "Rollback", spans[1]) + assertSpan(t, "Get", spans[2]) +} + +func getDatabase(t *testing.T, opts ...splunkbuntdb.Option) *splunkbuntdb.DB { + bdb, err := buntdb.Open(":memory:") + require.NoError(t, err) + + err = bdb.CreateIndex("test-index", "regular:*", buntdb.IndexBinary) + require.NoError(t, err) + + err = bdb.CreateSpatialIndex("test-spatial-index", "spatial:*", buntdb.IndexRect) + require.NoError(t, err) + + require.NoError(t, bdb.Update(func(tx *buntdb.Tx) error { + _, _, _ = tx.Set("regular:a", "1", nil) + _, _, _ = tx.Set("regular:b", "2", nil) + _, _, _ = tx.Set("regular:c", "3", nil) + _, _, _ = tx.Set("regular:d", "4", nil) + _, _, _ = tx.Set("regular:e", "5", nil) + + _, _, _ = tx.Set("spatial:a", "[1 1]", nil) + _, _, _ = tx.Set("spatial:b", "[2 2]", nil) + _, _, _ = tx.Set("spatial:c", "[3 3]", nil) + _, _, _ = tx.Set("spatial:d", "[4 4]", nil) + _, _, _ = tx.Set("spatial:e", "[5 5]", nil) + + return nil + })) + + return splunkbuntdb.WrapDB(bdb, opts...) +} diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/doc.go b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/doc.go new file mode 100644 index 000000000..4c063dec5 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/doc.go @@ -0,0 +1,19 @@ +// Copyright Splunk Inc. +// +// 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 test validates the splunkbuntdb instrumentation with the default SDK. +// This package is in a separate module from the instrumentation it tests to +// isolate the dependency of the default SDK and not impose this as a transitive +// dependency for users. +package test diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/go.mod b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/go.mod new file mode 100644 index 000000000..9541f9fd3 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/go.mod @@ -0,0 +1,17 @@ +module github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test + +go 1.16 + +require ( + github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb v0.0.0-00010101000000-000000000000 + github.com/stretchr/testify v1.7.0 + github.com/tidwall/buntdb v1.2.7 + go.opentelemetry.io/otel v1.2.0 + go.opentelemetry.io/otel/sdk v1.2.0 + go.opentelemetry.io/otel/trace v1.2.0 +) + +replace ( + github.com/signalfx/splunk-otel-go => ../../../../../../ + github.com/signalfx/splunk-otel-go/instrumentation/github.com/tidwall/buntdb/splunkbuntdb => ../ +) diff --git a/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/go.sum b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/go.sum new file mode 100644 index 000000000..89b992b26 --- /dev/null +++ b/instrumentation/github.com/tidwall/buntdb/splunkbuntdb/test/go.sum @@ -0,0 +1,44 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= +github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= +github.com/tidwall/btree v0.6.1 h1:75VVgBeviiDO+3g4U+7+BaNBNhNINxB0ULPT3fs9pMY= +github.com/tidwall/btree v0.6.1/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/buntdb v1.2.7 h1:SIyObKAymzLyGhDeIhVk2Yc1/EwfCC75Uyu77CHlVoA= +github.com/tidwall/buntdb v1.2.7/go.mod h1:b6KvZM27x/8JLI5hgRhRu60pa3q0Tz9c50TyD46OHUM= +github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/grect v0.1.3 h1:z9YwQAMUxVSBde3b7Sl8Da37rffgNfZ6Fq6h9t6KdXE= +github.com/tidwall/grect v0.1.3/go.mod h1:8GMjwh3gPZVpLBI/jDz9uslCe0dpxRpWDdtN0lWAS/E= +github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= +github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= +github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= +github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= +github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= +go.opentelemetry.io/contrib/propagators/b3 v1.2.0/go.mod h1:kO8hNKCfa1YmQJ0lM7pzfJGvbXEipn/S7afbOfaw2Kc= +go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ= +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= +go.opentelemetry.io/otel/exporters/jaeger v1.2.0/go.mod h1:KJLFbEMKTNPIfOxcg/WikIozEoKcPgJRz3Ce1vLlM8E= +go.opentelemetry.io/otel/sdk v1.2.0 h1:wKN260u4DesJYhyjxDa7LRFkuhH7ncEVKU37LWcyNIo= +go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= +go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0= +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=