diff --git a/pkg/cmd/roachtest/tests/BUILD.bazel b/pkg/cmd/roachtest/tests/BUILD.bazel index 74d51884652b..2c151655146e 100644 --- a/pkg/cmd/roachtest/tests/BUILD.bazel +++ b/pkg/cmd/roachtest/tests/BUILD.bazel @@ -96,6 +96,7 @@ go_library( "mixed_version_schemachange.go", "multitenant.go", "multitenant_distsql.go", + "multitenant_shared_process.go", "multitenant_tpch.go", "multitenant_upgrade.go", "multitenant_utils.go", diff --git a/pkg/cmd/roachtest/tests/cluster_to_cluster.go b/pkg/cmd/roachtest/tests/cluster_to_cluster.go index a98935ae0dae..f42aff2ac8cd 100644 --- a/pkg/cmd/roachtest/tests/cluster_to_cluster.go +++ b/pkg/cmd/roachtest/tests/cluster_to_cluster.go @@ -219,10 +219,14 @@ func setupC2C( srcClusterSettings(t, srcSQL) destClusterSettings(t, destSQL) + createTenantAdminRole(t, "src-system", srcSQL) + createTenantAdminRole(t, "dst-system", destSQL) + srcTenantID, destTenantID := 2, 2 srcTenantName := "src-tenant" destTenantName := "destination-tenant" - srcSQL.Exec(t, fmt.Sprintf(`CREATE TENANT %q`, srcTenantName)) + + createInMemoryTenant(ctx, t, c, srcTenantName, srcCluster, true) pgURL, err := copyPGCertsAndMakeURL(ctx, t, c, srcNode, srcClusterSetting.PGUrlCertsDir, addr[0]) require.NoError(t, err) @@ -241,18 +245,6 @@ func setupC2C( db: destDB, nodes: dstCluster} - // Currently, a tenant has by default a 10m RU burst limit, which can be - // reached during these tests. To prevent RU limit throttling, add 10B RUs to - // the tenant. - srcTenantInfo.sql.Exec(t, `SELECT crdb_internal.update_tenant_resource_limits($1, 10000000000, 0, -10000000000, now(), 0);`, srcTenantInfo.ID) - - createSystemRole(t, srcTenantInfo.name+" system tenant", srcTenantInfo.sql) - createSystemRole(t, srcTenantInfo.name+" system tenant", destTenantInfo.sql) - - srcTenantDB := c.Conn(ctx, t.L(), srcNode[0], option.TenantName(srcTenantName)) - srcTenantSQL := sqlutils.MakeSQLRunner(srcTenantDB) - createSystemRole(t, destTenantInfo.name+" app tenant", srcTenantSQL) return &c2cSetup{ src: srcTenantInfo, dst: destTenantInfo, @@ -260,16 +252,6 @@ func setupC2C( metrics: c2cMetrics{}} } -// createSystemRole creates a role that can be used to log into the cluster's db console -func createSystemRole(t test.Test, name string, sql *sqlutils.SQLRunner) { - username := "secure" - password := "roach" - sql.Exec(t, fmt.Sprintf(`CREATE ROLE %s WITH LOGIN PASSWORD '%s'`, username, password)) - sql.Exec(t, fmt.Sprintf(`GRANT ADMIN TO %s`, username)) - t.L().Printf(`Log into the %s db console with username "%s" and password "%s"`, - name, username, password) -} - type streamingWorkload interface { // sourceInitCmd returns a command that will populate the src cluster with data before the // replication stream begins diff --git a/pkg/cmd/roachtest/tests/multitenant_shared_process.go b/pkg/cmd/roachtest/tests/multitenant_shared_process.go new file mode 100644 index 000000000000..15bb67da19e5 --- /dev/null +++ b/pkg/cmd/roachtest/tests/multitenant_shared_process.go @@ -0,0 +1,68 @@ +// 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 tests + +import ( + "context" + "fmt" + "time" + + "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/cluster" + "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/option" + "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/registry" + "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/test" + "github.com/cockroachdb/cockroach/pkg/roachprod/install" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" +) + +func registerMultiTenantSharedProcess(r registry.Registry) { + crdbNodeCount := 4 + + r.Add(registry.TestSpec{ + Name: "multitenant/shared-process/basic", + Owner: registry.OwnerMultiTenant, + Cluster: r.MakeClusterSpec(crdbNodeCount + 1), + Timeout: 1 * time.Hour, + Run: func(ctx context.Context, t test.Test, c cluster.Cluster) { + var ( + appTenantName = "app" + tpccWarehouses = 500 + crdbNodes = c.Range(1, crdbNodeCount) + workloadNode = c.Node(crdbNodeCount + 1) + ) + t.Status(`set up Unified Architecture Cluster`) + c.Put(ctx, t.Cockroach(), "./cockroach", crdbNodes) + c.Put(ctx, t.DeprecatedWorkload(), "./workload", workloadNode) + + // In order to observe the app tenant's db console, create a secure + // cluster and add Admin roles to the system and app tenant. + clusterSettings := install.MakeClusterSettings(install.SecureOption(true)) + c.Start(ctx, t.L(), option.DefaultStartOpts(), clusterSettings, crdbNodes) + + sysConn := c.Conn(ctx, t.L(), crdbNodes.RandNode()[0]) + sysSQL := sqlutils.MakeSQLRunner(sysConn) + + createTenantAdminRole(t, "system", sysSQL) + + createInMemoryTenant(ctx, t, c, appTenantName, crdbNodes, true) + + t.Status(`initialize tpcc workload`) + initCmd := fmt.Sprintf(`./workload init tpcc --data-loader import --warehouses %d {pgurl%s:%s}`, + tpccWarehouses, crdbNodes, appTenantName) + c.Run(ctx, workloadNode, initCmd) + + t.Status(`run tpcc workload`) + runCmd := fmt.Sprintf(`./workload run tpcc --warehouses %d --tolerate-errors --duration 10m {pgurl%s:%s}`, + tpccWarehouses, crdbNodes, appTenantName) + c.Run(ctx, workloadNode, runCmd) + }, + }) +} diff --git a/pkg/cmd/roachtest/tests/multitenant_utils.go b/pkg/cmd/roachtest/tests/multitenant_utils.go index c57b102b92bc..f604b530e546 100644 --- a/pkg/cmd/roachtest/tests/multitenant_utils.go +++ b/pkg/cmd/roachtest/tests/multitenant_utils.go @@ -28,6 +28,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/roachprod/config" "github.com/cockroachdb/cockroach/pkg/security/username" "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" "github.com/stretchr/testify/require" ) @@ -321,3 +322,57 @@ func newTenantInstance( c.Run(ctx, c.Node(node), "chmod", "0600", filepath.Join("certs", key)) return &inst, nil } + +// createTenantAdminRole creates a role that can be used to log into a secure cluster's db console. +func createTenantAdminRole(t test.Test, tenantName string, tenantSQL *sqlutils.SQLRunner) { + username := "secure" + password := "roach" + tenantSQL.Exec(t, fmt.Sprintf(`CREATE ROLE %s WITH LOGIN PASSWORD '%s'`, username, password)) + tenantSQL.Exec(t, fmt.Sprintf(`GRANT ADMIN TO %s`, username)) + t.L().Printf(`Log into %s db console with username "%s" and password "%s"`, + tenantName, username, password) +} + +// createInMemoryTenant runs through the necessary steps to create an in-memory tenant without +// resource limits. +func createInMemoryTenant( + ctx context.Context, + t test.Test, + c cluster.Cluster, + tenantName string, + nodes option.NodeListOption, + secure bool, +) { + sysSQL := sqlutils.MakeSQLRunner(c.Conn(ctx, t.L(), nodes.RandNode()[0])) + sysSQL.Exec(t, "CREATE TENANT $1", tenantName) + sysSQL.Exec(t, "ALTER TENANT $1 START SERVICE SHARED", tenantName) + + // Opening a SQL session to a newly created in-process tenant may require a + // few retries. Unfortunately, the c.ConnE and MakeSQLRunner APIs do not make + // it clear if they eagerly open a session with the tenant or wait until the + // first query. Therefore, wrap connection opening and a ping to the tenant + // server in a retry loop. + var tenantSQL *sqlutils.SQLRunner + testutils.SucceedsSoon(t, func() error { + tenantConn, err := c.ConnE(ctx, t.L(), nodes.RandNode()[0]) + if err != nil { + return err + } + if err := tenantConn.Ping(); err != nil { + return err + } + tenantSQL = sqlutils.MakeSQLRunner(tenantConn) + return nil + }) + + // Currently, a tenant has by default a 10m RU burst limit, which can be + // reached during these tests. To prevent RU limit throttling, add 10B RUs to + // the tenant. + var tenantID int + sysSQL.QueryRow(t, `SELECT id FROM [SHOW TENANT $1]`, tenantName).Scan(&tenantID) + sysSQL.Exec(t, `SELECT crdb_internal.update_tenant_resource_limits($1, 10000000000, 0, +10000000000, now(), 0);`, tenantID) + if secure { + createTenantAdminRole(t, tenantName, tenantSQL) + } +} diff --git a/pkg/cmd/roachtest/tests/registry.go b/pkg/cmd/roachtest/tests/registry.go index 7ebf3a43c6f3..29038c96c5ba 100644 --- a/pkg/cmd/roachtest/tests/registry.go +++ b/pkg/cmd/roachtest/tests/registry.go @@ -87,6 +87,7 @@ func RegisterTests(r registry.Registry) { registerMultiTenantDistSQL(r) registerMultiTenantTPCH(r) registerMultiTenantUpgrade(r) + registerMultiTenantSharedProcess(r) registerNetwork(r) registerNodeJSPostgres(r) registerPebbleWriteThroughput(r) diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index 0fe94c9a6921..3167f884b9ca 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -6429,6 +6429,9 @@ Parameters:` + randgencfg.ConfigDoc, ), // Used to configure the tenant token bucket. See UpdateTenantResourceLimits. + // + // TODO(multitenantTeam): use tenantName instead of tenantID. See issue: + // https://github.com/cockroachdb/cockroach/issues/96176 "crdb_internal.update_tenant_resource_limits": makeBuiltin( tree.FunctionProperties{ Category: builtinconstants.CategoryMultiTenancy,