From 640035eddc666129bf2441f04c90797ebf79d034 Mon Sep 17 00:00:00 2001 From: angelapwen Date: Thu, 11 Feb 2021 12:55:46 +0100 Subject: [PATCH] builtins: add builtin to retrieve the payload(s) for a span. The `crdb_internal.payloads_for_span` builtin retrieves all payloads for a given span ID, given that the span is part of an active trace. The payloads are returned in JSONB format. If the span is not found, or if the span does not have any payloads, the builtin returns an empty JSON object. With the appropriate usage of this builtin and the `crdb_internal.trace_id` builtin as shown in the `contention_event` logic test, all payloads for the current trace may be surfaced. Release note (sql change): add `payloads_for_span` builtin that takes in a span ID and returns its paylods in JSONB format. If the span is not found, or if the span does not have any payloads, the builtin returns an empty JSON object. --- docs/generated/sql/functions.md | 2 ++ .../testdata/logic_test/contention_event | 16 +++++++++ pkg/sql/sem/builtins/BUILD.bazel | 1 + pkg/sql/sem/builtins/builtins.go | 36 +++++++++++++++++++ pkg/util/tracing/tracer.go | 11 +++++- 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/docs/generated/sql/functions.md b/docs/generated/sql/functions.md index fd830a090539..529a7dcc8745 100644 --- a/docs/generated/sql/functions.md +++ b/docs/generated/sql/functions.md @@ -2638,6 +2638,8 @@ SELECT * FROM crdb_internal.check_consistency(true, ‘\x02’, ‘\x04’)

crdb_internal.num_inverted_index_entries(val: jsonb, version: int) → int

This function is used only by CockroachDB’s developers for testing purposes.

+crdb_internal.payloads_for_span(span ID: int) → jsonb

Returns the payload(s) of the span whose ID is passed in the argument.

+
crdb_internal.pretty_key(raw_key: bytes, skip_fields: int) → string

This function is used only by CockroachDB’s developers for testing purposes.

crdb_internal.range_stats(key: bytes) → jsonb

This function is used to retrieve range statistics information as a JSON object.

diff --git a/pkg/sql/logictest/testdata/logic_test/contention_event b/pkg/sql/logictest/testdata/logic_test/contention_event index ede473134a10..95ff95fd2b55 100644 --- a/pkg/sql/logictest/testdata/logic_test/contention_event +++ b/pkg/sql/logictest/testdata/logic_test/contention_event @@ -52,7 +52,23 @@ user root # # NB: this needs the 5node-pretend59315 config because otherwise the span is not # tracked. +# +# TODO(angelapwen): Remove this test along with num_payloads column query B SELECT count(num_payloads) > 0 FROM crdb_internal.node_inflight_trace_spans WHERE trace_id = crdb_internal.trace_id(); ---- true + +# For all spans, make sure there is 1 payload that is a contention event. +query B +WITH Spans AS ( +SELECT span_id FROM crdb_internal.node_inflight_trace_spans +WHERE trace_id = crdb_internal.trace_id() +), PayloadTypes AS ( +SELECT jsonb_array_elements(crdb_internal.payloads_for_span(span_id))->>'@type' AS type, crdb_internal.payloads_for_span(span_id) +FROM Spans +) SELECT COUNT(*) = 1 +FROM PayloadTypes +WHERE type = 'type.googleapis.com/cockroach.roachpb.ContentionEvent'; +---- +true diff --git a/pkg/sql/sem/builtins/BUILD.bazel b/pkg/sql/sem/builtins/BUILD.bazel index b699dd2b53ba..bf7c5a630f20 100644 --- a/pkg/sql/sem/builtins/BUILD.bazel +++ b/pkg/sql/sem/builtins/BUILD.bazel @@ -84,6 +84,7 @@ go_library( "//pkg/util/uuid", "@com_github_cockroachdb_apd_v2//:apd", "@com_github_cockroachdb_errors//:errors", + "@com_github_gogo_protobuf//types", "@com_github_golang_geo//s1", "@com_github_knz_strtime//:strtime", "@com_github_lib_pq//oid", diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index 09a3a34ea2cb..6abe29df5ed8 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -73,6 +73,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/unaccent" "github.com/cockroachdb/cockroach/pkg/util/uuid" "github.com/cockroachdb/errors" + pbtypes "github.com/gogo/protobuf/types" "github.com/knz/strtime" ) @@ -3598,6 +3599,41 @@ may increase either contention or retry errors, or both.`, }, ), + "crdb_internal.payloads_for_span": makeBuiltin( + tree.FunctionProperties{Category: categorySystemInfo}, + tree.Overload{ + Types: tree.ArgTypes{{"span ID", types.Int}}, + ReturnType: tree.FixedReturnType(types.Jsonb), + Fn: func(ctx *tree.EvalContext, args tree.Datums) (tree.Datum, error) { + builder := json.NewArrayBuilder(len(args)) + + spanID := uint64(*(args[0].(*tree.DInt))) + span, found := ctx.Settings.Tracer.GetActiveSpanFromID(spanID) + // A span may not be found if its ID was surfaced previously but its + // corresponding trace has ended by the time this builtin was run. + if !found { + // Returns an empty JSON array. + return tree.NewDJSON(builder.Build()), nil + } + + for _, rec := range span.GetRecording() { + rec.Structured(func(item *pbtypes.Any) { + payload, err := protoreflect.MessageToJSON(item, true /* emitDefaults */) + if err != nil { + return + } + if payload != nil { + builder.Add(payload) + } + }) + } + return tree.NewDJSON(builder.Build()), nil + }, + Info: "Returns the payload(s) of the span whose ID is passed in the argument.", + Volatility: tree.VolatilityVolatile, + }, + ), + "crdb_internal.locality_value": makeBuiltin( tree.FunctionProperties{Category: categorySystemInfo}, tree.Overload{ diff --git a/pkg/util/tracing/tracer.go b/pkg/util/tracing/tracer.go index b34ed6b35e64..8aef5f31a8be 100644 --- a/pkg/util/tracing/tracer.go +++ b/pkg/util/tracing/tracer.go @@ -155,7 +155,8 @@ type Tracer struct { // In normal operation, a local root Span is inserted on creation and // removed on .Finish(). // - // The map can be introspected by `Tracer.VisitSpans`. + // The map can be introspected by `Tracer.VisitSpans`. A Span can also be + // retrieved from its ID by `Tracer.GetActiveSpanFromID`. activeSpans struct { // NB: it might be tempting to use a sync.Map here, but // this incurs an allocation per Span (sync.Map does @@ -680,6 +681,14 @@ func (t *Tracer) ExtractMetaFrom(carrier Carrier) (*SpanMeta, error) { }, nil } +// GetActiveSpanFromID retrieves any active span given its span ID. +func (t *Tracer) GetActiveSpanFromID(spanID uint64) (*Span, bool) { + t.activeSpans.Lock() + span, found := t.activeSpans.m[spanID] + t.activeSpans.Unlock() + return span, found +} + // VisitSpans invokes the visitor with all active Spans. The function will // gracefully exit if the visitor returns iterutil.StopIteration(). func (t *Tracer) VisitSpans(visitor func(*Span) error) error {