Skip to content

Commit

Permalink
chore(spanner): add support of client side native metrics collection …
Browse files Browse the repository at this point in the history
…and export
  • Loading branch information
rahul2393 committed Aug 16, 2024
1 parent 48eb295 commit c507ea3
Show file tree
Hide file tree
Showing 8 changed files with 1,051 additions and 24 deletions.
168 changes: 160 additions & 8 deletions go.work.sum

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions spanner/apiv1/spanner_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 21 additions & 1 deletion spanner/apiv1/spanner_client_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@

package spanner

import "google.golang.org/api/option"
import (
"cloud.google.com/go/spanner"
"context"
"github.com/googleapis/gax-go/v2"
"google.golang.org/api/option"
)

// Returns the default client options used by the generated Spanner client.
//
Expand All @@ -23,3 +28,18 @@ import "google.golang.org/api/option"
func DefaultClientOptions() []option.ClientOption {
return defaultGRPCClientOptions()
}

// gaxInvokeWithRecorder:
// - wraps 'f' in a new function 'callWrapper' that:
// - updates tracer state and records built in attempt specific metrics
// - does not return errors seen while recording the metrics
//
// - then, calls gax.Invoke with 'callWrapper' as an argument
func gaxInvokeWithRecorder(ctx context.Context, method string,
f func(ctx context.Context, _ gax.CallSettings) error, opts ...gax.CallOption) error {
mt, ok := ctx.Value(spanner.ContextKeyBuiltInMetricsTracer).(*spanner.BuiltinMetricsTracer)
if !ok {
return gax.Invoke(ctx, f, opts...)
}
return gax.Invoke(ctx, mt.GetCallWrapper(method, f), opts...)
}
55 changes: 55 additions & 0 deletions spanner/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package spanner
import (
"context"
"fmt"
"go.opentelemetry.io/otel/metric/noop"
"io"
"log"
"os"
Expand Down Expand Up @@ -111,6 +112,7 @@ type Client struct {
disableRouteToLeader bool
dro *sppb.DirectedReadOptions
otConfig *openTelemetryConfig
metricsTracerFactory *builtinMetricsTracerFactory
}

// DatabaseName returns the full name of a database, e.g.,
Expand All @@ -124,6 +126,40 @@ func (c *Client) ClientID() string {
return c.sc.id
}

func (c *Client) newBuiltinMetricsTracer(ctx context.Context, isStreaming bool) *BuiltinMetricsTracer {
mt := c.metricsTracerFactory.createBuiltinMetricsTracer(ctx, isStreaming)
ctx = context.WithValue(ctx, ContextKeyBuiltInMetricsTracer, &mt)
return &mt
}

// recordOperationCompletion records as many operation specific metrics as it can
// Ignores error seen while creating metric attributes since metric can still
// be recorded with rest of the attributes
func recordOperationCompletion(mt *BuiltinMetricsTracer) {
if !mt.builtInEnabled {
return
}

// Calculate elapsed time
elapsedTimeMs := convertToMs(time.Since(mt.currOp.startTime))

// Record operation_count
opCntAttrs, _ := mt.toOtelMetricAttrs(metricNameOperationCount)
mt.instrumentOperationCount.Add(mt.ctx, 1, metric.WithAttributes(opCntAttrs...))

// Record operation_latencies
opLatAttrs, _ := mt.toOtelMetricAttrs(metricNameOperationLatencies)
mt.instrumentOperationLatencies.Record(mt.ctx, elapsedTimeMs, metric.WithAttributes(opLatAttrs...))

// Record attempt_count
attemptCntAttrs, _ := mt.toOtelMetricAttrs(metricNameAttemptCount)
if mt.currOp.attemptCount > 1 {
// Only record when retry count is greater than 0 so the retry
// graph will be less confusing
mt.instrumentAttemptCount.Add(mt.ctx, mt.currOp.attemptCount-1, metric.WithAttributes(attemptCntAttrs...))
}
}

func createGCPMultiEndpoint(cfg *grpcgcp.GCPMultiEndpointOptions, config ClientConfig, opts ...option.ClientOption) (*grpcgcp.GCPMultiEndpoint, error) {
if cfg.GRPCgcpConfig == nil {
cfg.GRPCgcpConfig = &grpcgcppb.ApiConfig{}
Expand Down Expand Up @@ -469,6 +505,18 @@ func newClientWithConfig(ctx context.Context, database string, config ClientConf
return nil, err
}

metricsProvider := otConfig.meterProvider
if emulatorAddr := os.Getenv("SPANNER_EMULATOR_HOST"); emulatorAddr != "" {
// Do not emit metrics when emulator is being used
metricsProvider = noop.NewMeterProvider()
}

// Create a OpenTelemetry metrics configuration
metricsTracerFactory, err := newBuiltinMetricsTracerFactory(ctx, database, metricsProvider)
if err != nil {
return nil, err
}

c = &Client{
sc: sc,
idleSessions: sp,
Expand All @@ -482,6 +530,7 @@ func newClientWithConfig(ctx context.Context, database string, config ClientConf
disableRouteToLeader: config.DisableRouteToLeader,
dro: config.DirectedReadOptions,
otConfig: otConfig,
metricsTracerFactory: metricsTracerFactory,
}
return c, nil
}
Expand Down Expand Up @@ -570,6 +619,9 @@ func getQueryOptions(opts QueryOptions) QueryOptions {

// Close closes the client.
func (c *Client) Close() {
if c.metricsTracerFactory != nil {
c.metricsTracerFactory.shutdown()
}
if c.idleSessions != nil {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
Expand Down Expand Up @@ -932,6 +984,9 @@ func ApplyCommitOptions(co CommitOptions) ApplyOption {

// Apply applies a list of mutations atomically to the database.
func (c *Client) Apply(ctx context.Context, ms []*Mutation, opts ...ApplyOption) (commitTimestamp time.Time, err error) {
mt := c.newBuiltinMetricsTracer(ctx, false)
defer recordOperationCompletion(mt)

ao := &applyOption{}

for _, opt := range c.ao {
Expand Down
11 changes: 7 additions & 4 deletions spanner/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ require (
cloud.google.com/go v0.115.0
cloud.google.com/go/iam v1.1.12
cloud.google.com/go/longrunning v0.5.11
cloud.google.com/go/monitoring v1.20.3
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0
github.com/google/go-cmp v0.6.0
github.com/googleapis/gax-go/v2 v2.13.0
go.opencensus.io v0.24.0
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/metric v1.24.0
go.opentelemetry.io/otel v1.28.0
go.opentelemetry.io/otel/metric v1.28.0
go.opentelemetry.io/otel/sdk/metric v1.28.0
golang.org/x/oauth2 v0.22.0
golang.org/x/sync v0.8.0
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9
Expand Down Expand Up @@ -39,11 +41,12 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
Expand Down
20 changes: 12 additions & 8 deletions spanner/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,8 @@ cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhI
cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4=
cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w=
cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw=
cloud.google.com/go/monitoring v1.20.3 h1:v/7MXFxYrhXLEZ9sSfwXdlTLLB/xrU7xTyYjY5acynQ=
cloud.google.com/go/monitoring v1.20.3/go.mod h1:GPIVIdNznIdGqEjtRKQWTLcUeRnPjZW85szouimiczU=
cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM=
Expand Down Expand Up @@ -905,14 +907,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08=
go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
Expand Down
Loading

0 comments on commit c507ea3

Please sign in to comment.