diff --git a/docs/generated/settings/settings-for-tenants.txt b/docs/generated/settings/settings-for-tenants.txt index aa505b41e955..4248fcea0444 100644 --- a/docs/generated/settings/settings-for-tenants.txt +++ b/docs/generated/settings/settings-for-tenants.txt @@ -134,9 +134,6 @@ This session variable default should now be configured using ALTER ROLE... SET: sql.defaults.experimental_implicit_column_partitioning.enabled boolean false "default value for experimental_enable_temp_tables; allows for the use of implicit column partitioning This cluster setting is being kept to preserve backwards-compatibility. This session variable default should now be configured using ALTER ROLE... SET: https://www.cockroachlabs.com/docs/stable/alter-role.html" -sql.defaults.experimental_stream_replication.enabled boolean false "default value for experimental_stream_replication session setting;enables the ability to setup a replication stream -This cluster setting is being kept to preserve backwards-compatibility. -This session variable default should now be configured using ALTER ROLE... SET: https://www.cockroachlabs.com/docs/stable/alter-role.html" sql.defaults.experimental_temporary_tables.enabled boolean false "default value for experimental_enable_temp_tables; allows for use of temporary tables by default This cluster setting is being kept to preserve backwards-compatibility. This session variable default should now be configured using ALTER ROLE... SET: https://www.cockroachlabs.com/docs/stable/alter-role.html" diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index 62825f595d8a..11081ead30f9 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -135,7 +135,6 @@
sql.defaults.experimental_distsql_planning
enumerationoffdefault experimental_distsql_planning mode; enables experimental opt-driven DistSQL planning [off = 0, on = 1]
This cluster setting is being kept to preserve backwards-compatibility.
This session variable default should now be configured using ALTER ROLE... SET
sql.defaults.experimental_enable_unique_without_index_constraints.enabled
booleanfalsedefault value for experimental_enable_unique_without_index_constraints session setting;disables unique without index constraints by default
This cluster setting is being kept to preserve backwards-compatibility.
This session variable default should now be configured using ALTER ROLE... SET
sql.defaults.experimental_implicit_column_partitioning.enabled
booleanfalsedefault value for experimental_enable_temp_tables; allows for the use of implicit column partitioning
This cluster setting is being kept to preserve backwards-compatibility.
This session variable default should now be configured using ALTER ROLE... SET -
sql.defaults.experimental_stream_replication.enabled
booleanfalsedefault value for experimental_stream_replication session setting;enables the ability to setup a replication stream
This cluster setting is being kept to preserve backwards-compatibility.
This session variable default should now be configured using ALTER ROLE... SET
sql.defaults.experimental_temporary_tables.enabled
booleanfalsedefault value for experimental_enable_temp_tables; allows for use of temporary tables by default
This cluster setting is being kept to preserve backwards-compatibility.
This session variable default should now be configured using ALTER ROLE... SET
sql.defaults.foreign_key_cascades_limit
integer10000default value for foreign_key_cascades_limit session setting; limits the number of cascading operations that run as part of a single query
This cluster setting is being kept to preserve backwards-compatibility.
This session variable default should now be configured using ALTER ROLE... SET
sql.defaults.idle_in_session_timeout
duration0sdefault value for the idle_in_session_timeout; default value for the idle_in_session_timeout session setting; controls the duration a session is permitted to idle before the session is terminated; if set to 0, there is no timeout
This cluster setting is being kept to preserve backwards-compatibility.
This session variable default should now be configured using ALTER ROLE... SET diff --git a/pkg/ccl/spanconfigccl/spanconfigreconcilerccl/testdata/multitenant/tenant_end_key_split b/pkg/ccl/spanconfigccl/spanconfigreconcilerccl/testdata/multitenant/tenant_end_key_split new file mode 100644 index 000000000000..ac6a68ec6b5c --- /dev/null +++ b/pkg/ccl/spanconfigccl/spanconfigreconcilerccl/testdata/multitenant/tenant_end_key_split @@ -0,0 +1,115 @@ +# Test that tenant creation does not overwrite span config state for another +# tenant. + +reconcile +---- + +mutations discard +---- + +# Initialize a new tenant, tenant=11, that DOES NOT have a pre-existing tenant, +# tenant=12, next to it. +initialize tenant=11 +---- + +# A record IS written for a key that logically belongs to the next tenant, +# tenant=12, because tenant=12 DOES NOT exist. +state offset=57 limit=3 +---- +... +/Tenant/11{-\x00} database system (tenant) +/Tenant/12{-\x00} database system (tenant) + +# Start the reconciliation loop for the tenant=11. It'll first clear out its own +# first-key record and install span configs for its SQL state. It won't touch +# the record we created at in the tenant=12 keyspace because it's not taught to +# look there, and neither should it. The keyspace it's responsible for is its +# own. +reconcile tenant=11 +---- + +# Peek near the start of the span_configurations table where tenant=11's records +# are stored. The first one is from the start of its keyspace to start of +# table with ID=4: /Tenant/11{-/Table/4}. +state offset=56 limit=3 +---- +... +/Table/5{7-8} ttl_seconds=3600 ignore_strict_gc=true num_replicas=5 rangefeed_enabled=true +/Tenant/11{-/Table/4} database system (tenant) +/Tenant/11/Table/{4-5} database system (tenant) +... + +# Peek near the end of the span_configurations table where tenant=11's records +# are stored. The last one is for its last system table: +# /Tenant/11/Table/5{7-8}. Right now the split is at /Tenant/12. Which is fine. +state offset=99 limit=3 +---- +... +/Tenant/11/Table/5{7-8} database system (tenant) +/Tenant/12{-\x00} database system (tenant) + +# Just another view of what the tenant's reconciler actually did. It got rid of +# the original, single-key /Tenant/11{-\x00} record, and replaced it with +# /Tenant/11{-/Table/4}, just like the comment above said. The remaining upserts +# are for its SQL state. +mutations tenant=11 limit=2 +---- +delete /Tenant/11{-\x00} +upsert /Tenant/11{-/Table/4} database system (tenant) +upsert /Tenant/11/Table/{4-5} database system (tenant) +upsert /Tenant/11/Table/{5-6} database system (tenant) +upsert /Tenant/11/Table/{6-7} database system (tenant) +upsert /Tenant/11/Table/{7-8} database system (tenant) +upsert /Tenant/11/Table/1{1-2} database system (tenant) +upsert /Tenant/11/Table/1{2-3} database system (tenant) +upsert /Tenant/11/Table/1{3-4} database system (tenant) +upsert /Tenant/11/Table/1{4-5} database system (tenant) +upsert /Tenant/11/Table/1{5-6} database system (tenant) +upsert /Tenant/11/Table/{19-20} database system (tenant) +upsert /Tenant/11/Table/2{0-1} database system (tenant) +upsert /Tenant/11/Table/2{1-2} database system (tenant) +upsert /Tenant/11/Table/2{3-4} database system (tenant) +upsert /Tenant/11/Table/2{4-5} database system (tenant) +upsert /Tenant/11/Table/2{5-6} database system (tenant) +upsert /Tenant/11/Table/2{6-7} database system (tenant) +upsert /Tenant/11/Table/2{7-8} database system (tenant) +upsert /Tenant/11/Table/2{8-9} database system (tenant) +upsert /Tenant/11/NamespaceTable/{30-Max} database system (tenant) +upsert /Tenant/11/{NamespaceTable/Max-Table/32} database system (tenant) +upsert /Tenant/11/Table/3{2-3} database system (tenant) +upsert /Tenant/11/Table/3{3-4} database system (tenant) +upsert /Tenant/11/Table/3{4-5} database system (tenant) +upsert /Tenant/11/Table/3{5-6} database system (tenant) +upsert /Tenant/11/Table/3{6-7} database system (tenant) +upsert /Tenant/11/Table/3{7-8} database system (tenant) +upsert /Tenant/11/Table/{39-40} database system (tenant) +upsert /Tenant/11/Table/4{0-1} database system (tenant) +upsert /Tenant/11/Table/4{1-2} database system (tenant) +upsert /Tenant/11/Table/4{2-3} database system (tenant) +upsert /Tenant/11/Table/4{3-4} database system (tenant) +upsert /Tenant/11/Table/4{4-5} database system (tenant) +upsert /Tenant/11/Table/4{6-7} database system (tenant) +upsert /Tenant/11/Table/4{8-9} database system (tenant) +upsert /Tenant/11/Table/5{0-1} database system (tenant) +upsert /Tenant/11/Table/5{1-2} database system (tenant) +upsert /Tenant/11/Table/5{2-3} database system (tenant) +upsert /Tenant/11/Table/5{3-4} database system (tenant) +upsert /Tenant/11/Table/5{4-5} database system (tenant) +upsert /Tenant/11/Table/5{5-6} database system (tenant) +upsert /Tenant/11/Table/5{6-7} database system (tenant) +upsert /Tenant/11/Table/5{7-8} database system (tenant) + +# Initialize a new tenant, tenant=10, that DOES have a pre-existing tenant, +# tenant=11, next to it. +initialize tenant=10 +---- + +# A record IS NOT written for a key that logically belongs to the next tenant, +# tenant=11, because tenant=11 DOES exist. +state offset=57 limit=3 +---- +... +/Tenant/10{-\x00} database system (tenant) +/Tenant/11{-/Table/4} database system (tenant) +/Tenant/11/Table/{4-5} database system (tenant) +... diff --git a/pkg/ccl/streamingccl/replicationtestutils/replication_helpers.go b/pkg/ccl/streamingccl/replicationtestutils/replication_helpers.go index 0b33bd078476..80c6fe418837 100644 --- a/pkg/ccl/streamingccl/replicationtestutils/replication_helpers.go +++ b/pkg/ccl/streamingccl/replicationtestutils/replication_helpers.go @@ -223,7 +223,7 @@ func NewReplicationHelper( SET CLUSTER SETTING kv.rangefeed.enabled = true; SET CLUSTER SETTING kv.closed_timestamp.target_duration = '1s'; SET CLUSTER SETTING changefeed.experimental_poll_interval = '10ms'; -SET CLUSTER SETTING sql.defaults.experimental_stream_replication.enabled = 'on'; +SET CLUSTER SETTING cross_cluster_replication.enabled = true; `, `;`)...) // Sink to read data from. diff --git a/pkg/ccl/streamingccl/replicationtestutils/testutils.go b/pkg/ccl/streamingccl/replicationtestutils/testutils.go index 9d370d63d04a..847bb513cbe5 100644 --- a/pkg/ccl/streamingccl/replicationtestutils/testutils.go +++ b/pkg/ccl/streamingccl/replicationtestutils/testutils.go @@ -273,7 +273,7 @@ func CreateTenantStreamingClusters( args.DestInitFunc(t, tsc.DestSysSQL) } // Enable stream replication on dest by default. - tsc.DestSysSQL.Exec(t, `SET enable_experimental_stream_replication = true;`) + tsc.DestSysSQL.Exec(t, `SET CLUSTER SETTING cross_cluster_replication.enabled = true;`) return tsc, func() { require.NoError(t, srcTenantConn.Close()) destCleanup() diff --git a/pkg/ccl/streamingccl/settings.go b/pkg/ccl/streamingccl/settings.go index ebaff1db04f4..cdcc62ee44b6 100644 --- a/pkg/ccl/streamingccl/settings.go +++ b/pkg/ccl/streamingccl/settings.go @@ -14,6 +14,15 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings" ) +// CrossClusterReplicationEnabled enables the ability to setup and control a +// cross cluster replication stream. +var CrossClusterReplicationEnabled = settings.RegisterBoolSetting( + settings.TenantWritable, + "cross_cluster_replication.enabled", + "enables the ability to setup and control a cross cluster replication stream", + false, +) + // StreamReplicationStreamLivenessTrackFrequency controls frequency to check // the liveness of a streaming replication producer job. var StreamReplicationStreamLivenessTrackFrequency = settings.RegisterDurationSetting( diff --git a/pkg/ccl/streamingccl/streamingest/alter_replication_job.go b/pkg/ccl/streamingccl/streamingest/alter_replication_job.go index faa9562ef314..4644ef86afbe 100644 --- a/pkg/ccl/streamingccl/streamingest/alter_replication_job.go +++ b/pkg/ccl/streamingccl/streamingest/alter_replication_job.go @@ -12,6 +12,7 @@ import ( "context" "math" + "github.com/cockroachdb/cockroach/pkg/ccl/streamingccl" "github.com/cockroachdb/cockroach/pkg/ccl/streamingccl/replicationutils" "github.com/cockroachdb/cockroach/pkg/ccl/utilccl" "github.com/cockroachdb/cockroach/pkg/jobs" @@ -111,6 +112,19 @@ func alterReplicationJobHook( return nil, nil, nil, false, nil } + if !streamingccl.CrossClusterReplicationEnabled.Get(&p.ExecCfg().Settings.SV) { + return nil, nil, nil, false, errors.WithTelemetry( + pgerror.WithCandidateCode( + errors.WithHint( + errors.Newf("cross cluster replication is disabled"), + "You can enable cross cluster replication by running `SET CLUSTER SETTING cross_cluster_replication.enabled = true`.", + ), + pgcode.ExperimentalFeature, + ), + "cross_cluster_replication.enabled", + ) + } + if err := p.RequireAdminRole(ctx, "ALTER TENANT REPLICATION"); err != nil { return nil, nil, nil, false, err } diff --git a/pkg/ccl/streamingccl/streamingest/alter_replication_job_test.go b/pkg/ccl/streamingccl/streamingest/alter_replication_job_test.go index ec17cfb30764..cc1424251297 100644 --- a/pkg/ccl/streamingccl/streamingest/alter_replication_job_test.go +++ b/pkg/ccl/streamingccl/streamingest/alter_replication_job_test.go @@ -85,6 +85,7 @@ func TestAlterTenantPauseResume(t *testing.T) { }) t.Run("pause-resume-as-non-system-tenant", func(t *testing.T) { + c.DestTenantSQL.Exec(t, `SET CLUSTER SETTING cross_cluster_replication.enabled = true`) c.DestTenantSQL.ExpectErr(t, "only the system tenant can alter tenant", `ALTER TENANT $1 PAUSE REPLICATION`, "foo") c.DestTenantSQL.ExpectErr(t, "only the system tenant can alter tenant", `ALTER TENANT $1 RESUME REPLICATION`, "foo") }) diff --git a/pkg/ccl/streamingccl/streamingest/metrics.go b/pkg/ccl/streamingccl/streamingest/metrics.go index 18cba44f8fb1..909cdefb1479 100644 --- a/pkg/ccl/streamingccl/streamingest/metrics.go +++ b/pkg/ccl/streamingccl/streamingest/metrics.go @@ -24,31 +24,31 @@ const ( var ( metaReplicationEventsIngested = metric.Metadata{ Name: "replication.events_ingested", - Help: "Events ingested by all ingestion jobs", + Help: "Events ingested by all replication jobs", Measurement: "Events", Unit: metric.Unit_COUNT, } metaReplicationResolvedEventsIngested = metric.Metadata{ Name: "replication.resolved_events_ingested", - Help: "Resolved events ingested by all ingestion jobs", + Help: "Resolved events ingested by all replication jobs", Measurement: "Events", Unit: metric.Unit_COUNT, } metaReplicationIngestedBytes = metric.Metadata{ - Name: "replication.ingested_bytes", - Help: "Bytes ingested by all ingestion jobs", + Name: "replication.logical_bytes", + Help: "Logical bytes (sum of keys + values) ingested by all replication jobs", Measurement: "Bytes", Unit: metric.Unit_BYTES, } metaReplicationSSTBytes = metric.Metadata{ Name: "replication.sst_bytes", - Help: "SST bytes (compressed) sent to KV by all ingestion jobs", + Help: "SST bytes (compressed) sent to KV by all replication jobs", Measurement: "Bytes", Unit: metric.Unit_BYTES, } metaReplicationFlushes = metric.Metadata{ Name: "replication.flushes", - Help: "Total flushes across all ingestion jobs", + Help: "Total flushes across all replication jobs", Measurement: "Flushes", Unit: metric.Unit_COUNT, } @@ -105,8 +105,9 @@ var ( Unit: metric.Unit_COUNT, } metaFrontierLagSeconds = metric.Metadata{ - Name: "replication.frontier_lag_seconds", - Help: "Time the replication frontier lags", + Name: "replication.frontier_lag_seconds", + Help: "Time between the wall clock and replicated time of the replication stream. " + + "This metric tracks how far behind the replication stream is relative to now", Measurement: "Seconds", Unit: metric.Unit_SECONDS, } diff --git a/pkg/ccl/streamingccl/streamingest/replication_random_client_test.go b/pkg/ccl/streamingccl/streamingest/replication_random_client_test.go index def502dec46f..9fd3f7041cba 100644 --- a/pkg/ccl/streamingccl/streamingest/replication_random_client_test.go +++ b/pkg/ccl/streamingccl/streamingest/replication_random_client_test.go @@ -238,9 +238,9 @@ func TestStreamIngestionJobWithRandomClient(t *testing.T) { // Attempt to run the ingestion job without enabling the experimental setting. _, err = conn.Exec(query) - require.True(t, testutils.IsError(err, "stream replication is only supported experimentally")) + require.True(t, testutils.IsError(err, "cross cluster replication is disabled")) - _, err = conn.Exec(`SET enable_experimental_stream_replication = true`) + _, err = conn.Exec(`SET CLUSTER SETTING cross_cluster_replication.enabled = true;`) require.NoError(t, err) _, err = conn.Exec(query) diff --git a/pkg/ccl/streamingccl/streamingest/stream_ingestion_job_test.go b/pkg/ccl/streamingccl/streamingest/stream_ingestion_job_test.go index 4ff05a3f5a70..5dfc1872461b 100644 --- a/pkg/ccl/streamingccl/streamingest/stream_ingestion_job_test.go +++ b/pkg/ccl/streamingccl/streamingest/stream_ingestion_job_test.go @@ -113,7 +113,7 @@ SET CLUSTER SETTING stream_replication.consumer_heartbeat_frequency = '100ms'; SET CLUSTER SETTING bulkio.stream_ingestion.minimum_flush_interval = '500ms'; SET CLUSTER SETTING bulkio.stream_ingestion.cutover_signal_poll_interval = '100ms'; SET CLUSTER SETTING stream_replication.job_checkpoint_frequency = '100ms'; -SET enable_experimental_stream_replication = true; +SET CLUSTER SETTING cross_cluster_replication.enabled = true; `, ";")...) @@ -191,7 +191,7 @@ func TestTenantStreamingCreationErrors(t *testing.T) { SrcSysSQL.Exec(t, `SET CLUSTER SETTING kv.rangefeed.enabled = true`) DestSysSQL := sqlutils.MakeSQLRunner(destDB) - DestSysSQL.Exec(t, `SET enable_experimental_stream_replication = true`) + DestSysSQL.Exec(t, `SET CLUSTER SETTING cross_cluster_replication.enabled = true;`) // Sink to read data from. srcPgURL, cleanupSink := sqlutils.PGUrl(t, srcServer.ServingSQLAddr(), t.Name(), url.User(username.RootUser)) diff --git a/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go b/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go index 249533858a62..9f77c1dd61cc 100644 --- a/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go +++ b/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go @@ -68,17 +68,16 @@ func ingestionPlanHook( return nil, nil, nil, false, nil } - // Check if the experimental feature is enabled. - if !p.SessionData().EnableStreamReplication { + if !streamingccl.CrossClusterReplicationEnabled.Get(&p.ExecCfg().Settings.SV) { return nil, nil, nil, false, errors.WithTelemetry( pgerror.WithCandidateCode( errors.WithHint( - errors.Newf("stream replication is only supported experimentally"), - "You can enable stream replication by running `SET enable_experimental_stream_replication = true`.", + errors.Newf("cross cluster replication is disabled"), + "You can enable cross cluster replication by running `SET CLUSTER SETTING cross_cluster_replication.enabled = true`.", ), pgcode.ExperimentalFeature, ), - "replication.ingest.disabled", + "cross_cluster_replication.enabled", ) } diff --git a/pkg/ccl/streamingccl/streamproducer/replication_stream_test.go b/pkg/ccl/streamingccl/streamproducer/replication_stream_test.go index 8314de7806f8..c416b85911b9 100644 --- a/pkg/ccl/streamingccl/streamproducer/replication_stream_test.go +++ b/pkg/ccl/streamingccl/streamproducer/replication_stream_test.go @@ -176,7 +176,7 @@ func startReplication( conn, err := pgx.ConnectConfig(queryCtx, pgxConfig) require.NoError(t, err) - rows, err := conn.Query(queryCtx, `SET enable_experimental_stream_replication = true`) + rows, err := conn.Query(queryCtx, `SET CLUSTER SETTING cross_cluster_replication.enabled = true;`) require.NoError(t, err) rows.Close() diff --git a/pkg/cmd/roachtest/cluster.go b/pkg/cmd/roachtest/cluster.go index b6629203a625..2912588e41e6 100644 --- a/pkg/cmd/roachtest/cluster.go +++ b/pkg/cmd/roachtest/cluster.go @@ -1047,14 +1047,14 @@ func (c *clusterImpl) StopCockroachGracefullyOnNode( gracefulOpts.RoachprodOpts.Sig = 15 // SIGTERM gracefulOpts.RoachprodOpts.Wait = true gracefulOpts.RoachprodOpts.MaxWait = 60 - c.Stop(ctx, l, gracefulOpts, c.Node(node)) - // NB: we still call Stop to make sure the process is dead when we try - // to restart it (or we'll catch an error from the RocksDB dir being - // locked). This won't happen unless run with --local due to timing. - c.Stop(ctx, l, option.DefaultStopOpts(), c.Node(node)) - // TODO(tschottdorf): should return an error. I doubt that we want to - // call these *testing.T-style methods on goroutines. - return nil + if err := c.StopE(ctx, l, gracefulOpts, c.Node(node)); err != nil { + return err + } + + // NB: we still call Stop to make sure the process is dead when we + // try to restart it (in case it takes longer than `MaxWait` for it + // to finish). + return c.StopE(ctx, l, option.DefaultStopOpts(), c.Node(node)) } // Save marks the cluster as "saved" so that it doesn't get destroyed. diff --git a/pkg/cmd/roachtest/tests/cluster_to_cluster.go b/pkg/cmd/roachtest/tests/cluster_to_cluster.go index f42aff2ac8cd..dcf348b5dac6 100644 --- a/pkg/cmd/roachtest/tests/cluster_to_cluster.go +++ b/pkg/cmd/roachtest/tests/cluster_to_cluster.go @@ -611,7 +611,7 @@ func srcClusterSettings(t test.Test, db *sqlutils.SQLRunner) { } func destClusterSettings(t test.Test, db *sqlutils.SQLRunner) { - db.ExecMultiple(t, `SET enable_experimental_stream_replication = true;`) + db.ExecMultiple(t, `SET CLUSTER SETTING cross_cluster_replication.enabled = true;`) } func copyPGCertsAndMakeURL( diff --git a/pkg/settings/registry.go b/pkg/settings/registry.go index efbbb3e4e4fd..05844f866d32 100644 --- a/pkg/settings/registry.go +++ b/pkg/settings/registry.go @@ -160,7 +160,8 @@ var retiredSettings = map[string]struct{}{ "changefeed.active_protected_timestamps.enabled": {}, "jobs.scheduler.single_node_scheduler.enabled": {}, // renamed. - "spanconfig.host_coalesce_adjacent.enabled": {}, + "spanconfig.host_coalesce_adjacent.enabled": {}, + "sql.defaults.experimental_stream_replication.enabled": {}, } // sqlDefaultSettings is the list of "grandfathered" existing sql.defaults @@ -182,7 +183,6 @@ var sqlDefaultSettings = map[string]struct{}{ "sql.defaults.experimental_distsql_planning": {}, "sql.defaults.experimental_enable_unique_without_index_constraints.enabled": {}, "sql.defaults.experimental_implicit_column_partitioning.enabled": {}, - "sql.defaults.experimental_stream_replication.enabled": {}, "sql.defaults.experimental_temporary_tables.enabled": {}, "sql.defaults.foreign_key_cascades_limit": {}, "sql.defaults.idle_in_session_timeout": {}, diff --git a/pkg/spanconfig/spanconfigreconciler/BUILD.bazel b/pkg/spanconfig/spanconfigreconciler/BUILD.bazel index 986d4ee63a2a..cfccd98cf5d4 100644 --- a/pkg/spanconfig/spanconfigreconciler/BUILD.bazel +++ b/pkg/spanconfig/spanconfigreconciler/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "//pkg/sql/isql", "//pkg/sql/sqlliveness", "//pkg/util/hlc", + "//pkg/util/iterutil", "//pkg/util/log", "//pkg/util/retry", "//pkg/util/syncutil", diff --git a/pkg/spanconfig/spanconfigreconciler/reconciler.go b/pkg/spanconfig/spanconfigreconciler/reconciler.go index af08674f43e9..b04c3022da8b 100644 --- a/pkg/spanconfig/spanconfigreconciler/reconciler.go +++ b/pkg/spanconfig/spanconfigreconciler/reconciler.go @@ -28,6 +28,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/isql" "github.com/cockroachdb/cockroach/pkg/sql/sqlliveness" "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/iterutil" "github.com/cockroachdb/cockroach/pkg/util/log" "github.com/cockroachdb/cockroach/pkg/util/retry" "github.com/cockroachdb/cockroach/pkg/util/syncutil" @@ -298,6 +299,29 @@ func (f *fullReconciler) reconcile( storeWithLatestSpanConfigs.Apply(ctx, false /* dryrun */, del) } + if !f.codec.ForSystemTenant() { + tenantPrefixKey := f.codec.TenantPrefix() + // We want to ensure tenant ranges do not straddle tenant boundaries. As + // such, a full reconciliation should always include a SpanConfig where the + // start key is keys.MakeTenantPrefix(tenantID). This ensures there is a + // split point right at the start of the tenant's keyspace, so that the + // last range of the previous tenant doesn't straddle across into this + // tenant. Also, sql.CreateTenantRecord relies on such a SpanConfigs + // existence to ensure the same thing for newly created tenants. + if err := storeWithLatestSpanConfigs.Iterate(func(record spanconfig.Record) error { + spanConfigStartKey := record.GetTarget().GetSpan().Key + if tenantPrefixKey.Compare(spanConfigStartKey) != 0 { + return errors.AssertionFailedf( + "tenant prefix key %q does not match first span config start key %q", + tenantPrefixKey, spanConfigStartKey, + ) + } + return iterutil.StopIteration() + }); err != nil { + return nil, hlc.Timestamp{}, err + } + } + return storeWithLatestSpanConfigs, readTimestamp, nil } diff --git a/pkg/sql/BUILD.bazel b/pkg/sql/BUILD.bazel index f56f0f75dc15..521f196eedbf 100644 --- a/pkg/sql/BUILD.bazel +++ b/pkg/sql/BUILD.bazel @@ -666,6 +666,7 @@ go_test( "txn_restart_test.go", "txn_state_test.go", "type_change_test.go", + "unique_without_index_test.go", "unsplit_range_test.go", "unsplit_test.go", "upsert_test.go", diff --git a/pkg/sql/catalog/tabledesc/structured.go b/pkg/sql/catalog/tabledesc/structured.go index 40e42fa27e1a..875c20790404 100644 --- a/pkg/sql/catalog/tabledesc/structured.go +++ b/pkg/sql/catalog/tabledesc/structured.go @@ -276,6 +276,12 @@ func ForEachExprStringInTableDesc(descI catalog.TableDescriptor, f func(expr *st doCheck := func(c *descpb.TableDescriptor_CheckConstraint) error { return f(&c.Expr) } + doUwi := func(uwi *descpb.UniqueWithoutIndexConstraint) error { + if uwi.Predicate != "" { + return f(&uwi.Predicate) + } + return nil + } // Process columns. for i := range desc.Columns { @@ -300,6 +306,13 @@ func ForEachExprStringInTableDesc(descI catalog.TableDescriptor, f func(expr *st } } + // Process uwis. + for i := range desc.UniqueWithoutIndexConstraints { + if err := doUwi(&desc.UniqueWithoutIndexConstraints[i]); err != nil { + return err + } + } + // Process all non-index mutations. for _, mut := range desc.Mutations { if c := mut.GetColumn(); c != nil { @@ -313,6 +326,12 @@ func ForEachExprStringInTableDesc(descI catalog.TableDescriptor, f func(expr *st return err } } + if c := mut.GetConstraint(); c != nil && + c.ConstraintType == descpb.ConstraintToUpdate_UNIQUE_WITHOUT_INDEX { + if err := doUwi(&c.UniqueWithoutIndexConstraint); err != nil { + return err + } + } } return nil } diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index 15f6f9776713..d1f991066f0c 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -512,14 +512,6 @@ var experimentalUseNewSchemaChanger = settings.RegisterEnumSetting( }, ).WithPublic() -var experimentalStreamReplicationEnabled = settings.RegisterBoolSetting( - settings.TenantWritable, - "sql.defaults.experimental_stream_replication.enabled", - "default value for experimental_stream_replication session setting;"+ - "enables the ability to setup a replication stream", - false, -).WithPublic() - var stubCatalogTablesEnabledClusterValue = settings.RegisterBoolSetting( settings.TenantWritable, `sql.defaults.stub_catalog_tables.enabled`, diff --git a/pkg/sql/logictest/testdata/logic_test/information_schema b/pkg/sql/logictest/testdata/logic_test/information_schema index 79783fd52ad1..050851e452c1 100644 --- a/pkg/sql/logictest/testdata/logic_test/information_schema +++ b/pkg/sql/logictest/testdata/logic_test/information_schema @@ -4931,7 +4931,6 @@ disallow_full_table_scans off enable_auto_rehoming off enable_drop_enum_value on enable_experimental_alter_column_type_general off -enable_experimental_stream_replication off enable_implicit_select_for_update on enable_implicit_transaction_for_batch_statements on enable_insert_fast_path on diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog index ee94dd9d7550..fe282b88117e 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_catalog +++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog @@ -2570,7 +2570,6 @@ disallow_full_table_scans off NULL distsql off NULL NULL NULL string enable_auto_rehoming off NULL NULL NULL string enable_experimental_alter_column_type_general off NULL NULL NULL string -enable_experimental_stream_replication off NULL NULL NULL string enable_implicit_select_for_update on NULL NULL NULL string enable_implicit_transaction_for_batch_statements on NULL NULL NULL string enable_insert_fast_path on NULL NULL NULL string @@ -2714,7 +2713,6 @@ disallow_full_table_scans off NULL distsql off NULL user NULL off off enable_auto_rehoming off NULL user NULL off off enable_experimental_alter_column_type_general off NULL user NULL off off -enable_experimental_stream_replication off NULL user NULL off off enable_implicit_select_for_update on NULL user NULL on on enable_implicit_transaction_for_batch_statements on NULL user NULL on on enable_insert_fast_path on NULL user NULL on on @@ -2856,7 +2854,6 @@ distsql NULL NULL NULL distsql_workmem NULL NULL NULL NULL NULL enable_auto_rehoming NULL NULL NULL NULL NULL enable_experimental_alter_column_type_general NULL NULL NULL NULL NULL -enable_experimental_stream_replication NULL NULL NULL NULL NULL enable_implicit_select_for_update NULL NULL NULL NULL NULL enable_implicit_transaction_for_batch_statements NULL NULL NULL NULL NULL enable_insert_fast_path NULL NULL NULL NULL NULL diff --git a/pkg/sql/logictest/testdata/logic_test/schema_change_in_txn b/pkg/sql/logictest/testdata/logic_test/schema_change_in_txn index 6bada7ca76eb..c2037b0e683a 100644 --- a/pkg/sql/logictest/testdata/logic_test/schema_change_in_txn +++ b/pkg/sql/logictest/testdata/logic_test/schema_change_in_txn @@ -1950,3 +1950,50 @@ DROP SCHEMA sc; # Prior to fixing this bug, this commit would hang indefinitely. statement ok COMMIT; + +# This is a regression test to handle the case where a newly created table or +# a previously unleased table are referenced in a transaction involving +# transactional constraint validation. +subtest validate_constraint_referencing_modified_table + +statement ok +CREATE TABLE t1 (id STRING PRIMARY KEY, other_id STRING NOT NULL); +CREATE TABLE t2 (id STRING PRIMARY KEY); +ALTER TABLE t1 ADD CONSTRAINT fk FOREIGN KEY (other_id) REFERENCES t2(id) NOT VALID; + +statement ok +BEGIN; +SET TRANSACTION PRIORITY HIGH; +ALTER TABLE t2 RENAME TO t3; +ALTER TABLE t1 VALIDATE CONSTRAINT fk; +COMMIT; + +statement ok +DROP TABLE t1, t3 CASCADE + +statement ok +CREATE TABLE t1 (id STRING PRIMARY KEY, other_id STRING NOT NULL); +CREATE TABLE t2 (id STRING PRIMARY KEY); +ALTER TABLE t1 ADD CONSTRAINT fk FOREIGN KEY (other_id) REFERENCES t2(id) NOT VALID; + +statement ok +BEGIN; +SET TRANSACTION PRIORITY HIGH; +ALTER TABLE t1 RENAME TO t3; +ALTER TABLE t3 VALIDATE CONSTRAINT fk; +COMMIT; + +statement ok +DROP TABLE t2, t3 CASCADE + +statement ok +BEGIN; +SET TRANSACTION PRIORITY HIGH; +CREATE TABLE t1 (id STRING PRIMARY KEY, other_id STRING NOT NULL); +CREATE TABLE t2 (id STRING PRIMARY KEY); +ALTER TABLE t1 ADD CONSTRAINT fk FOREIGN KEY (other_id) REFERENCES t2(id); +ALTER TABLE t1 VALIDATE CONSTRAINT fk; +COMMIT; + +statement ok +DROP TABLE t1, t2 CASCADE diff --git a/pkg/sql/logictest/testdata/logic_test/sequences b/pkg/sql/logictest/testdata/logic_test/sequences index f9754e6b2b23..3f03b7878908 100644 --- a/pkg/sql/logictest/testdata/logic_test/sequences +++ b/pkg/sql/logictest/testdata/logic_test/sequences @@ -2085,6 +2085,10 @@ CREATE SEQUENCE seqas_error subtest sequence_in_transaction +# We're using explicit transactions, so we might hit retries. + +skip_on_retry + # create a sequence and nextval in the same transaction statement ok BEGIN diff --git a/pkg/sql/logictest/testdata/logic_test/show_source b/pkg/sql/logictest/testdata/logic_test/show_source index d766199fe25a..0241725c4dba 100644 --- a/pkg/sql/logictest/testdata/logic_test/show_source +++ b/pkg/sql/logictest/testdata/logic_test/show_source @@ -57,7 +57,6 @@ disallow_full_table_scans off distsql off enable_auto_rehoming off enable_experimental_alter_column_type_general off -enable_experimental_stream_replication off enable_implicit_select_for_update on enable_implicit_transaction_for_batch_statements on enable_insert_fast_path on diff --git a/pkg/sql/logictest/testdata/logic_test/temp_table b/pkg/sql/logictest/testdata/logic_test/temp_table index 11b103d55dae..762d099916d8 100644 --- a/pkg/sql/logictest/testdata/logic_test/temp_table +++ b/pkg/sql/logictest/testdata/logic_test/temp_table @@ -356,4 +356,44 @@ to_drop.pg_temp.testuser_tmp to_drop.pg_temp.tempuser_view to_drop.pg_temp.root_temp +subtest create_after_discard_and_drop_database + +statement ok +ALTER ROLE testuser WITH CREATEDB; + user testuser + +statement ok +CREATE DATABASE to_drop + +statement ok +USE to_drop + +statement ok +CREATE TEMPORARY TABLE t (i INT PRIMARY KEY); + +statement ok +SELECT * FROM pg_temp.t + +statement ok +DISCARD TEMP + +statement error pgcode 42P01 relation "pg_temp.t" does not exist +SELECT * FROM pg_temp.t + +statement ok +USE defaultdb; +DROP DATABASE to_drop CASCADE; + +statement ok +CREATE DATABASE to_drop + +statement ok +CREATE TEMPORARY TABLE t (i INT PRIMARY KEY); + +statement ok +SELECT * FROM pg_temp.t + +statement ok +USE defaultdb; +DROP DATABASE to_drop CASCADE; diff --git a/pkg/sql/pgwire/conn_test.go b/pkg/sql/pgwire/conn_test.go index 979076c6df82..e61c10ae7fb0 100644 --- a/pkg/sql/pgwire/conn_test.go +++ b/pkg/sql/pgwire/conn_test.go @@ -1593,6 +1593,14 @@ func TestParseClientProvidedSessionParameters(t *testing.T) { _ = c.Ping(ctx) // closing connection immediately, since getSessionArgs is blocking _ = c.Close(ctx) + + select { + case <-c.PgConn().CleanupDone(): + case <-time.After(20 * time.Second): + // 20 seconds was picked because pgconn has an internal deadline of + // 15 seconds when performing the async close request. + t.Error("pgconn asyncClose did not clean up on time") + } }(tc.query) // Wait for the client to connect and perform the handshake. _, args, err := getSessionArgs(ln, true /* trustRemoteAddr */) diff --git a/pkg/sql/schemachanger/scplan/internal/opgen/opgen_unique_without_index_constraint.go b/pkg/sql/schemachanger/scplan/internal/opgen/opgen_unique_without_index_constraint.go index 1155d15c1ae4..76c868d55f6a 100644 --- a/pkg/sql/schemachanger/scplan/internal/opgen/opgen_unique_without_index_constraint.go +++ b/pkg/sql/schemachanger/scplan/internal/opgen/opgen_unique_without_index_constraint.go @@ -44,15 +44,6 @@ func init() { BackReferencedTableID: this.TableID, } }), - emit(func(this *scpb.UniqueWithoutIndexConstraint) *scop.UpdateTableBackReferencesInSequences { - if this.Predicate == nil || this.Predicate.UsesSequenceIDs == nil || len(this.Predicate.UsesSequenceIDs) == 0 { - return nil - } - return &scop.UpdateTableBackReferencesInSequences{ - SequenceIDs: this.Predicate.UsesSequenceIDs, - BackReferencedTableID: this.TableID, - } - }), ), to(scpb.Status_VALIDATED, emit(func(this *scpb.UniqueWithoutIndexConstraint) *scop.ValidateConstraint { @@ -92,6 +83,15 @@ func init() { ConstraintID: this.ConstraintID, } }), + emit(func(this *scpb.UniqueWithoutIndexConstraint) *scop.UpdateTableBackReferencesInTypes { + if this.Predicate == nil || this.Predicate.UsesTypeIDs == nil || len(this.Predicate.UsesTypeIDs) == 0 { + return nil + } + return &scop.UpdateTableBackReferencesInTypes{ + TypeIDs: this.Predicate.UsesTypeIDs, + BackReferencedTableID: this.TableID, + } + }), ), ), ) diff --git a/pkg/sql/tenant_creation.go b/pkg/sql/tenant_creation.go index 7e6e25d57dcd..442b08f90197 100644 --- a/pkg/sql/tenant_creation.go +++ b/pkg/sql/tenant_creation.go @@ -396,27 +396,65 @@ func CreateTenantRecord( // This adds a split at the start of the tenant keyspace. tenantPrefix := keys.MakeTenantPrefix(tenantID) - startRecord, err := spanconfig.MakeRecord(spanconfig.MakeTargetFromSpan(roachpb.Span{ + startRecordTarget := spanconfig.MakeTargetFromSpan(roachpb.Span{ Key: tenantPrefix, EndKey: tenantPrefix.Next(), - }), tenantSpanConfig) + }) + startRecord, err := spanconfig.MakeRecord(startRecordTarget, tenantSpanConfig) if err != nil { return roachpb.TenantID{}, err } - - // This adds a split at the end of the tenant keyspace. This split would - // eventually be created when the next tenant is created, but until then - // this tenant's EndKey will be /Max which is outside of it's keyspace. + toUpsert := []spanconfig.Record{startRecord} + + // We want to ensure we have a split at the non-inclusive end of the tenant's + // keyspace, which also happens to be the inclusive start of the next + // tenant's (with ID=ours+1). If we didn't do anything here, and we were the + // tenant with the highest ID thus far, our last range would extend to /Max. + // If a tenant with a higher ID was created, when installing its initial span + // config record, it would carve itself off (and our last range would only + // extend to that next tenant's boundary), but this is a bit awkward for code + // that wants to rely on the invariant that ranges for a tenant only extend + // to the tenant's well-defined end key. + // + // So how do we ensure this split at this tenant's non-inclusive end key? + // Hard splits are controlled by the start keys on span config records[^1], + // so we'll try insert one accordingly. But we cannot do this blindly. We + // cannot assume that tenants are created in ID order, so it's possible that + // the tenant with the next ID is already present + running. If so, it may + // already have span config records that start at the key at which we want + // to write a span config record for. Over-writing it blindly would be a + // mistake -- there's no telling what config that next tenant has associated + // for that span. So we'll do something simple -- we'll check transactionally + // whether there's anything written already, and if so, do nothing. We + // already have the split we need. + // + // [^1]: See ComputeSplitKey in spanconfig.StoreReader. tenantPrefixEnd := tenantPrefix.PrefixEnd() - endRecord, err := spanconfig.MakeRecord(spanconfig.MakeTargetFromSpan(roachpb.Span{ + endRecordTarget := spanconfig.MakeTargetFromSpan(roachpb.Span{ Key: tenantPrefixEnd, EndKey: tenantPrefixEnd.Next(), - }), tenantSpanConfig) + }) + + // Check if a record exists for the next tenant's startKey from when the next + // tenant was created. The current tenant's endRecordTarget is the same as + // the next tenant's startRecordTarget. + records, err := spanConfigs.GetSpanConfigRecords(ctx, []spanconfig.Target{endRecordTarget}) if err != nil { return roachpb.TenantID{}, err } - toUpsert := []spanconfig.Record{startRecord, endRecord} + // If the next tenant's startKey record exists then do not split at the + // current tenant's endKey. Doing will incorrectly overwrite the next + // tenant's first span config. + // See: https://github.com/cockroachdb/cockroach/issues/95882 + if len(records) == 0 { + endRecord, err := spanconfig.MakeRecord(endRecordTarget, tenantSpanConfig) + if err != nil { + return roachpb.TenantID{}, err + } + toUpsert = append(toUpsert, endRecord) + } + return tenantID, spanConfigs.UpdateSpanConfigRecords( ctx, nil, toUpsert, hlc.MinTimestamp, hlc.MaxTimestamp, ) diff --git a/pkg/sql/unique_without_index_test.go b/pkg/sql/unique_without_index_test.go new file mode 100644 index 000000000000..9e973773122a --- /dev/null +++ b/pkg/sql/unique_without_index_test.go @@ -0,0 +1,73 @@ +// Copyright 2023 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package sql + +import ( + "context" + "testing" + + "github.com/cockroachdb/cockroach/pkg/base" + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/desctestutils" + "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/stretchr/testify/require" +) + +// TestUWIConstraintReferencingTypes tests that adding/dropping +// unique without index constraints that reference other type descriptors +// properly adds/drops back-references in the type descriptor. +func TestUWIConstraintReferencingTypes(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + ctx := context.Background() + + testutils.RunTrueAndFalse(t, "test-in-both-legacy-and-declarative-schema-changer", func( + t *testing.T, useDeclarativeSchemaChanger bool, + ) { + s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) + defer s.Stopper().Stop(ctx) + tdb := sqlutils.MakeSQLRunner(sqlDB) + + if useDeclarativeSchemaChanger { + tdb.Exec(t, "SET use_declarative_schema_changer = on;") + } else { + tdb.Exec(t, "SET use_declarative_schema_changer = off;") + } + tdb.Exec(t, "SET experimental_enable_unique_without_index_constraints = true;") + tdb.Exec(t, "CREATE TYPE typ AS ENUM ('a', 'b');") + tdb.Exec(t, "CREATE TABLE t (i INT PRIMARY KEY, j STRING);") + tdb.Exec(t, "ALTER TABLE t ADD UNIQUE WITHOUT INDEX (j) WHERE (j::typ != 'a');") + + // Ensure that `typ` has a back-reference to table `t`. + tableDesc := desctestutils.TestingGetPublicTableDescriptor(kvDB, keys.SystemSQLCodec, + "defaultdb", "t") + typDesc := desctestutils.TestingGetPublicTypeDescriptor(kvDB, keys.SystemSQLCodec, + "defaultdb", "typ") + require.Equal(t, []descpb.ID{tableDesc.GetID()}, typDesc.GetReferencingDescriptorIDs()) + + // Ensure that dropping `typ` fails because `typ` is referenced by the constraint. + tdb.ExpectErr(t, `pq: cannot drop type "typ" because other objects \(\[defaultdb.public.t\]\) still depend on it`, "DROP TYPE typ") + + // Ensure that dropping the constraint removes the back-reference from `typ`. + tdb.Exec(t, "ALTER TABLE t DROP CONSTRAINT unique_j") + typDesc = desctestutils.TestingGetPublicTypeDescriptor(kvDB, keys.SystemSQLCodec, + "defaultdb", "typ") + require.Nil(t, typDesc.GetReferencingDescriptorIDs()) + + // Ensure that now we can succeed dropping `typ`. + tdb.Exec(t, "DROP TYPE typ") + }) +} diff --git a/pkg/sql/vars.go b/pkg/sql/vars.go index f0648b65938b..b98e10818fb0 100644 --- a/pkg/sql/vars.go +++ b/pkg/sql/vars.go @@ -1745,24 +1745,6 @@ var varGen = map[string]sessionVar{ }, }, - `enable_experimental_stream_replication`: { - GetStringVal: makePostgresBoolGetStringValFn(`enable_experimental_stream_replication`), - Set: func(_ context.Context, m sessionDataMutator, s string) error { - b, err := paramparse.ParseBoolVar(`enable_experimental_stream_replication`, s) - if err != nil { - return err - } - m.SetStreamReplicationEnabled(b) - return nil - }, - Get: func(evalCtx *extendedEvalContext, _ *kv.Txn) (string, error) { - return formatBoolAsPostgresSetting(evalCtx.SessionData().EnableStreamReplication), nil - }, - GlobalDefault: func(sv *settings.Values) string { - return formatBoolAsPostgresSetting(experimentalStreamReplicationEnabled.Get(sv)) - }, - }, - // CockroachDB extension. See experimentalComputedColumnRewrites or // ParseComputedColumnRewrites for a description of the format. `experimental_computed_column_rewrites`: { diff --git a/pkg/ts/catalog/chart_catalog.go b/pkg/ts/catalog/chart_catalog.go index 265ca532f0f9..0a099065b693 100644 --- a/pkg/ts/catalog/chart_catalog.go +++ b/pkg/ts/catalog/chart_catalog.go @@ -1614,9 +1614,9 @@ var charts = []sectionDescription{ }, }, { - Title: "Ingested Bytes", + Title: "Logical Bytes", Metrics: []string{ - "replication.ingested_bytes", + "replication.logical_bytes", }, }, { diff --git a/pkg/ui/workspaces/db-console/src/redux/clusterSettings/clusterSettings.selectors.ts b/pkg/ui/workspaces/db-console/src/redux/clusterSettings/clusterSettings.selectors.ts index 62d0e3601e27..efb446a5bed4 100644 --- a/pkg/ui/workspaces/db-console/src/redux/clusterSettings/clusterSettings.selectors.ts +++ b/pkg/ui/workspaces/db-console/src/redux/clusterSettings/clusterSettings.selectors.ts @@ -52,3 +52,14 @@ export const selectAutomaticStatsCollectionEnabled = createSelector( return value === "true"; }, ); + +export const selectCrossClusterReplicationEnabled = createSelector( + selectClusterSettings, + (settings): boolean | undefined => { + if (!settings) { + return undefined; + } + const value = settings["cross_cluster_replication.enabled"]?.value; + return value === "true"; + }, +); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/crossClusterReplication.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/crossClusterReplication.tsx new file mode 100644 index 000000000000..3c02089335a5 --- /dev/null +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/dashboards/crossClusterReplication.tsx @@ -0,0 +1,63 @@ +// Copyright 2023 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +import React from "react"; + +import { LineGraph } from "src/views/cluster/components/linegraph"; +import { Metric, Axis } from "src/views/shared/components/metricQuery"; +import { AxisUnits } from "@cockroachlabs/cluster-ui"; + +import { GraphDashboardProps } from "./dashboardUtils"; + +export default function (props: GraphDashboardProps) { + const { storeSources } = props; + + return [ + + + + + , + + + + + , + + + + + , + ]; +} diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx index 57b1f60d4b3a..81bd8711b389 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx @@ -64,6 +64,7 @@ import hardwareDashboard from "./dashboards/hardware"; import changefeedsDashboard from "./dashboards/changefeeds"; import overloadDashboard from "./dashboards/overload"; import ttlDashboard from "./dashboards/ttl"; +import crossClusterReplicationDashboard from "./dashboards/crossClusterReplication"; import { getMatchParamByName } from "src/util/query"; import { PayloadAction } from "src/interfaces/action"; import { @@ -84,7 +85,9 @@ import moment from "moment"; import { selectResolution10sStorageTTL, selectResolution30mStorageTTL, + selectCrossClusterReplicationEnabled, } from "src/redux/clusterSettings"; + interface GraphDashboard { label: string; component: (props: GraphDashboardProps) => React.ReactElement[]; @@ -103,6 +106,10 @@ const dashboards: { [key: string]: GraphDashboard } = { changefeeds: { label: "Changefeeds", component: changefeedsDashboard }, overload: { label: "Overload", component: overloadDashboard }, ttl: { label: "TTL", component: ttlDashboard }, + crossClusterReplication: { + label: "Cross-Cluster Replication", + component: crossClusterReplicationDashboard, + }, }; const defaultDashboard = "overview"; @@ -127,6 +134,7 @@ type MapStateToProps = { nodeDisplayNameByID: ReturnType< typeof nodeDisplayNameByIDSelector.resultFunc >; + crossClusterReplicationEnabled: boolean; }; type MapDispatchToProps = { @@ -166,6 +174,7 @@ export class NodeGraphs extends React.Component< refresh = () => { this.props.refreshNodes(); this.props.refreshLiveness(); + this.props.refreshNodeSettings(); }; setClusterPath(nodeID: string, dashboardName: string) { @@ -317,6 +326,12 @@ export class NodeGraphs extends React.Component< const paddingBottom = nodeIDs.length > 8 ? 90 + Math.ceil(nodeIDs.length / 3) * 10 : 50; + const filteredDropdownOptions = this.props.crossClusterReplicationEnabled + ? dashboardDropdownOptions // Already in the list, no need to filter + : dashboardDropdownOptions.filter( + option => option.label !== "Cross-Cluster Replication", + ); + return (
@@ -333,7 +348,7 @@ export class NodeGraphs extends React.Component< ({ storeIDsByNodeID: selectStoreIDsByNodeID(state), nodeDropdownOptions: nodeDropdownOptionsSelector(state), nodeDisplayNameByID: nodeDisplayNameByIDSelector(state), + crossClusterReplicationEnabled: selectCrossClusterReplicationEnabled(state), }); const mapDispatchToProps: MapDispatchToProps = {