instagocb

package module
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2024 License: MIT Imports: 10 Imported by: 0

README

Instana instrumentation of Couchbase SDK v2 (gocb) for Go

This module contains the code for instrumenting the Couchbase SDK, based on the gocb library for Go.

The following services are currently instrumented:

  1. Cluster
    1. Query
    2. SearchQuery
    3. AnalyticsQuery
  2. Bucket
  3. Bucket Manager
    1. Get/Create/Update/Drop/Flush Bucket
  4. Collection
    1. Bulk Operations
    2. Data operation (Insert, Upsert, Get etc)
    3. Sub-doc operations
      1. LookupIn & MutateIn
  5. Scope
    1. Query
    2. AnalyticsQuery
  6. Binary Collection
  7. Collection Manager
  8. Collection Data Structures
    1. List
    2. Map
    3. Queue
    4. Set

[!NOTE] While you can call methods such as QueryIndexManager or SearchIndexManager from the provided instagocb cluster interface, it's important to note that tracing support for these methods is currently not implemented. If you find the instrumentation of any unlisted features necessary, please feel free to raise an issue.

Installation

To add the module to your go.mod file run the following command in your project directory:

$ go get github.com/instana/go-sensor/instrumentation/instagocb

Usage

  • Instead of using gocb.Connect, use instagocb.Connect to connect to the Couchbase server.
    • The function definition seems identical, except you need to pass an extra argument of instana.TraceLogger to instagocb.Connect.
  • For each instrumented service, you will find an interface in instagocb. Use this interface instead of using the direct instances from gocb.
    • For example, instead of *gocb.Cluster, use instagocb.Cluster interface.
    • *gocb.Collection becomes instagocb.Collection.
    • This applies to all instrumented services.
  • If you use instagocb.Connect, the returned cluster will be able to provide all the instrumented functionalities. For example, if you use cluster.Buckets(), it will return an instrumented instagocb.BucketManager interface instead of *gocb.BucketManager.
  • Set the ParentSpan property of the options function argument using instagocb.GetParentSpanFromContext(ctx) if your Couchbase call is part of some HTTP request or something. Otherwise, the parent-child relationship of the spans won't be tracked (see the example for a full demo).
  • There is an Unwrap() method in all instagocb provided interfaces; it will return the underlying gocb instance. For example, cluster.Unwrap() will return an instance of *gocb.Cluster.

[!IMPORTANT] Use Unwrap() if you need the original instance other than the instrumented one. It is not advisable to use this directly, as Instana tracing will not be enabled if you directly utilize this instance.

Sample Usage


   var collector instana.TracerLogger
   collector = instana.InitCollector(&instana.Options{
   	Service:           "sample-app-couchbase",
   	EnableAutoProfile: true,
   }) 

   // connect to database
   // this will returns an instance of instagocb.Cluster, 
   // which is capable of enabling instana tracing for Couchbase calls.
   cluster, err := instagocb.Connect(collector, connectionString, gocb.ClusterOptions{
   	Authenticator: gocb.PasswordAuthenticator{
   		Username: username,
   		Password: password,
   	},
   })
   if err != nil {
   	// Handle error
   }

   bucket := cluster.Bucket(bucketName)
   err = bucket.WaitUntilReady(5*time.Second, nil)
   if err != nil {
   	// Handle error
   }

   collection := bucket.Scope("tenant_agent_00").Collection("users")

   type User struct {
   	Name      string   `json:"name"`
   	Email     string   `json:"email"`
   	Interests []string `json:"interests"`
   }

   // Create and store a Document
   _, err = col.Upsert("u:jade",
   	User{
   		Name:      "Jade",
   		Email:     "jade@test-email.com",
   		Interests: []string{"Swimming", "Rowing"},
   	}, &gocb.UpsertOptions{
           // If you are using couchbase call as part of some http request or something,
           // you need to set this parentSpan property using `instagocb.GetParentSpanFromContext` method,
           // Else the parent-child span relationship wont be tracked.
           // You can keep this as nil, otherwise.
           ParentSpan: instagocb.GetParentSpanFromContext(ctx)
       })
   if err != nil {
   	// Handle error
   }

Full example

Transactions

  • Get a full idea of using transactions in Couchbase using Go SDK from here.
  • Create a new transactions instance by calling cluster.Transactions(). Like all other instrumented features, this one also returns an instagocb provided interface (instagocb.Transactions) instead of the original one (*gocb.Transactions).
  • You can use the same transactions.Run() method to start transactions.
	// Starting transactions
	transactions := cluster.Transactions()
  • Commit, Rollback, and all other transaction-specific things are handled by gocb only. The advantage is that you will be getting Instana tracing support on top of transactions.
  • There is one more thing you need to do in the transaction callback function. In the first line, call the below method to create an instagocb instrumented interface of TransactionAttemptContext and use it for the rest of the function to do all the operations like Insert, Replace, Remove, Get, and Query.
	// Create new TransactionAttemptContext from instagocb
	tacNew := cluster.WrapTransactionAttemptContext(tac, instagocb.GetParentSpanFromContext(ctx))
  • The function signatures of the tacNew.Replace and tacNew.Remove have a small change from the original method; you need to pass the collection as an extra argument to these functions if you are using the instrumented TransactionAttemptContext.

[!IMPORTANT] If you need to use the scope or collection inside the transaction function, use the unwrapped one (scope.Unwrap()) instead of the instagocb interface.

Sample Usage

// Starting transactions
   transactions := cluster.Transactions()
   _, err = transactions.Run(func(tac *gocb.TransactionAttemptContext) error {

   	// Create new TransactionAttemptContext from instagocb
   	tacNew := cluster.WrapTransactionAttemptContext(tac, instagocb.GetParentSpanFromContext(ctx))

   	// Unwrapped collection is required to pass it to transaction operations
   	collectionUnwrapped := collection.Unwrap()

   	// Inserting a doc:
   	_, err := tacNew.Insert(collectionUnwrapped, "doc-a", map[string]interface{}{})
   	if err != nil {
   		return err
   	}

   	// Getting documents:
   	docA, err := tacNew.Get(collectionUnwrapped, "doc-a")
   	// Use err != nil && !errors.Is(err, gocb.ErrDocumentNotFound) if the document may or may not exist
   	if err != nil {
   		return err
   	}

   	// Replacing a doc:
   	var content map[string]interface{}
   	err = docA.Content(&content)
   	if err != nil {
   		return err
   	}
   	content["transactions"] = "are awesome"
   	_, err = tacNew.Replace(collectionUnwrapped, docA, content)
   	if err != nil {
   		return err
   	}

   	// Removing a doc:
   	docA1, err := tacNew.Get(collectionUnwrapped, "doc-a")
   	if err != nil {
   		return err
   	}
   	err = tacNew.Remove(collectionUnwrapped, docA1)
   	if err != nil {
   		return err
   	}

   	// Performing a SELECT N1QL query against a scope:
   	qr, err := tacNew.Query("SELECT * FROM hotel WHERE country = $1", &gocb.TransactionQueryOptions{
   		PositionalParameters: []interface{}{"United Kingdom"},

   		// Unwrapped scope is required here
   		Scope: inventoryScope.Unwrap(),
   	})
   	if err != nil {
   		return err
   	}

   	type hotel struct {
   		Name string `json:"name"`
   	}

   	var hotels []hotel
   	for qr.Next() {
   		var h hotel
   		err = qr.Row(&h)
   		if err != nil {
   			return err
   		}

   		hotels = append(hotels, h)
   	}

   	// Performing an UPDATE N1QL query on multiple documents, in the `inventory` scope:
   	_, err = tacNew.Query("UPDATE route SET airlineid = $1 WHERE airline = $2", &gocb.TransactionQueryOptions{
   		PositionalParameters: []interface{}{"airline_137", "AF"},
   		// Unwrapped scope is required here
   		Scope: inventoryScope.Unwrap(),
   	})
   	if err != nil {
   		return err
   	}

   	// There is no commit call, by not returning an error the transaction will automatically commit
   	return nil
   }, nil)
   var ambigErr gocb.TransactionCommitAmbiguousError
   if errors.As(err, &ambigErr) {
   	log.Println("Transaction possibly committed")

   	log.Printf("%+v", ambigErr)
   	return nil
   }
   var failedErr gocb.TransactionFailedError
   if errors.As(err, &failedErr) {
   	log.Println("Transaction did not reach commit point")

   	log.Printf("%+v", failedErr)
   	return nil
   }

   if err != nil {
   	return err
   }

Full example

Documentation

Overview

(c) Copyright IBM Corp. 2023 Package Instagocb provides instrumentation for the gocb package

Index

Constants

View Source
const Version = "1.5.0"

Version is the instrumentation module semantic version

Variables

This section is empty.

Functions

This section is empty.

Types

type BinaryCollection

type BinaryCollection interface {
	Append(id string, val []byte, opts *gocb.AppendOptions) (mutOut *gocb.MutationResult, errOut error)
	Prepend(id string, val []byte, opts *gocb.PrependOptions) (mutOut *gocb.MutationResult, errOut error)
	Increment(id string, opts *gocb.IncrementOptions) (countOut *gocb.CounterResult, errOut error)
	Decrement(id string, opts *gocb.DecrementOptions) (countOut *gocb.CounterResult, errOut error)

	Unwrap() *gocb.BinaryCollection
}

type Bucket

type Bucket interface {
	Name() string
	Scope(scopeName string) Scope
	DefaultScope() Scope
	Collection(collectionName string) Collection
	DefaultCollection() Collection
	ViewIndexes() *gocb.ViewIndexManager
	Collections() CollectionManager
	WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error

	ViewQuery(designDoc string, viewName string, opts *gocb.ViewOptions) (*gocb.ViewResult, error)

	Ping(opts *gocb.PingOptions) (*gocb.PingResult, error)

	Internal() *gocb.InternalBucket

	Unwrap() *gocb.Bucket
}

type BucketManager

type BucketManager interface {
	GetBucket(bucketName string, opts *gocb.GetBucketOptions) (*gocb.BucketSettings, error)
	GetAllBuckets(opts *gocb.GetAllBucketsOptions) (map[string]gocb.BucketSettings, error)
	CreateBucket(settings gocb.CreateBucketSettings, opts *gocb.CreateBucketOptions) error
	UpdateBucket(settings gocb.BucketSettings, opts *gocb.UpdateBucketOptions) error
	DropBucket(name string, opts *gocb.DropBucketOptions) error
	FlushBucket(name string, opts *gocb.FlushBucketOptions) error

	Unwrap() *gocb.BucketManager
}

type Cluster

type Cluster interface {
	Bucket(bucketName string) Bucket
	WaitUntilReady(timeout time.Duration, opts *gocb.WaitUntilReadyOptions) error
	Close(opts *gocb.ClusterCloseOptions) error
	Users() *gocb.UserManager
	Buckets() BucketManager
	AnalyticsIndexes() *gocb.AnalyticsIndexManager
	QueryIndexes() *gocb.QueryIndexManager
	SearchIndexes() *gocb.SearchIndexManager
	EventingFunctions() *gocb.EventingFunctionManager
	Transactions() Transactions

	SearchQuery(indexName string, query cbsearch.Query, opts *gocb.SearchOptions) (*gocb.SearchResult, error)

	Query(statement string, opts *gocb.QueryOptions) (*gocb.QueryResult, error)

	Ping(opts *gocb.PingOptions) (*gocb.PingResult, error)

	Internal() *gocb.InternalCluster

	Diagnostics(opts *gocb.DiagnosticsOptions) (*gocb.DiagnosticsResult, error)

	AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (*gocb.AnalyticsResult, error)

	// These methods are only available in Cluster interface, not available in gocb.Cluster instance
	Unwrap() *gocb.Cluster
	WrapTransactionAttemptContext(tac *gocb.TransactionAttemptContext, parentSpan gocb.RequestSpan) TransactionAttemptContext
}

func Connect

func Connect(s instana.TracerLogger, connStr string, opts gocb.ClusterOptions) (Cluster, error)

wrapper for gocb.Connect - it will return an instrumented *gocb.Cluster instance

type Collection

type Collection interface {
	Bucket() Bucket
	Name() string
	QueryIndexes() *gocb.CollectionQueryIndexManager

	Do(ops []gocb.BulkOp, opts *gocb.BulkOpOptions) error

	Insert(id string, val interface{}, opts *gocb.InsertOptions) (mutOut *gocb.MutationResult, errOut error)
	Upsert(id string, val interface{}, opts *gocb.UpsertOptions) (mutOut *gocb.MutationResult, errOut error)
	Replace(id string, val interface{}, opts *gocb.ReplaceOptions) (mutOut *gocb.MutationResult, errOut error)
	Get(id string, opts *gocb.GetOptions) (docOut *gocb.GetResult, errOut error)
	Exists(id string, opts *gocb.ExistsOptions) (docOut *gocb.ExistsResult, errOut error)
	GetAllReplicas(id string, opts *gocb.GetAllReplicaOptions) (docOut *gocb.GetAllReplicasResult, errOut error)
	GetAnyReplica(id string, opts *gocb.GetAnyReplicaOptions) (docOut *gocb.GetReplicaResult, errOut error)
	Remove(id string, opts *gocb.RemoveOptions) (mutOut *gocb.MutationResult, errOut error)
	GetAndTouch(id string, expiry time.Duration, opts *gocb.GetAndTouchOptions) (docOut *gocb.GetResult, errOut error)
	GetAndLock(id string, lockTime time.Duration, opts *gocb.GetAndLockOptions) (docOut *gocb.GetResult, errOut error)
	Unlock(id string, cas gocb.Cas, opts *gocb.UnlockOptions) (errOut error)
	Touch(id string, expiry time.Duration, opts *gocb.TouchOptions) (mutOut *gocb.MutationResult, errOut error)
	Binary() BinaryCollection

	List(id string) CouchbaseList
	Map(id string) CouchbaseMap
	Set(id string) CouchbaseSet
	Queue(id string) CouchbaseQueue

	LookupIn(id string, ops []gocb.LookupInSpec, opts *gocb.LookupInOptions) (docOut *gocb.LookupInResult, errOut error)
	MutateIn(id string, ops []gocb.MutateInSpec, opts *gocb.MutateInOptions) (mutOut *gocb.MutateInResult, errOut error)
	ScopeName() string

	Unwrap() *gocb.Collection
}

type CollectionManager

type CollectionManager interface {
	GetAllScopes(opts *gocb.GetAllScopesOptions) ([]gocb.ScopeSpec, error)
	CreateCollection(spec gocb.CollectionSpec, opts *gocb.CreateCollectionOptions) error
	DropCollection(spec gocb.CollectionSpec, opts *gocb.DropCollectionOptions) error
	CreateScope(scopeName string, opts *gocb.CreateScopeOptions) error
	DropScope(scopeName string, opts *gocb.DropScopeOptions) error

	Unwrap() *gocb.CollectionManager
}

type CouchbaseList

type CouchbaseList interface {
	Iterator() ([]interface{}, error)
	At(index int, valuePtr interface{}) error
	RemoveAt(index int) error
	Append(val interface{}) error
	Prepend(val interface{}) error
	IndexOf(val interface{}) (int, error)
	Size() (int, error)
	Clear() error

	Unwrap() *gocb.CouchbaseList
}

type CouchbaseMap

type CouchbaseMap interface {
	Iterator() (map[string]interface{}, error)
	At(id string, valuePtr interface{}) error
	Add(id string, val interface{}) error
	Remove(id string) error
	Exists(id string) (bool, error)
	Size() (int, error)
	Keys() ([]string, error)
	Values() ([]interface{}, error)
	Clear() error

	Unwrap() *gocb.CouchbaseMap
}

type CouchbaseQueue

type CouchbaseQueue interface {
	Iterator() ([]interface{}, error)
	Push(val interface{}) error
	Pop(valuePtr interface{}) error
	Size() (int, error)
	Clear() error

	Unwrap() *gocb.CouchbaseQueue
}

type CouchbaseSet

type CouchbaseSet interface {
	Iterator() ([]interface{}, error)
	Add(val interface{}) error
	Remove(val string) error
	Values() ([]interface{}, error)
	Contains(val string) (bool, error)
	Size() (int, error)
	Clear() error

	Unwrap() *gocb.CouchbaseSet
}

type Scope

type Scope interface {
	Name() string
	BucketName() string
	Collection(collectionName string) Collection

	Query(statement string, opts *gocb.QueryOptions) (*gocb.QueryResult, error)

	AnalyticsQuery(statement string, opts *gocb.AnalyticsOptions) (*gocb.AnalyticsResult, error)

	Unwrap() *gocb.Scope
}

type Span

type Span struct {
	// contains filtered or unexported fields
}

Instana span

func GetParentSpanFromContext

func GetParentSpanFromContext(ctx context.Context) *Span

Getting parent span from current context. Users need to pass this parent span to the options (eg : gocb.QueryOptions)

func (*Span) AddEvent

func (s *Span) AddEvent(name string, timestamp time.Time)

Not used; implemented as this one is part of gocb.RequestSpan interface

func (*Span) Context

func (s *Span) Context() gocb.RequestSpanContext

To get the span context

func (*Span) End

func (s *Span) End()

Ending a span

func (*Span) SetAttribute

func (s *Span) SetAttribute(key string, value interface{})

Setting attributes in span

type Tracer

type Tracer struct {
	// contains filtered or unexported fields
}

Instana tracer

func (*Tracer) RequestSpan

func (t *Tracer) RequestSpan(parentContext gocb.RequestSpanContext, operationType string) gocb.RequestSpan

Request span will create a new span

type TransactionAttemptContext

type TransactionAttemptContext interface {
	Query(statement string, options *gocb.TransactionQueryOptions) (*gocb.TransactionQueryResult, error)
	Get(collection *gocb.Collection, id string) (*gocb.TransactionGetResult, error)
	Replace(collection *gocb.Collection, doc *gocb.TransactionGetResult, value interface{}) (*gocb.TransactionGetResult, error)
	Insert(collection *gocb.Collection, id string, value interface{}) (*gocb.TransactionGetResult, error)
	Remove(collection *gocb.Collection, doc *gocb.TransactionGetResult) error

	Unwrap() *gocb.TransactionAttemptContext
}

type Transactions

type Transactions interface {
	Run(logicFn gocb.AttemptFunc, perConfig *gocb.TransactionOptions) (*gocb.TransactionResult, error)

	Unwrap() *gocb.Transactions
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL