Skip to content

Commit 9f25778

Browse files
craig[bot]fqazialyshanjahani-crlUzair5162
committed
151737: sql: add support for automatically repairing dangling comments r=fqazi a=fqazi Previously, we added logic to detect dangling comments on descriptors but did not include a mechanism to clean them up. This could block initial upgrades due to dangling entries in the system.comments table. This patch adds support for automatically cleaning up these dangling comments. Fixes: #151497 Release note (bug fix): Added an automatic repair for dangling or invalid entries in the system.comment table. 151880: server: Filter high contention insights in ListExecutionInsights r=alyshanjahani-crl a=alyshanjahani-crl Previously high contention insights included in the ListExecutionInsights response often did not have their respective contention events available in the contention events registry. This led to confusion when viewing an insight execution overview page since the table displaying the contention events for the execution would often be empty. This commit fixes this behaviour by ensuring that any high contention insights are only included in the ListExecutionInsights response if they have at least one valid contention event available in the contention event registry. A contention event is valid if the blocking transaction fingerprint ID (i.e. the column of most interest in the UI table) is resolved. Fixes: https://cockroachlabs.atlassian.net/browse/CRDB-53271 Release note: None 151951: sql: fix `BETWEEN SYMMETRIC` normalization to use matching typed lefts r=Uzair5162 a=Uzair5162 We normalize symmetric `RangeCond` expressions (`<left> BETWEEN SYMMETRIC <from> AND <to>`) by expanding them into 2 pairs of `ComparisonExpr`, covering both orderings of the BETWEEN bounds. When the type-checks for the left/from and left/to comparisons chose different coercions, the symmetric half could pair `leftFrom` with `to` (and `leftTo` with `from`) during normalization. This resulted in mixed-type comparisons on type-checked expressions and caused panics in some paths (e.g. AOST normalization). This change ensures that the type-checked pairings are kept aligned when constructing `ComparisonExpr` in the symmetric case. Fixes: #133395 Release note (bug fix): Previously, executing certain statements with `BETWEEN SYMMETRIC` expressions could panic if used with values of different types, such as `... b'bytes' BETWEEN SYMMETRIC 'a' AND 'c'`, which is now fixed. 152033: sql/stats: ignore partial stats in `crdb_internal.table_row_statistics` r=Uzair5162 a=Uzair5162 Previously, the `crdb_internal.table_row_statistics` virtual table was being populated with the row count from the most recent table statistic. This caused incorrect row counts to be shown for `SHOW TABLES` when the most recent statistic was a partial collection. This change ignores partial stats when populating `crdb_internal.table_row_statistics` to fix this issue. Fixes: #152024 Release note (bug fix): Previously, `SHOW TABLES` would show inaccurate row counts if the most recent statistic collection was partial, which is now fixed. Co-authored-by: Faizan Qazi <[email protected]> Co-authored-by: Alyshan Jahani <[email protected]> Co-authored-by: Uzair Ahmad <[email protected]>
5 parents bf671a7 + bda91c9 + 6105cef + 98b77e1 + 8deda8b commit 9f25778

File tree

17 files changed

+240
-16
lines changed

17 files changed

+240
-16
lines changed

pkg/server/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,8 @@ go_test(
556556
"//pkg/sql/catalog/descs",
557557
"//pkg/sql/catalog/desctestutils",
558558
"//pkg/sql/catalog/systemschema",
559+
"//pkg/sql/contention",
560+
"//pkg/sql/contentionpb",
559561
"//pkg/sql/execinfrapb",
560562
"//pkg/sql/isql",
561563
"//pkg/sql/pgwire",

pkg/server/status.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import (
6161
"github.com/cockroachdb/cockroach/pkg/settings/cluster"
6262
"github.com/cockroachdb/cockroach/pkg/spanconfig"
6363
"github.com/cockroachdb/cockroach/pkg/sql"
64+
"github.com/cockroachdb/cockroach/pkg/sql/appstatspb"
6465
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descs"
6566
"github.com/cockroachdb/cockroach/pkg/sql/clusterunique"
6667
"github.com/cockroachdb/cockroach/pkg/sql/contention"
@@ -86,6 +87,7 @@ import (
8687
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
8788
"github.com/cockroachdb/cockroach/pkg/util/tracing/tracingpb"
8889
"github.com/cockroachdb/cockroach/pkg/util/uint128"
90+
"github.com/cockroachdb/cockroach/pkg/util/uuid"
8991
"github.com/cockroachdb/errors"
9092
"github.com/cockroachdb/redact"
9193
"github.com/google/pprof/profile"
@@ -393,23 +395,70 @@ func (b *baseStatusServer) localExecutionInsights(
393395
) (*serverpb.ListExecutionInsightsResponse, error) {
394396
var response serverpb.ListExecutionInsightsResponse
395397

398+
highContentionInsights := make(map[uuid.UUID]insights.Insight, 0)
396399
reader := b.sqlServer.pgServer.SQLServer.GetInsightsReader()
397400
reader.IterateInsights(ctx, func(ctx context.Context, insight *insights.Insight) {
398401
if insight == nil {
399402
return
400403
}
401404

402-
insightsCopy := *insight
403405
// Copy statements slice - these insights objects can be read concurrently.
406+
insightsCopy := *insight
404407
insightsCopy.Statements = make([]*insights.Statement, len(insight.Statements))
405408
copy(insightsCopy.Statements, insight.Statements)
406-
407-
response.Insights = append(response.Insights, insightsCopy)
409+
if insight.Transaction != nil && slices.Contains(insight.Transaction.Causes, insights.Cause_HighContention) {
410+
// Collect high contention insights seperately, they need additional validation / filtering.
411+
// If it is valid we will add it to the response later.
412+
highContentionInsights[insightsCopy.Transaction.ID] = insightsCopy
413+
} else {
414+
// All other insights are included in the response.
415+
response.Insights = append(response.Insights, insightsCopy)
416+
}
408417
})
409418

419+
// Validating contention insights involves iterating through the contention events registry.
420+
// Only do so if we have insights to validate.
421+
if len(highContentionInsights) > 0 {
422+
// Include only the valid high contention insights in the response.
423+
valid, err := validContentionInsights(b.sqlServer.execCfg.ContentionRegistry, highContentionInsights)
424+
if err != nil {
425+
return nil, errors.Wrap(err, "validating contention insights")
426+
}
427+
428+
for validInsightTxnID := range valid {
429+
response.Insights = append(response.Insights, highContentionInsights[validInsightTxnID])
430+
}
431+
}
432+
410433
return &response, nil
411434
}
412435

436+
// validContentionInsights iterates through the contention event registry and checks the
437+
// events that are related to the passed in (contention) insights. The insights that have
438+
// valid (resolved BlockingTxnFingerprintID) contention events are returned.
439+
func validContentionInsights(
440+
registry *contention.Registry, contentionInsights map[uuid.UUID]insights.Insight,
441+
) (map[uuid.UUID]bool, error) {
442+
valid := make(map[uuid.UUID]bool, len(contentionInsights))
443+
err := registry.ForEachEvent(func(event *contentionpb.ExtendedContentionEvent) error {
444+
// If we have already determined an execution (insight) to be valid, no work to do.
445+
if ok := valid[event.WaitingTxnID]; ok {
446+
return nil
447+
}
448+
// We are interested in the contention events that are related to our insights.
449+
if _, ok := contentionInsights[event.WaitingTxnID]; ok {
450+
// If the event has the blocking transaction fingerprint ID resolved, we consider it
451+
// valid for the insights response (since the insight response already has the waiting
452+
// transaction fingerprint ID).
453+
if event.BlockingTxnFingerprintID != appstatspb.InvalidTransactionFingerprintID {
454+
valid[event.WaitingTxnID] = true
455+
}
456+
}
457+
return nil
458+
})
459+
return valid, err
460+
}
461+
413462
func (b *baseStatusServer) localTxnIDResolution(
414463
req *serverpb.TxnIDResolutionRequest,
415464
) *serverpb.TxnIDResolutionResponse {

pkg/server/status_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ import (
2525
"github.com/cockroachdb/cockroach/pkg/server/authserver"
2626
"github.com/cockroachdb/cockroach/pkg/server/serverpb"
2727
"github.com/cockroachdb/cockroach/pkg/server/status/statuspb"
28+
"github.com/cockroachdb/cockroach/pkg/settings/cluster"
2829
"github.com/cockroachdb/cockroach/pkg/sql/catalog/desctestutils"
30+
"github.com/cockroachdb/cockroach/pkg/sql/contention"
31+
"github.com/cockroachdb/cockroach/pkg/sql/contentionpb"
2932
"github.com/cockroachdb/cockroach/pkg/sql/sqlstats/insights"
3033
tablemetadatacacheutil "github.com/cockroachdb/cockroach/pkg/sql/tablemetadatacache/util"
3134
"github.com/cockroachdb/cockroach/pkg/testutils"
@@ -34,6 +37,7 @@ import (
3437
"github.com/cockroachdb/cockroach/pkg/util"
3538
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
3639
"github.com/cockroachdb/cockroach/pkg/util/log"
40+
"github.com/cockroachdb/cockroach/pkg/util/uuid"
3741
"github.com/cockroachdb/errors"
3842
"github.com/stretchr/testify/assert"
3943
"github.com/stretchr/testify/require"
@@ -521,6 +525,49 @@ func TestListExecutionInsightsWhileEvictingInsights(t *testing.T) {
521525
wg.Wait()
522526
}
523527

528+
// TestLocalExecutionInsights tests the helper functions used by localExecutionInsights
529+
func TestLocalExecutionInsights(t *testing.T) {
530+
defer leaktest.AfterTest(t)()
531+
defer log.Scope(t).Close(t)
532+
533+
t.Run("validContentionInsights", func(t *testing.T) {
534+
id1 := uuid.MakeV4()
535+
id2 := uuid.MakeV4()
536+
id3 := uuid.MakeV4()
537+
insights := map[uuid.UUID]insights.Insight{
538+
id1: {},
539+
id2: {},
540+
id3: {},
541+
}
542+
// id1 insight has no contention events.
543+
// id2 insight has a contention event that is not resolved.
544+
// id3 insight has multiple contention events, one of them is resolved.
545+
events := []contentionpb.ExtendedContentionEvent{
546+
{
547+
WaitingTxnID: id2,
548+
},
549+
{
550+
WaitingTxnID: id3,
551+
},
552+
{
553+
WaitingTxnID: id3,
554+
BlockingTxnFingerprintID: 1234,
555+
},
556+
}
557+
558+
st := cluster.MakeTestingClusterSettings()
559+
m := contention.NewMetrics()
560+
registry := contention.NewRegistry(st, nil, &m)
561+
registry.AddEventsForTest(events)
562+
valid, err := validContentionInsights(registry, insights)
563+
require.NoError(t, err)
564+
// Only id3 should be returned.
565+
require.Equal(t, 1, len(valid))
566+
_, exists := valid[id3]
567+
require.True(t, exists)
568+
})
569+
}
570+
524571
// TestStatusUpdateTableMetadataCache tests that signalling the update
525572
// table metadata cache job via the status server triggers the update
526573
// table metadata job to run.

pkg/sql/contention/event_store.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,22 @@ func (s *eventStore) upsertBatch(events []contentionpb.ExtendedContentionEvent)
339339
}
340340
}
341341

342+
// addEventsForTest is a convenience function used by tests to directly add events to
343+
// the eventStore bypassing the resolver and buffer guard.
344+
func (s *eventStore) addEventsForTest(events []contentionpb.ExtendedContentionEvent) {
345+
s.mu.Lock()
346+
defer s.mu.Unlock()
347+
348+
for i := range events {
349+
blockingTxnID := events[i].BlockingEvent.TxnMeta.ID
350+
_, ok := s.mu.store.Get(blockingTxnID)
351+
if !ok {
352+
atomic.AddInt64(&s.atomic.storageSize, int64(entryBytes(&events[i])))
353+
}
354+
s.mu.store.Add(events[i].Hash(), events[i])
355+
}
356+
}
357+
342358
func (s *eventStore) resolutionIntervalWithJitter() time.Duration {
343359
baseInterval := TxnIDResolutionInterval.Get(&s.st.SV)
344360

pkg/sql/contention/registry.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ func (r *Registry) Start(ctx context.Context, stopper *stop.Stopper) {
256256
func (r *Registry) AddContentionEvent(event contentionpb.ExtendedContentionEvent) {
257257
r.globalLock.Lock()
258258
defer r.globalLock.Unlock()
259+
259260
if event.ContentionType == contentionpb.ContentionType_LOCK_WAIT {
260261
// (xinhaoz) We will need to change the indexMap structs if we want to surface
261262
// non lock wait related contention to index contention surfaces.
@@ -288,6 +289,12 @@ func (r *Registry) AddContentionEvent(event contentionpb.ExtendedContentionEvent
288289
r.eventStore.addEvent(event)
289290
}
290291

292+
// AddEventsForTest is a convenience function used by tests to directly add events to
293+
// the underlying eventStore.
294+
func (r *Registry) AddEventsForTest(events []contentionpb.ExtendedContentionEvent) {
295+
r.eventStore.addEventsForTest(events)
296+
}
297+
291298
// ForEachEvent implements the eventReader interface.
292299
func (r *Registry) ForEachEvent(op func(event *contentionpb.ExtendedContentionEvent) error) error {
293300
return r.eventStore.ForEachEvent(op)

pkg/sql/crdb_internal.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ CREATE TABLE crdb_internal.table_row_statistics (
776776
SELECT DISTINCT ON ("tableID") "tableID", "rowCount"
777777
FROM system.table_statistics
778778
AS OF SYSTEM TIME '%s'
779+
WHERE "partialPredicate" IS NULL
779780
ORDER BY "tableID", "createdAt" DESC, "rowCount" DESC`,
780781
statsAsOfTimeClusterMode.String(&p.ExecCfg().Settings.SV))
781782
statRows, err := p.ExtendedEvalContext().ExecCfg.InternalDB.Executor().QueryBufferedEx(
@@ -6665,10 +6666,23 @@ CREATE VIEW crdb_internal.kv_repairable_catalog_corruptions (
66656666
FROM
66666667
system.namespace AS ns FULL JOIN system.descriptor AS d ON ns.id = d.id
66676668
),
6669+
orphaned_comments
6670+
AS (
6671+
SELECT
6672+
0 AS parent_id,
6673+
0 AS parent_schema_id,
6674+
'' AS name,
6675+
object_id AS id,
6676+
'comment' AS corruption
6677+
FROM
6678+
system.comments
6679+
WHERE
6680+
object_id NOT IN (SELECT id FROM system.descriptor)
6681+
),
66686682
diag
66696683
AS (
66706684
SELECT
6671-
*,
6685+
parent_id, parent_schema_id, name, id,
66726686
CASE
66736687
WHEN descriptor IS NULL AND id != 29 THEN 'namespace'
66746688
WHEN updated_descriptor != repaired_descriptor THEN 'descriptor'
@@ -6677,6 +6691,8 @@ CREATE VIEW crdb_internal.kv_repairable_catalog_corruptions (
66776691
AS corruption
66786692
FROM
66796693
data
6694+
UNION
6695+
SELECT * FROM orphaned_comments
66806696
)
66816697
SELECT
66826698
parent_id, parent_schema_id, name, id, corruption

pkg/sql/faketreeeval/evalctx.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ func (ep *DummyEvalPlanner) UpsertDroppedRelationGCTTL(
227227
return errors.WithStack(errEvalPlanner)
228228
}
229229

230+
// UnsafeDeleteComment is part of the Planner interface.
231+
func (ep *DummyEvalPlanner) UnsafeDeleteComment(ctx context.Context, objectID int64) error {
232+
return errors.WithStack(errEvalPlanner)
233+
}
234+
230235
// UserHasAdminRole is part of the Planner interface.
231236
func (ep *DummyEvalPlanner) UserHasAdminRole(
232237
ctx context.Context, user username.SQLUsername,

pkg/sql/logictest/testdata/logic_test/as_of

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,12 @@ statement error pq: AS OF SYSTEM TIME: timestamp '2090-05-08 12:00:00' is in the
226226
CREATE VIEW wontcreate AS SELECT * FROM nonexistent AS OF SYSTEM TIME '2090-05-08 12:00:00'
227227

228228
subtest end
229+
230+
# Regression test for #133395. Ensure that an invalid AS OF SYSTEM TIME clause
231+
# requiring normalization doesn't panic and returns an error.
232+
statement ok
233+
CREATE TABLE v00 (c01 INT)
234+
235+
statement error pq: AS OF SYSTEM TIME: expected timestamp, decimal, or interval, got bool
236+
SELECT ALL FROM ( v00 AS ta1401 NATURAL JOIN v00 AS ta1402 ) WITH ORDINALITY AS ta1403
237+
AS OF SYSTEM TIME b'any_bytes' BETWEEN SYMMETRIC 'abc' AND 'abc';

pkg/sql/logictest/testdata/logic_test/table

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,3 +674,23 @@ pr_check CHECK ((price > 0)) true
674674

675675
statement ok
676676
COMMIT;
677+
678+
subtest end
679+
680+
# Test that partial statistics are filtered out from table_row_statistics
681+
statement ok
682+
CREATE TABLE t (k INT PRIMARY KEY)
683+
684+
statement ok
685+
INSERT INTO t SELECT generate_series(0, 9)
686+
687+
statement ok
688+
ANALYZE t
689+
690+
statement ok
691+
CREATE STATISTICS partial FROM t USING EXTREMES
692+
693+
query I
694+
SELECT estimated_row_count FROM crdb_internal.table_row_statistics WHERE table_name = 't'
695+
----
696+
10

pkg/sql/pgwire/testdata/pgtest/procedure

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ until
6767
ReadyForQuery
6868
----
6969
{"Type":"RowDescription","Fields":null}
70-
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"foo","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func378","UnknownFields":null}
70+
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"foo","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func379","UnknownFields":null}
7171
{"Type":"RowDescription","Fields":null}
72-
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"bar","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func378","UnknownFields":null}
72+
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"bar","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func379","UnknownFields":null}
7373
{"Type":"RowDescription","Fields":null}
74-
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"baz","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func378","UnknownFields":null}
74+
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"baz","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func379","UnknownFields":null}
7575
{"Type":"CommandComplete","CommandTag":"CALL"}
7676
{"Type":"ReadyForQuery","TxStatus":"I"}
7777

@@ -87,10 +87,10 @@ ReadyForQuery
8787
----
8888
{"Type":"ParseComplete"}
8989
{"Type":"BindComplete"}
90-
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"foo","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func378","UnknownFields":null}
90+
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"foo","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func379","UnknownFields":null}
9191
{"Type":"RowDescription","Fields":null}
92-
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"bar","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func378","UnknownFields":null}
92+
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"bar","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func379","UnknownFields":null}
9393
{"Type":"RowDescription","Fields":null}
94-
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"baz","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func378","UnknownFields":null}
94+
{"Severity":"NOTICE","SeverityUnlocalized":"NOTICE","Code":"00000","Message":"baz","Detail":"","Hint":"","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"builtins.go","Line":0,"Routine":"func379","UnknownFields":null}
9595
{"Type":"CommandComplete","CommandTag":"CALL"}
9696
{"Type":"ReadyForQuery","TxStatus":"I"}

0 commit comments

Comments
 (0)