Skip to content

Commit c8c2809

Browse files
craig[bot]kyle-a-wong
andcommitted
Merge #153608
153608: builtins,sql: Add new request_transaction_bundle builtin r=kyle-a-wong a=kyle-a-wong This new builtin creates a transaction diagnostic request to capture and generate a transaction bundle for a specified transaction fingerprint id. This builting has the following signature: `crdb_internal.request_transaction_bundle(transaction_fingerprint_id: string, sampling_probability: float, min_execution_latency: interval, expires_after: interval, redacted: bool) -> bool` transaction_fingerprint_id - a hex-encoded fingerprint id for the transaction intended to be captured sampling_probability - the probability used to determine if a transaction bundle should be recorded. This value must be between [0,1]. min_execution_latency - the minimum execution latency for a transaction. If the execution time of a transaction is lower than this value, the request will not be considered fulfilled and will remain uncompleted. If sampling_probability is non-zero, this value must also be non-zero. expires_after - the duration in which a request will remain open. If zero is provided, the request will exist until it is completed or deleted. redacted - whether or not the collected bundle will be redacted or unredacted The builtin will return true if the request is generated successfully, or false if creating a request for transaction fingerprint id that doesn't exist. Existence of a transaction fingerprint id is determined by its existence in `crdb_internal.transaction_statistics`. This is necessary because it is currently the only system of record for transaction fingerprints in cockroachdb. The user making the request for a transaction diagnostics bundle must have either VIEWACTIVITY or VIEWACTIVITYREDACTED grants to call this builtin. If they have VIEWACTIVITYREDACTED, only redacted bundles can be requested. Resolves: [CRDB-54322](https://cockroachlabs.atlassian.net/browse/CRDB-54322) Epic: [CRDB-53541](https://cockroachlabs.atlassian.net/browse/CRDB-53541) Release note (sql change): adds a new builtin: `crdb_internal.request_transaction_bundle(transaction_fingerprint_id: string, sampling_probability: float, min_execution_latency: interval, expires_after: interval, redacted: bool) -> bool` transaction_fingerprint_id - a hex-encoded fingerprint id for the transaction intended to be captured sampling_probability - the probability used to determine if a transaction bundle should be recorded. This value must be between [0,1]. min_execution_latency - the minimum execution latency for a transaction. If the execution time of a transaction is lower than this value, the request will not be considered fulfilled and will remain uncompleted. If sampling_probability is non-zero, this value must also be non-zero. expires_after - the duration in which a request will remain open. If zero is provided, the request will exist until it is completed or deleted. redacted - whether or not the collected bundle will be redacted or unredacted The built-in will return true if the request is generated successfully, or false if creating a request for transaction fingerprint id that doesn't exist. The user making the request for a transaction diagnostics bundle must have either VIEWACTIVITY or VIEWACTIVITYREDACTED grants to call this builtin. If they have VIEWACTIVITYREDACTED, only redacted bundles can be requested. ---- note: This is a stacked PR, only the last commit should be reviewed Co-authored-by: Kyle Wong <[email protected]>
2 parents 1a26fe7 + 1f08c98 commit c8c2809

File tree

10 files changed

+426
-8
lines changed

10 files changed

+426
-8
lines changed

pkg/sql/conn_executor.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3825,6 +3825,7 @@ func (ex *connExecutor) initEvalCtx(ctx context.Context, evalCtx *extendedEvalCo
38253825
ConsistencyChecker: p.execCfg.ConsistencyChecker,
38263826
RangeProber: p.execCfg.RangeProber,
38273827
StmtDiagnosticsRequestInserter: ex.server.cfg.StmtDiagnosticsRecorder.InsertRequest,
3828+
TxnDiagnosticsRequestInserter: ex.server.cfg.TxnDiagnosticsRecorder.InsertTxnRequest,
38283829
CatalogBuiltins: &p.evalCatalogBuiltins,
38293830
QueryCancelKey: ex.queryCancelKey,
38303831
DescIDGenerator: ex.getDescIDGenerator(),

pkg/sql/planner.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ func internalExtendedEvalCtx(
556556
IndexUsageStatsController: indexUsageStatsController,
557557
ConsistencyChecker: execCfg.ConsistencyChecker,
558558
StmtDiagnosticsRequestInserter: execCfg.StmtDiagnosticsRecorder.InsertRequest,
559+
TxnDiagnosticsRequestInserter: execCfg.TxnDiagnosticsRecorder.InsertTxnRequest,
559560
RangeStatsFetcher: execCfg.RangeStatsFetcher,
560561
},
561562
Tracing: &SessionTracing{},

pkg/sql/sem/builtins/fixed_oids.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,6 +2859,7 @@ var builtinOidsArray = []string{
28592859
2904: `lca(ltree[]: ltree[]) -> ltree`,
28602860
2905: `levenshtein_less_equal(source: string, target: string, max_d: int) -> int`,
28612861
2906: `levenshtein_less_equal(source: string, target: string, ins_cost: int, del_cost: int, sub_cost: int, max_d: int) -> int`,
2862+
2907: `crdb_internal.request_transaction_bundle(transaction_fingerprint_id: string, sampling_probability: float, min_execution_latency: interval, expires_after: interval, redacted: bool) -> tuple{int AS request_id, bool AS created}`,
28622863
}
28632864

28642865
var builtinOidsBySignature map[string]oid.Oid

pkg/sql/sem/builtins/generator_builtins.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,25 @@ The last argument is a JSONB object containing the following optional fields:
838838
makeInternallyExecutedQueryGeneratorOverload(false /* withSessionBound */, true /* withOverrides */, true /* withTxn */),
839839
makeInternallyExecutedQueryGeneratorOverload(true /* withSessionBound */, true /* withOverrides */, true /* withTxn */),
840840
),
841+
"crdb_internal.request_transaction_bundle": makeBuiltin(
842+
tree.FunctionProperties{
843+
Category: builtinconstants.CategorySystemInfo,
844+
DistsqlBlocklist: true,
845+
},
846+
makeGeneratorOverload(
847+
tree.ParamTypes{
848+
{Name: "transaction_fingerprint_id", Typ: types.String},
849+
{Name: "sampling_probability", Typ: types.Float},
850+
{Name: "min_execution_latency", Typ: types.Interval},
851+
{Name: "expires_after", Typ: types.Interval},
852+
{Name: "redacted", Typ: types.Bool},
853+
},
854+
txnDiagnosticsRequestGeneratorType,
855+
makeTxnDiagnosticsRequestGenerator,
856+
"Starts a transaction diagnostic for the transaction fingerprint id",
857+
volatility.Volatile,
858+
),
859+
),
841860
}
842861

843862
var decodePlanGistGeneratorType = types.String
@@ -4199,3 +4218,135 @@ func (qi *internallyExecutedQueryIterator) Close(context.Context) {
41994218
func (qi *internallyExecutedQueryIterator) ResolvedType() *types.T {
42004219
return internallyExecutedQueryGeneratorType
42014220
}
4221+
4222+
func makeTxnDiagnosticsRequestGenerator(
4223+
ctx context.Context, evalCtx *eval.Context, args tree.Datums,
4224+
) (eval.ValueGenerator, error) {
4225+
hasPriv, shouldRedact, err := evalCtx.SessionAccessor.HasViewActivityOrViewActivityRedactedRole(ctx)
4226+
if err != nil {
4227+
return nil, err
4228+
}
4229+
4230+
txnFingerprintIdStr := string(tree.MustBeDString(args[0]))
4231+
samplingProbability := float64(tree.MustBeDFloat(args[1]))
4232+
minExecutionLatency := tree.MustBeDInterval(args[2])
4233+
expiresAfter := tree.MustBeDInterval(args[3])
4234+
redacted := bool(tree.MustBeDBool(args[4]))
4235+
txnFingerprintId, err := strconv.ParseUint(txnFingerprintIdStr, 16, 64)
4236+
if err != nil {
4237+
return nil, errors.New("invalid transaction fingerprint id: must be a hex encoded representation of a transaction fingerprint id")
4238+
}
4239+
query := `
4240+
SELECT jsonb_array_elements_text(metadata->'stmtFingerprintIDs') as stmt_fingerprint_id
4241+
FROM (
4242+
SELECT metadata from crdb_internal.transaction_statistics
4243+
WHERE fingerprint_id = decode($1, 'hex')
4244+
LIMIT 1
4245+
) t
4246+
`
4247+
it, err := evalCtx.Planner.QueryIteratorEx(ctx, "get_txn_fingerprint", sessiondata.NoSessionDataOverride, query, txnFingerprintIdStr)
4248+
if err != nil {
4249+
return &txnDiagnosticsRequestGenerator{requestId: 0, created: false}, err
4250+
}
4251+
4252+
var stmtFPs []uint64
4253+
var ok, found bool
4254+
for ok, err = it.Next(ctx); ok; ok, err = it.Next(ctx) {
4255+
if err != nil {
4256+
return nil, err
4257+
}
4258+
found = true
4259+
id := string(tree.MustBeDString(it.Cur()[0]))
4260+
value, err := strconv.ParseUint(id, 16, 64)
4261+
if err != nil {
4262+
return nil, err
4263+
}
4264+
stmtFPs = append(stmtFPs, value)
4265+
}
4266+
4267+
if shouldRedact {
4268+
if !redacted {
4269+
return nil, pgerror.Newf(
4270+
pgcode.InsufficientPrivilege,
4271+
"users with VIEWACTIVITYREDACTED privilege can only request redacted statement bundles",
4272+
)
4273+
}
4274+
} else if !hasPriv {
4275+
return nil, pgerror.Newf(
4276+
pgcode.InsufficientPrivilege,
4277+
"requesting statement bundle requires VIEWACTIVITY privilege",
4278+
)
4279+
}
4280+
4281+
var username string
4282+
if sd := evalCtx.SessionData(); sd != nil {
4283+
username = sd.User().Normalized()
4284+
}
4285+
reqId, err := evalCtx.TxnDiagnosticsRequestInserter(
4286+
ctx,
4287+
txnFingerprintId,
4288+
stmtFPs,
4289+
username,
4290+
samplingProbability,
4291+
time.Duration(minExecutionLatency.Duration.Nanos()),
4292+
time.Duration(expiresAfter.Duration.Nanos()),
4293+
redacted)
4294+
if err != nil {
4295+
return nil, err
4296+
}
4297+
4298+
if !found {
4299+
return &txnDiagnosticsRequestGenerator{created: false}, nil
4300+
}
4301+
return &txnDiagnosticsRequestGenerator{requestId: reqId, created: true}, nil
4302+
}
4303+
4304+
// txnDiagnosticsRequestGenerator supports the execution of request_transaction_bundle.
4305+
var txnDiagnosticsRequestGeneratorType = types.MakeLabeledTuple(
4306+
[]*types.T{types.Int, types.Bool},
4307+
[]string{"request_id", "created"},
4308+
)
4309+
4310+
type txnDiagnosticsRequestGenerator struct {
4311+
requestId int
4312+
created bool
4313+
called bool
4314+
}
4315+
4316+
// ResolvedType implements the eval.ValueGenerator interface.
4317+
func (g *txnDiagnosticsRequestGenerator) ResolvedType() *types.T {
4318+
return txnDiagnosticsRequestGeneratorType
4319+
}
4320+
4321+
// Start implements the eval.ValueGenerator interface.
4322+
func (g *txnDiagnosticsRequestGenerator) Start(ctx context.Context, txn *kv.Txn) error {
4323+
return nil
4324+
}
4325+
4326+
// Next implements the eval.ValueGenerator interface.
4327+
func (g *txnDiagnosticsRequestGenerator) Next(ctx context.Context) (bool, error) {
4328+
if g.called {
4329+
return false, nil
4330+
}
4331+
g.called = true
4332+
return true, nil
4333+
}
4334+
4335+
// Values implements the eval.ValueGenerator interface.
4336+
func (g *txnDiagnosticsRequestGenerator) Values() (tree.Datums, error) {
4337+
var requestIdDatum tree.Datum
4338+
if g.created {
4339+
requestIdDatum = tree.NewDInt(tree.DInt(g.requestId))
4340+
} else {
4341+
requestIdDatum = tree.DNull
4342+
}
4343+
4344+
return tree.Datums{
4345+
requestIdDatum,
4346+
tree.MakeDBool(tree.DBool(g.created)),
4347+
}, nil
4348+
}
4349+
4350+
// Close implements the eval.ValueGenerator interface.
4351+
func (g *txnDiagnosticsRequestGenerator) Close(ctx context.Context) {
4352+
}

pkg/sql/sem/eval/context.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@ type Context struct {
246246
// bundle request.
247247
StmtDiagnosticsRequestInserter StmtDiagnosticsRequestInsertFunc
248248

249+
// TxnDiagnosticsRequestInsertFunc is used by the
250+
// crdb_internal.request_transaction_bundle builtin to insert a transaction
251+
// bundle request.
252+
TxnDiagnosticsRequestInserter TxnDiagnosticsRequestInsertFunc
253+
249254
// CatalogBuiltins is used by various builtins which depend on looking up
250255
// catalog information. Unlike the Planner, it is available in DistSQL.
251256
CatalogBuiltins CatalogBuiltins

pkg/sql/sem/eval/deps.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,20 @@ type StmtDiagnosticsRequestInsertFunc func(
765765
username string,
766766
) error
767767

768+
// TxnDiagnosticsRequestInsertFunc is an interface embedded in EvalCtx that can
769+
// be used by the builtins to insert a transaction diagnostics request. This
770+
// interface is introduced to avoid circular dependency.
771+
type TxnDiagnosticsRequestInsertFunc func(
772+
ctx context.Context,
773+
txnFingerprintId uint64,
774+
stmtFingerprintIds []uint64,
775+
username string,
776+
samplingProbability float64,
777+
minExecutionLatency time.Duration,
778+
expiresAfter time.Duration,
779+
redacted bool,
780+
) (int, error)
781+
768782
// AsOfSystemTime represents the result from the evaluation of AS OF SYSTEM TIME
769783
// clause.
770784
type AsOfSystemTime struct {

0 commit comments

Comments
 (0)