Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pkg/kv/kvpb/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3900,6 +3900,12 @@ message ContentionEvent {
// IsLatch, if true, indicates that the contention event was due to waiting to
// acquire a latch.
bool is_latch = 4;

// BlockingStmtFingerprintID is the fingerprint ID of the statement that held
// the contended lock.
uint64 blocking_stmt_fingerprint_id = 5 [
(gogoproto.customname) = "BlockingStmtFingerprintID"
];
}

// QuorumReplicationFlowAdmissionEvent is a message that is recorded on traces by a
Expand Down
35 changes: 22 additions & 13 deletions pkg/sql/crdb_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -7912,6 +7912,7 @@ CREATE TABLE crdb_internal.transaction_contention_events (

blocking_txn_id UUID NOT NULL,
blocking_txn_fingerprint_id BYTES NOT NULL,
blocking_stmt_fingerprint_id BYTES,

waiting_txn_id UUID NOT NULL,
waiting_txn_fingerprint_id BYTES NOT NULL,
Expand Down Expand Up @@ -7957,17 +7958,24 @@ CREATE TABLE crdb_internal.transaction_contention_events (
return nil, nil, err
}

const numDatums = 15
const numDatums = 16
row := make(tree.Datums, numDatums)
worker := func(ctx context.Context, pusher rowPusher) error {
for i := range resp.Events {
collectionTs, err := tree.MakeDTimestampTZ(resp.Events[i].CollectionTs, time.Microsecond)
if err != nil {
return err
}
blockingFingerprintID := tree.NewDBytes(
blockingTxnFingerprintID := tree.NewDBytes(
tree.DBytes(sqlstatsutil.EncodeUint64ToBytes(uint64(resp.Events[i].BlockingTxnFingerprintID))))

blockingStmtFingerprintID := tree.DNull
if resp.Events[i].BlockingEvent.BlockingStmtFingerprintID != 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can pull this out into a variable to make the if block a little more readable

blockingStmtFingerprintID = tree.NewDBytes(
tree.DBytes(sqlstatsutil.EncodeUint64ToBytes(
resp.Events[i].BlockingEvent.BlockingStmtFingerprintID)))
}

waitingFingerprintID := tree.NewDBytes(
tree.DBytes(sqlstatsutil.EncodeUint64ToBytes(uint64(resp.Events[i].WaitingTxnFingerprintID))))

Expand Down Expand Up @@ -7998,18 +8006,19 @@ CREATE TABLE crdb_internal.transaction_contention_events (
row = append(row[:0],
collectionTs, // collection_ts
tree.NewDUuid(tree.DUuid{UUID: resp.Events[i].BlockingEvent.TxnMeta.ID}), // blocking_txn_id
blockingFingerprintID, // blocking_fingerprint_id
blockingTxnFingerprintID, // blocking_txn_fingerprint_id
blockingStmtFingerprintID, // blocking_stmt_fingerprint_id
tree.NewDUuid(tree.DUuid{UUID: resp.Events[i].WaitingTxnID}), // waiting_txn_id
waitingFingerprintID, // waiting_fingerprint_id
contentionDuration, // contention_duration
contendingKey, // contending_key,
contendingPrettyKey, // contending_pretty_key
waitingStmtId, // waiting_stmt_id
waitingStmtFingerprintID, // waiting_stmt_fingerprint_id
tree.NewDString(dbName), // database_name
tree.NewDString(schemaName), // schema_name
tree.NewDString(tableName), // table_name
tree.NewDString(indexName), // index_name
waitingFingerprintID, // waiting_fingerprint_id
contentionDuration, // contention_duration
contendingKey, // contending_key,
contendingPrettyKey, // contending_pretty_key
waitingStmtId, // waiting_stmt_id
waitingStmtFingerprintID, // waiting_stmt_fingerprint_id
tree.NewDString(dbName), // database_name
tree.NewDString(schemaName), // schema_name
tree.NewDString(tableName), // table_name
tree.NewDString(indexName), // index_name
tree.NewDString(resp.Events[i].ContentionType.String()),
)
if buildutil.CrdbTestBuild {
Expand Down
78 changes: 78 additions & 0 deletions pkg/sql/crdb_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1925,3 +1925,81 @@ func TestSupportedCRDBInternalTablesNotChanged(t *testing.T) {
}
}
}

func TestBlockingStmtFingerprintIDInContentionEventsTable(t *testing.T) {
defer leaktest.AfterTest(t)()
defer log.Scope(t).Close(t)

ctx := context.Background()
srv, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{
DefaultTestTenant: base.TestIsSpecificToStorageLayerAndNeedsASystemTenant,
})
Comment on lines +1934 to +1936

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: im not sure if this DefaultTestTenant is needed. I think you can just do serverutils.StartServer(t, base.TestServerArgs{})

defer srv.Stopper().Stop(ctx)
s := srv.ApplicationLayer()

_, err := sqlDB.Exec("SET CLUSTER SETTING sql.contention.event_store.resolution_interval = '10ms'")
require.NoError(t, err)

testCases := []struct {
name string
key string
blockingStmtFingerprintID uint64
expected gosql.NullString
}{
{
name: "non-null",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: lets name these something like "blocking_stmt_fingerprint_not_exists" and "blocking_stmt_fingerprint_exists"

key: "/Animals/Cats",
blockingStmtFingerprintID: 9005,
expected: gosql.NullString{String: fmt.Sprintf("%016x", uint64(9005)), Valid: true},
},
{
name: "null",
key: "/Animals/Dogs",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keyEscaped := fmt.Sprintf("\"%s\"", tc.key)
s.ExecutorConfig().(sql.ExecutorConfig).ContentionRegistry.AddContentionEvent(contentionpb.ExtendedContentionEvent{
BlockingEvent: kvpb.ContentionEvent{
Key: roachpb.Key(tc.key),
TxnMeta: enginepb.TxnMeta{
Key: roachpb.Key(tc.key),
ID: uuid.MakeV4(),
},
Duration: 1 * time.Minute,
BlockingStmtFingerprintID: tc.blockingStmtFingerprintID,
},
BlockingTxnFingerprintID: 9001,
WaitingTxnID: uuid.MakeV4(),
WaitingTxnFingerprintID: 9002,
WaitingStmtID: clusterunique.ID{Uint128: uint128.Uint128{Lo: 9003, Hi: 1004}},
WaitingStmtFingerprintID: 9004,
ContentionType: contentionpb.ContentionType_LOCK_WAIT,
})

// Contention flush can take some time to flush the events.
testutils.SucceedsSoon(t, func() error {
row := sqlDB.QueryRow(`SELECT
encode(blocking_stmt_fingerprint_id, 'hex')
FROM crdb_internal.transaction_contention_events
WHERE contending_pretty_key = $1`, keyEscaped)

var actual gosql.NullString
err = row.Scan(&actual)
if err != nil {
return err
}
if actual != tc.expected {
return errors.Newf(
"unexpected blocking stmt fingerprint id: got %+v, expected %+v",
actual,
tc.expected,
)
}
return nil
})
})
}
}
Loading