diff --git a/DEPS.bzl b/DEPS.bzl index 4c95bcc518fb..0e7641b251ec 100644 --- a/DEPS.bzl +++ b/DEPS.bzl @@ -1198,16 +1198,6 @@ def go_deps(): "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/census-instrumentation/opencensus-proto/com_github_census_instrumentation_opencensus_proto-v0.2.1.zip", ], ) - go_repository( - name = "com_github_certifi_gocertifi", - build_file_proto_mode = "disable_global", - importpath = "github.com/certifi/gocertifi", - sha256 = "11d525844c3dd711fb0ae31acc9ebd8a4d602215f14ff24ad1764ecb48464849", - strip_prefix = "github.com/certifi/gocertifi@v0.0.0-20200922220541-2c3bb06c6054", - urls = [ - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/certifi/gocertifi/com_github_certifi_gocertifi-v0.0.0-20200922220541-2c3bb06c6054.zip", - ], - ) go_repository( name = "com_github_cespare_xxhash", build_file_proto_mode = "disable_global", @@ -2812,16 +2802,6 @@ def go_deps(): "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/getkin/kin-openapi/com_github_getkin_kin_openapi-v0.53.0.zip", ], ) - go_repository( - name = "com_github_getsentry_raven_go", - build_file_proto_mode = "disable_global", - importpath = "github.com/getsentry/raven-go", - sha256 = "eaffe69939612cd05f95e1846b8ddb4043655571be34cdb6412a66b41b6826eb", - strip_prefix = "github.com/getsentry/raven-go@v0.2.0", - urls = [ - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/getsentry/raven-go/com_github_getsentry_raven_go-v0.2.0.zip", - ], - ) go_repository( name = "com_github_getsentry_sentry_go", build_file_proto_mode = "disable_global", @@ -8054,10 +8034,10 @@ def go_deps(): name = "com_github_stretchr_testify", build_file_proto_mode = "disable_global", importpath = "github.com/stretchr/testify", - sha256 = "36f64e4f229f87672ef8de1c756648c4165e76abd034362517578397e824856c", - strip_prefix = "github.com/stretchr/testify@v1.8.1", + sha256 = "400e18c88e5c4beb7ecca5d675048f1915a6e675b30fc03f8f563eb4dfde079a", + strip_prefix = "github.com/stretchr/testify@v1.8.2", urls = [ - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/stretchr/testify/com_github_stretchr_testify-v1.8.1.zip", + "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/stretchr/testify/com_github_stretchr_testify-v1.8.2.zip", ], ) go_repository( @@ -10341,10 +10321,10 @@ def go_deps(): ], build_file_proto_mode = "default", importpath = "go.etcd.io/raft/v3", - sha256 = "b27a4c7ddef64664745eaa5c817c80f3a7dc0d07d3b7837c02fde20993861ac7", - strip_prefix = "go.etcd.io/raft/v3@v3.0.0-20221221215055-65a0bf3b6779", + sha256 = "a20a4dc3311336d6cf64bf8a436a6bed8095748733af309435df8de3b794e36f", + strip_prefix = "go.etcd.io/raft/v3@v3.0.0-20230315220435-5fe1c31c5158", urls = [ - "https://storage.googleapis.com/cockroach-godeps/gomod/go.etcd.io/raft/v3/io_etcd_go_raft_v3-v3.0.0-20221221215055-65a0bf3b6779.zip", + "https://storage.googleapis.com/cockroach-godeps/gomod/go.etcd.io/raft/v3/io_etcd_go_raft_v3-v3.0.0-20230315220435-5fe1c31c5158.zip", ], ) go_repository( diff --git a/build/bazelutil/distdir_files.bzl b/build/bazelutil/distdir_files.bzl index 9a0a8bb15911..ddea3b1c744c 100644 --- a/build/bazelutil/distdir_files.bzl +++ b/build/bazelutil/distdir_files.bzl @@ -280,7 +280,6 @@ DISTDIR_FILES = { "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cenkalti/backoff/com_github_cenkalti_backoff-v2.2.1+incompatible.zip": "f8196815a1b4d25e5b8158029d5264801fc8aa5ff128ccf30752fd169693d43b", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cenkalti/backoff/v4/com_github_cenkalti_backoff_v4-v4.1.3.zip": "73ff572a901c0307aa1c16db43812da7ca2555aa403cfdd9d3a239ecbdad2274", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/census-instrumentation/opencensus-proto/com_github_census_instrumentation_opencensus_proto-v0.2.1.zip": "b3c09f3e635d47b4138695a547d1f2c7138f382cbe5a8b5865b66a8e08233461", - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/certifi/gocertifi/com_github_certifi_gocertifi-v0.0.0-20200922220541-2c3bb06c6054.zip": "11d525844c3dd711fb0ae31acc9ebd8a4d602215f14ff24ad1764ecb48464849", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cespare/xxhash/com_github_cespare_xxhash-v1.1.0.zip": "fe98c56670b21631f7fd3305a29a3b17e86a6cce3876a2119460717a18538e2e", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cespare/xxhash/v2/com_github_cespare_xxhash_v2-v2.2.0.zip": "fc180cdb0c00fbffbd39b774a72cdb5f0c32ace25370d5135195918a8c3fbd25", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/charmbracelet/bubbles/com_github_charmbracelet_bubbles-v0.15.1-0.20230123181021-a6a12c4a31eb.zip": "72954af77ec32995cfdf218fd31e9357a0fbef96f252bb1a9e6f0b8f158d3531", @@ -434,7 +433,6 @@ DISTDIR_FILES = { "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/garyburd/redigo/com_github_garyburd_redigo-v0.0.0-20150301180006-535138d7bcd7.zip": "7ed5f8194388955d2f086c170960cb096ee28d421b32bd12328d5f2a2b0ad488", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/gavv/httpexpect/com_github_gavv_httpexpect-v2.0.0+incompatible.zip": "3db05c59a5c70d11b9452727c529be6934ddf8b42f4bfdc3138441055f1529b1", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/getkin/kin-openapi/com_github_getkin_kin_openapi-v0.53.0.zip": "e3a00cb5828f8922087a0a74aad06c6177fa2eab44763a19aeec38f7fab7834b", - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/getsentry/raven-go/com_github_getsentry_raven_go-v0.2.0.zip": "eaffe69939612cd05f95e1846b8ddb4043655571be34cdb6412a66b41b6826eb", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/getsentry/sentry-go/com_github_getsentry_sentry_go-v0.12.0.zip": "1d127ea620f897c64de60982882cea34a4bb9c1481a9c9a379226af7cfa454ba", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/ghemawat/stream/com_github_ghemawat_stream-v0.0.0-20171120220530-696b145b53b9.zip": "9c0a42cacc8e22024b58db15127886a6f8ddbcfbf89d4d062bfdc43dc40d80d5", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/ghodss/yaml/com_github_ghodss_yaml-v1.0.0.zip": "c3f295d23c02c0b35e4d3b29053586e737cf9642df9615da99c0bda9bbacc624", @@ -928,7 +926,7 @@ DISTDIR_FILES = { "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/streadway/handy/com_github_streadway_handy-v0.0.0-20190108123426-d5acb3125c2a.zip": "f770ed96081220a9cbc5e975a06c2858b4f3d02820cb9902982116af491b171f", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/streadway/quantile/com_github_streadway_quantile-v0.0.0-20150917103942-b0c588724d25.zip": "45156bab62475784e2eacb349570c86bcf245a84d97825ce9ee2bf604a4438d5", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/stretchr/objx/com_github_stretchr_objx-v0.5.0.zip": "1a00b3bb5ad41cb72634ace06b7eb7df857404d77a7cab4e401a7c729561fe4c", - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/stretchr/testify/com_github_stretchr_testify-v1.8.1.zip": "36f64e4f229f87672ef8de1c756648c4165e76abd034362517578397e824856c", + "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/stretchr/testify/com_github_stretchr_testify-v1.8.2.zip": "400e18c88e5c4beb7ecca5d675048f1915a6e675b30fc03f8f563eb4dfde079a", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/subosito/gotenv/com_github_subosito_gotenv-v1.2.0.zip": "21474df92536f36de6f91dfbf466995289445cc4e5a5900d9c40ae8776b8b0cf", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/syndtr/gocapability/com_github_syndtr_gocapability-v0.0.0-20200815063812-42c35b437635.zip": "91ff91da1936e17aa68fc13756e40ba4db1d7c9375a4ef0969fe19c9aa281195", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/tchap/go-patricia/com_github_tchap_go_patricia-v2.2.6+incompatible.zip": "948494017eae153a8c2d4ae9b450fd42abcb2578211f1c28e69ab71a2f27814d", @@ -998,7 +996,7 @@ DISTDIR_FILES = { "https://storage.googleapis.com/cockroach-godeps/gomod/go.etcd.io/etcd/client/pkg/v3/io_etcd_go_etcd_client_pkg_v3-v3.5.0.zip": "c0ca209767c5734c6ed023888ba5be02aab5bd3c4d018999467f2bfa8bf65ee3", "https://storage.googleapis.com/cockroach-godeps/gomod/go.etcd.io/etcd/client/v2/io_etcd_go_etcd_client_v2-v2.305.0.zip": "91fcb507fe8c193844b56bfb6c8741aaeb6ffa11ee9043de2af0f141173679f3", "https://storage.googleapis.com/cockroach-godeps/gomod/go.etcd.io/etcd/io_etcd_go_etcd-v0.5.0-alpha.5.0.20200910180754-dd1b699fc489.zip": "d982ee501979b41b68625693bad77d15e4ae79ab9d0eae5f6028205f96a74e49", - "https://storage.googleapis.com/cockroach-godeps/gomod/go.etcd.io/raft/v3/io_etcd_go_raft_v3-v3.0.0-20221221215055-65a0bf3b6779.zip": "b27a4c7ddef64664745eaa5c817c80f3a7dc0d07d3b7837c02fde20993861ac7", + "https://storage.googleapis.com/cockroach-godeps/gomod/go.etcd.io/raft/v3/io_etcd_go_raft_v3-v3.0.0-20230315220435-5fe1c31c5158.zip": "a20a4dc3311336d6cf64bf8a436a6bed8095748733af309435df8de3b794e36f", "https://storage.googleapis.com/cockroach-godeps/gomod/go.mongodb.org/mongo-driver/org_mongodb_go_mongo_driver-v1.5.1.zip": "446cff132e82c64af7ffcf48e268eb16ec81f694914aa6baecb06cbbae1be0d7", "https://storage.googleapis.com/cockroach-godeps/gomod/go.mozilla.org/pkcs7/org_mozilla_go_pkcs7-v0.0.0-20200128120323-432b2356ecb1.zip": "3c4c1667907ff3127e371d44696326bad9e965216d4257917ae28e8b82a9e08d", "https://storage.googleapis.com/cockroach-godeps/gomod/go.opencensus.io/io_opencensus_go-v0.24.0.zip": "203a767d7f8e7c1ebe5588220ad168d1e15b14ae70a636de7ca9a4a88a7e0d0c", diff --git a/go.mod b/go.mod index 4a24a97ef224..28de05fa062f 100644 --- a/go.mod +++ b/go.mod @@ -205,14 +205,14 @@ require ( github.com/spf13/afero v1.6.0 github.com/spf13/cobra v1.2.1 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.1 + github.com/stretchr/testify v1.8.2 github.com/twpayne/go-geom v1.4.2 github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad github.com/xdg-go/pbkdf2 v1.0.0 github.com/xdg-go/scram v1.1.2 github.com/xdg-go/stringprep v1.0.4 github.com/zabawaba99/go-gitignore v0.0.0-20200117185801-39e6bddfb292 - go.etcd.io/raft/v3 v3.0.0-20221221215055-65a0bf3b6779 + go.etcd.io/raft/v3 v3.0.0-20230315220435-5fe1c31c5158 go.opentelemetry.io/otel v1.0.0-RC3 go.opentelemetry.io/otel/exporters/jaeger v1.0.0-RC3 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.0.0-RC3 diff --git a/go.sum b/go.sum index f9f68a1c0b6d..648eb4feaf1b 100644 --- a/go.sum +++ b/go.sum @@ -2146,8 +2146,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -2277,8 +2278,8 @@ go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3C go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/raft/v3 v3.0.0-20221221215055-65a0bf3b6779 h1:GS8kfvdZ03yoCzHjYjZ2FV0CaNqhcnaAPjn0IHf565E= -go.etcd.io/raft/v3 v3.0.0-20221221215055-65a0bf3b6779/go.mod h1:eMshmuwXLWZrjHXN8ZgYrOMQRSbHqi5M84DEZWhG+o4= +go.etcd.io/raft/v3 v3.0.0-20230315220435-5fe1c31c5158 h1:JdVbiz8TIDFV4pyXRQ+z7dcJueHcqB4NwRGm7tnr4wo= +go.etcd.io/raft/v3 v3.0.0-20230315220435-5fe1c31c5158/go.mod h1:xa/jfCF4K9FpWEAXstT+Nw4ToWdmhJ8oeC5eburtypA= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= diff --git a/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go b/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go index 00327803b65f..07c11954fce3 100644 --- a/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go +++ b/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go @@ -48,6 +48,7 @@ func streamIngestionJobDescription( ReplicationSourceTenantName: streamIngestion.ReplicationSourceTenantName, ReplicationSourceAddress: tree.NewDString(redactedSourceAddr), Options: streamIngestion.Options, + Like: streamIngestion.Like, } ann := p.ExtendedEvalContext().Annotations return tree.AsStringWithFQNames(redactedCreateStmt, ann), nil @@ -64,12 +65,20 @@ func ingestionTypeCheck( if !ok { return false, nil, nil } - if err := exprutil.TypeCheck(ctx, "INGESTION", p.SemaCtx(), + toTypeCheck := []exprutil.ToTypeCheck{ exprutil.TenantSpec{TenantSpec: ingestionStmt.TenantSpec}, exprutil.TenantSpec{TenantSpec: ingestionStmt.ReplicationSourceTenantName}, exprutil.Strings{ ingestionStmt.ReplicationSourceAddress, - ingestionStmt.Options.Retention}); err != nil { + ingestionStmt.Options.Retention}, + } + if ingestionStmt.Like.OtherTenant != nil { + toTypeCheck = append(toTypeCheck, + exprutil.TenantSpec{TenantSpec: ingestionStmt.Like.OtherTenant}, + ) + } + + if err := exprutil.TypeCheck(ctx, "INGESTION", p.SemaCtx(), toTypeCheck...); err != nil { return false, nil, err } @@ -119,6 +128,15 @@ func ingestionPlanHook( return nil, nil, nil, false, err } + var likeTenantID uint64 + var likeTenantName string + if ingestionStmt.Like.OtherTenant != nil { + _, likeTenantID, likeTenantName, err = exprEval.TenantSpec(ctx, ingestionStmt.Like.OtherTenant) + if err != nil { + return nil, nil, nil, false, err + } + } + options, err := evalTenantReplicationOptions(ctx, ingestionStmt.Options, exprEval) if err != nil { return nil, nil, nil, false, err @@ -164,20 +182,20 @@ func ingestionPlanHook( sourceTenant, dstTenantName, dstTenantID) } + // Determine which template will be used as config template to + // create the new tenant below. + tenantInfo, err := sql.GetTenantTemplate(ctx, p.ExecCfg().Settings, p.InternalSQLTxn(), nil, likeTenantID, likeTenantName) + if err != nil { + return err + } + // Create a new tenant for the replication stream. jobID := p.ExecCfg().JobRegistry.MakeJobID() - tenantInfo := &mtinfopb.TenantInfoWithUsage{ - ProtoInfo: mtinfopb.ProtoInfo{ - TenantReplicationJobID: jobID, - }, - SQLInfo: mtinfopb.SQLInfo{ - // dstTenantID may be zero which will cause auto-allocation. - ID: dstTenantID, - DataState: mtinfopb.DataStateAdd, - ServiceMode: mtinfopb.ServiceModeNone, - Name: roachpb.TenantName(dstTenantName), - }, - } + tenantInfo.TenantReplicationJobID = jobID + // dstTenantID may be zero which will cause auto-allocation. + tenantInfo.ID = dstTenantID + tenantInfo.DataState = mtinfopb.DataStateAdd + tenantInfo.Name = roachpb.TenantName(dstTenantName) initialTenantZoneConfig, err := sql.GetHydratedZoneConfigForTenantsRange(ctx, p.Txn(), p.ExtendedEvalContext().Descs) if err != nil { diff --git a/pkg/kv/kvserver/replica_probe_test.go b/pkg/kv/kvserver/replica_probe_test.go index 47b389c156a2..b957fe68af02 100644 --- a/pkg/kv/kvserver/replica_probe_test.go +++ b/pkg/kv/kvserver/replica_probe_test.go @@ -125,20 +125,22 @@ func TestReplicaProbeRequest(t *testing.T) { kvpb.RoutingPolicy_LEASEHOLDER, kvpb.RoutingPolicy_NEAREST, } { - var b kv.Batch - b.AddRawRequest(probeReq) - b.Header.RoutingPolicy = policy - err := db.Run(ctx, &b) - if errors.HasType(err, (*kvpb.AmbiguousResultError)(nil)) { - // Rare but it can happen that we're proposing on a replica - // that is just about to get a snapshot. In that case we'll - // get: - // - // result is ambiguous: unable to determine whether command was applied via snapshot - t.Logf("ignoring: %s", err) - err = nil - } - require.NoError(t, err) + testutils.SucceedsSoon(t, func() error { + var b kv.Batch + b.AddRawRequest(probeReq) + b.Header.RoutingPolicy = policy + err := db.Run(ctx, &b) + if errors.HasType(err, (*kvpb.AmbiguousResultError)(nil)) { + // Rare but it can happen that we're proposing on a replica + // that is just about to get a snapshot. In that case we'll + // get: + // + // result is ambiguous: unable to determine whether command was applied via snapshot + return errors.Wrapf(err, "retrying") + } + require.NoError(t, err) + return nil + }) } } // Check expected number of probes seen on each Replica in the apply loop. diff --git a/pkg/multitenant/mtinfopb/info.proto b/pkg/multitenant/mtinfopb/info.proto index 2da7024b0f8f..f12c651d2990 100644 --- a/pkg/multitenant/mtinfopb/info.proto +++ b/pkg/multitenant/mtinfopb/info.proto @@ -103,10 +103,23 @@ message UsageInfo { // All-time consumption for this tenant. Each field has a corresponding column // in system.tenant_usage. optional roachpb.TenantConsumption consumption = 4 [(gogoproto.nullable) = false]; + + // Next ID: 5 +} + +// SettingOverride represents a cluster setting override for one tenant. +message SettingOverride { + option (gogoproto.equal) = true; + + optional string name = 1 [(gogoproto.nullable) = false]; + optional string value = 2 [(gogoproto.nullable) = false]; + optional string value_type = 3 [(gogoproto.nullable) = false]; + optional string reason = 4; + // Next ID: 5 } // TenantInfoWithUsage contains the information for a tenant in a multi-tenant -// cluster plus metadata related to cost control and consumption. +// cluster plus metadata related to cost control and consumption and setting overrides. message TenantInfoWithUsage { option (gogoproto.equal) = true; @@ -115,5 +128,7 @@ message TenantInfoWithUsage { optional SQLInfo extra_columns = 3 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - // Next ID: 4 + repeated SettingOverride setting_overrides = 4; + + // Next ID: 5 } diff --git a/pkg/sql/create_tenant.go b/pkg/sql/create_tenant.go index 87446dfc8971..1109f9a3c0de 100644 --- a/pkg/sql/create_tenant.go +++ b/pkg/sql/create_tenant.go @@ -13,11 +13,14 @@ package sql import ( "context" + "github.com/cockroachdb/cockroach/pkg/multitenant/mtinfopb" "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/errors" ) type createTenantNode struct { - tenantSpec tenantSpec + tenantSpec tenantSpec + likeTenantSpec tenantSpec } func (p *planner) CreateTenantNode(ctx context.Context, n *tree.CreateTenant) (planNode, error) { @@ -25,8 +28,16 @@ func (p *planner) CreateTenantNode(ctx context.Context, n *tree.CreateTenant) (p if err != nil { return nil, err } + var likeTenantSpec tenantSpec + if n.Like.OtherTenant != nil { + likeTenantSpec, err = p.planTenantSpec(ctx, n.Like.OtherTenant, "CREATE TENANT LIKE") + if err != nil { + return nil, err + } + } return &createTenantNode{ - tenantSpec: tspec, + tenantSpec: tspec, + likeTenantSpec: likeTenantSpec, }, nil } @@ -36,6 +47,20 @@ func (n *createTenantNode) startExec(params runParams) error { return err } + var tmplInfo *mtinfopb.TenantInfo + if n.likeTenantSpec != nil { + tmplInfo, err = n.likeTenantSpec.getTenantInfo(params.ctx, params.p) + if err != nil { + return errors.Wrap(err, "retrieving record for LIKE configuration template") + } + } + configTemplate, err := GetTenantTemplate(params.ctx, + params.p.ExecCfg().Settings, params.p.InternalSQLTxn(), + tmplInfo, 0, "") + if err != nil { + return err + } + var ctcfg createTenantConfig if tenantName != "" { ctcfg.Name = (*string)(&tenantName) @@ -44,7 +69,7 @@ func (n *createTenantNode) startExec(params runParams) error { tenantID := tid.ToUint64() ctcfg.ID = &tenantID } - _, err = params.p.createTenantInternal(params.ctx, ctcfg) + _, err = params.p.createTenantInternal(params.ctx, ctcfg, configTemplate) return err } diff --git a/pkg/sql/logictest/testdata/logic_test/tenant b/pkg/sql/logictest/testdata/logic_test/tenant index 88fea1d94cc8..10d369875563 100644 --- a/pkg/sql/logictest/testdata/logic_test/tenant +++ b/pkg/sql/logictest/testdata/logic_test/tenant @@ -329,3 +329,116 @@ DROP TENANT two statement ok DROP TENANT 'tenant-one' + +subtest tenant_templates + +query T +SHOW CLUSTER SETTING sql.create_tenant.default_template +---- +ยท + +# Check we can't use the system tenant as template. +statement error using the system tenant as config template +CREATE TENANT othertenant LIKE system + +# Create some "interesting" tenant template. +statement ok +CREATE TENANT tmpl; +ALTER TENANT tmpl GRANT CAPABILITY can_view_node_info; -- will be copied +ALTER TENANT tmpl SET CLUSTER SETTING trace.debug.enable = true; -- will be copied +-- Simulate resource limits. Will be copied. +-- Note: we cannot use the update_tenant_resource_limits() builtin +-- directly here because it can only be used from a CCL binary. +INSERT INTO system.tenant_usage( + tenant_id, instance_id, next_instance_id, last_update, + ru_burst_limit, ru_refill_rate, ru_current, current_share_sum, total_consumption) +VALUES ((SELECT id FROM system.tenants WHERE name = 'tmpl'), 0, 0, now(), + 11, 22, 33, 44, ''::BYTES); +ALTER TENANT tmpl START SERVICE SHARED; -- will not be copied. + +# Use it to create a new tenant. +statement ok +CREATE TENANT othertenant LIKE tmpl + +# Verify the service mode was not copied. +query ITTT +SHOW TENANT othertenant +---- +13 othertenant ready none + +# Verify the new tenant has the same caps as the template +# (by showing there's no difference between the two) +query TT +SELECT capability_name, capability_value FROM [SHOW TENANT tmpl WITH CAPABILITIES] +EXCEPT SELECT capability_name, capability_value FROM [SHOW TENANT othertenant WITH CAPABILITIES]; +---- + +# Check that the setting overrides were copied. +query TTTT +SELECT variable, value, type, origin FROM [SHOW CLUSTER SETTINGS FOR TENANT othertenant] +WHERE origin != 'no-override' +---- +trace.debug.enable true b per-tenant-override + +# Check that the resource usage parameters were copied. +query IIRRRRI +SELECT instance_id, next_instance_id, + ru_burst_limit, ru_refill_rate, ru_current, + current_share_sum, length(total_consumption) +FROM system.tenant_usage WHERE tenant_id = (SELECT id FROM system.tenants WHERE name = 'othertenant') +---- +0 0 11 22 33 0 0 + +# Clean up. +statement ok +DROP TENANT othertenant + +# Now set the default template and try again. +statement ok +SET CLUSTER SETTING sql.create_tenant.default_template = 'nonexistent'; + +statement error retrieving default tenant configuration template.*tenant "nonexistent" does not exist +CREATE TENANT othertenant + +statement ok +SET CLUSTER SETTING sql.create_tenant.default_template = 'tmpl'; + +# Create a new tenant - this should use the template implicitly now. +statement ok +CREATE TENANT othertenant + +# Verify the service mode was not copied. +query ITTT +SHOW TENANT othertenant +---- +14 othertenant ready none + +query TT +SELECT capability_name, capability_value FROM [SHOW TENANT tmpl WITH CAPABILITIES] +EXCEPT SELECT capability_name, capability_value FROM [SHOW TENANT othertenant WITH CAPABILITIES]; +---- + +# Check the setting overrides were taken over. +query TTTT +SELECT variable, value, type, origin FROM [SHOW CLUSTER SETTINGS FOR TENANT othertenant] +WHERE origin != 'no-override' +---- +trace.debug.enable true b per-tenant-override + +# Check that the resource usage parameters were copied. +query IIRRRRI +SELECT instance_id, next_instance_id, + ru_burst_limit, ru_refill_rate, ru_current, + current_share_sum, length(total_consumption) +FROM system.tenant_usage WHERE tenant_id = (SELECT id FROM system.tenants WHERE name = 'othertenant') +---- +0 0 11 22 33 0 0 + +# Clean up. +statement ok +DROP TENANT othertenant; +ALTER TENANT tmpl STOP SERVICE; +DROP TENANT tmpl + +statement ok +RESET CLUSTER SETTING sql.create_tenant.default_template diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 676a5c07595e..9038ada803b3 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -855,6 +855,9 @@ func (u *sqlSymUnion) showRangesOpts() *tree.ShowRangesOptions { func (u *sqlSymUnion) tenantSpec() *tree.TenantSpec { return u.val.(*tree.TenantSpec) } +func (u *sqlSymUnion) likeTenantSpec() *tree.LikeTenantSpec { + return u.val.(*tree.LikeTenantSpec) +} func (u *sqlSymUnion) cteMaterializeClause() tree.CTEMaterializeClause { return u.val.(tree.CTEMaterializeClause) } @@ -1169,6 +1172,8 @@ func (u *sqlSymUnion) showTenantOpts() tree.ShowTenantOptions { %type create_sequence_stmt %type create_func_stmt +%type <*tree.LikeTenantSpec> opt_like_tenant + %type create_stats_stmt %type <*tree.CreateStatsOptions> opt_create_stats_options %type <*tree.CreateStatsOptions> create_stats_option_list @@ -4348,26 +4353,43 @@ create_stmt: // %Help: CREATE TENANT - create new tenant // %Category: Experimental // %Text: -// CREATE TENANT name -// CREATE TENANT name FROM REPLICATION OF ON [ WITH OPTIONS ... ] +// CREATE TENANT name [ LIKE ] +// CREATE TENANT name [ LIKE ] FROM REPLICATION OF ON [ WITH OPTIONS ... ] create_tenant_stmt: - CREATE TENANT d_expr + CREATE TENANT d_expr opt_like_tenant { /* SKIP DOC */ - $$.val = &tree.CreateTenant{TenantSpec: &tree.TenantSpec{IsName: true, Expr: $3.expr()}} + $$.val = &tree.CreateTenant{ + TenantSpec: &tree.TenantSpec{IsName: true, Expr: $3.expr()}, + Like: $4.likeTenantSpec(), + } } -| CREATE TENANT d_expr FROM REPLICATION OF d_expr ON d_expr opt_with_tenant_replication_options +| CREATE TENANT d_expr opt_like_tenant FROM REPLICATION OF d_expr ON d_expr opt_with_tenant_replication_options { /* SKIP DOC */ $$.val = &tree.CreateTenantFromReplication{ TenantSpec: &tree.TenantSpec{IsName: true, Expr: $3.expr()}, - ReplicationSourceTenantName: &tree.TenantSpec{IsName: true, Expr: $7.expr()}, - ReplicationSourceAddress: $9.expr(), - Options: *$10.tenantReplicationOptions(), + ReplicationSourceTenantName: &tree.TenantSpec{IsName: true, Expr: $8.expr()}, + ReplicationSourceAddress: $10.expr(), + Options: *$11.tenantReplicationOptions(), + Like: $4.likeTenantSpec(), } } | CREATE TENANT error // SHOW HELP: CREATE TENANT +// opt_like_tenant defines a LIKE clause for CREATE TENANT. +// Eventually this can grow to support INCLUDING/EXCLUDING options +// like in CREATE TABLE. +opt_like_tenant: + /* EMPTY */ + { + $$.val = &tree.LikeTenantSpec{} + } +| LIKE tenant_spec + { + $$.val = &tree.LikeTenantSpec{OtherTenant: $2.tenantSpec()} + } + // Optional tenant replication options. opt_with_tenant_replication_options: WITH tenant_replication_options_list diff --git a/pkg/sql/parser/testdata/create_tenant b/pkg/sql/parser/testdata/create_tenant index 65a35dcf1cea..9e0077c78427 100644 --- a/pkg/sql/parser/testdata/create_tenant +++ b/pkg/sql/parser/testdata/create_tenant @@ -14,6 +14,22 @@ CREATE TENANT ("bar-with-hyphen") -- fully parenthesized CREATE TENANT "bar-with-hyphen" -- literals removed CREATE TENANT _ -- identifiers removed +parse +CREATE TENANT foo LIKE bar +---- +CREATE TENANT foo LIKE bar +CREATE TENANT (foo) LIKE (bar) -- fully parenthesized +CREATE TENANT foo LIKE bar -- literals removed +CREATE TENANT _ LIKE _ -- identifiers removed + +parse +CREATE TENANT foo LIKE [123] +---- +CREATE TENANT foo LIKE [123] +CREATE TENANT (foo) LIKE [(123)] -- fully parenthesized +CREATE TENANT foo LIKE [_] -- literals removed +CREATE TENANT _ LIKE [123] -- identifiers removed + parse CREATE TENANT destination FROM REPLICATION OF source ON 'pgurl' ---- @@ -22,6 +38,22 @@ CREATE TENANT (destination) FROM REPLICATION OF (source) ON ('pgurl') -- fully p CREATE TENANT destination FROM REPLICATION OF source ON '_' -- literals removed CREATE TENANT _ FROM REPLICATION OF _ ON 'pgurl' -- identifiers removed +parse +CREATE TENANT destination LIKE bar FROM REPLICATION OF source ON 'pgurl' +---- +CREATE TENANT destination LIKE bar FROM REPLICATION OF source ON 'pgurl' +CREATE TENANT (destination) LIKE (bar) FROM REPLICATION OF (source) ON ('pgurl') -- fully parenthesized +CREATE TENANT destination LIKE bar FROM REPLICATION OF source ON '_' -- literals removed +CREATE TENANT _ LIKE _ FROM REPLICATION OF _ ON 'pgurl' -- identifiers removed + +parse +CREATE TENANT destination LIKE [123] FROM REPLICATION OF source ON 'pgurl' +---- +CREATE TENANT destination LIKE [123] FROM REPLICATION OF source ON 'pgurl' +CREATE TENANT (destination) LIKE [(123)] FROM REPLICATION OF (source) ON ('pgurl') -- fully parenthesized +CREATE TENANT destination LIKE [_] FROM REPLICATION OF source ON '_' -- literals removed +CREATE TENANT _ LIKE [123] FROM REPLICATION OF _ ON 'pgurl' -- identifiers removed + parse CREATE TENANT "destination-hyphen" FROM REPLICATION OF "source-hyphen" ON 'pgurl' ---- diff --git a/pkg/sql/sem/tree/create.go b/pkg/sql/sem/tree/create.go index 1512a3322944..943aa89aa567 100644 --- a/pkg/sql/sem/tree/create.go +++ b/pkg/sql/sem/tree/create.go @@ -2150,12 +2150,27 @@ func (node *CreateExternalConnection) Format(ctx *FmtCtx) { // CreateTenant represents a CREATE TENANT statement. type CreateTenant struct { TenantSpec *TenantSpec + Like *LikeTenantSpec } // Format implements the NodeFormatter interface. func (node *CreateTenant) Format(ctx *FmtCtx) { ctx.WriteString("CREATE TENANT ") ctx.FormatNode(node.TenantSpec) + ctx.FormatNode(node.Like) +} + +// LikeTenantSpec represents a LIKE clause in CREATE TENANT. +type LikeTenantSpec struct { + OtherTenant *TenantSpec +} + +func (node *LikeTenantSpec) Format(ctx *FmtCtx) { + if node.OtherTenant == nil { + return + } + ctx.WriteString(" LIKE ") + ctx.FormatNode(node.OtherTenant) } // CreateTenantFromReplication represents a CREATE TENANT...FROM REPLICATION @@ -2175,6 +2190,8 @@ type CreateTenantFromReplication struct { ReplicationSourceAddress Expr Options TenantReplicationOptions + + Like *LikeTenantSpec } // TenantReplicationOptions options for the CREATE TENANT FROM REPLICATION command. @@ -2190,6 +2207,7 @@ func (node *CreateTenantFromReplication) Format(ctx *FmtCtx) { // NB: we do not anonymize the tenant name because we assume that tenant names // do not contain sensitive information. ctx.FormatNode(node.TenantSpec) + ctx.FormatNode(node.Like) if node.ReplicationSourceAddress != nil { ctx.WriteString(" FROM REPLICATION OF ") diff --git a/pkg/sql/sem/tree/walk.go b/pkg/sql/sem/tree/walk.go index f16308122480..b6dc103523fb 100644 --- a/pkg/sql/sem/tree/walk.go +++ b/pkg/sql/sem/tree/walk.go @@ -1041,6 +1041,34 @@ func (n *AlterTenantReplication) walkStmt(v Visitor) Statement { return ret } +// copyNode makes a copy of this node without recursing. +func (n *LikeTenantSpec) copyNode() *LikeTenantSpec { + nodeCopy := *n + return &nodeCopy +} + +// copyNode makes a copy of this Statement without recursing in any child Statements. +func (n *CreateTenant) copyNode() *CreateTenant { + stmtCopy := *n + return &stmtCopy +} + +// walkStmt is part of the walkableStmt interface. +func (n *CreateTenant) walkStmt(v Visitor) Statement { + ret := n + if n.Like.OtherTenant != nil { + ts, changed := walkTenantSpec(v, n.TenantSpec) + if changed { + if ret == n { + ret = n.copyNode() + } + ret.Like = n.Like.copyNode() + ret.Like.OtherTenant = ts + } + } + return ret +} + // copyNode makes a copy of this Statement without recursing in any child Statements. func (n *CreateTenantFromReplication) copyNode() *CreateTenantFromReplication { stmtCopy := *n @@ -1073,6 +1101,16 @@ func (n *CreateTenantFromReplication) walkStmt(v Visitor) Statement { ret.Options.Retention = e } } + if n.Like.OtherTenant != nil { + ts, changed := walkTenantSpec(v, n.TenantSpec) + if changed { + if ret == n { + ret = n.copyNode() + } + ret.Like = n.Like.copyNode() + ret.Like.OtherTenant = ts + } + } return ret } @@ -1900,6 +1938,7 @@ var _ walkableStmt = &CancelSessions{} var _ walkableStmt = &ControlJobs{} var _ walkableStmt = &ControlSchedules{} var _ walkableStmt = &CreateTable{} +var _ walkableStmt = &CreateTenant{} var _ walkableStmt = &CreateTenantFromReplication{} var _ walkableStmt = &Delete{} var _ walkableStmt = &DropTenant{} diff --git a/pkg/sql/tenant_accessors.go b/pkg/sql/tenant_accessors.go index ce557c0cd6b7..c68538c98fe9 100644 --- a/pkg/sql/tenant_accessors.go +++ b/pkg/sql/tenant_accessors.go @@ -15,8 +15,10 @@ import ( "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv/kvpb" "github.com/cockroachdb/cockroach/pkg/multitenant/mtinfopb" "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/settings" "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql/isql" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" @@ -187,3 +189,150 @@ func (p *planner) LookupTenantID( } return roachpb.MustMakeTenantID(rec.ID), nil } + +// GetExtendedTenantInfo hydrates a TenantInfoWithUsage with the +// additional data beyond the TenantInfo record. +func GetExtendedTenantInfo( + ctx context.Context, txn isql.Txn, info *mtinfopb.TenantInfo, +) (*mtinfopb.TenantInfoWithUsage, error) { + res := &mtinfopb.TenantInfoWithUsage{ + ProtoInfo: info.ProtoInfo, + SQLInfo: info.SQLInfo, + } + rows, err := txn.QueryBufferedEx(ctx, "get-tenant-setting-overrides", txn.KV(), sessiondata.NodeUserSessionDataOverride, + `SELECT name, value, value_type, reason FROM system.tenant_settings WHERE tenant_id = $1`, + info.ID, + ) + if err != nil { + return nil, err + } + for _, row := range rows { + override := &mtinfopb.SettingOverride{ + Name: string(tree.MustBeDString(row[0])), + Value: string(tree.MustBeDString(row[1])), + ValueType: string(tree.MustBeDString(row[2])), + } + if row[3] != tree.DNull { + s := string(tree.MustBeDString(row[3])) + override.Reason = &s + } + res.SettingOverrides = append(res.SettingOverrides, override) + } + + row, err := txn.QueryRowEx(ctx, "get-tenant-usage-config", txn.KV(), sessiondata.NodeUserSessionDataOverride, + `SELECT ru_current, ru_burst_limit, ru_refill_rate + FROM system.tenant_usage + WHERE tenant_id = $1 AND instance_id = 0`, + info.ID, + ) + if err != nil { + return nil, err + } + if row != nil { + res.Usage = &mtinfopb.UsageInfo{ + RUCurrent: float64(tree.MustBeDFloat(row[0])), + RUBurstLimit: float64(tree.MustBeDFloat(row[1])), + RURefillRate: float64(tree.MustBeDFloat(row[2])), + } + } + + return res, nil +} + +var defaultTenantConfigTemplate = func() *settings.StringSetting { + s := settings.RegisterStringSetting( + settings.SystemOnly, + "sql.create_tenant.default_template", + "tenant to use as configuration template when LIKE is not specified in CREATE TENANT", + // We use the empty string so that no template is used by default + // (i.e. empty proto, no setting overrides). + "", + ) + s.SetReportable(true) + return s +}() + +// GetTenantTemplate loads the tenant template corresponding to the +// provided origin tenant. If info is nil, likeTenantID is zero and +// likeTenantName is empty, the default template is returned. +func GetTenantTemplate( + ctx context.Context, + settings *cluster.Settings, + txn isql.Txn, + info *mtinfopb.TenantInfo, + likeTenantID uint64, + likeTenantName string, +) (res *mtinfopb.TenantInfoWithUsage, err error) { + if info != nil && (likeTenantID != 0 || likeTenantName != "") { + // Sanity check + return nil, errors.AssertionFailedf("programming error: cannot pass both default info struct and tenant reference") + } + if info == nil { + if likeTenantID == 0 && likeTenantName == "" { + // No LIKE at all. Do we have something in the cluster setting? + tmplName := defaultTenantConfigTemplate.Get(&settings.SV) + if tmplName == "" { + // No template at all - just use an empty protobuf. + return &mtinfopb.TenantInfoWithUsage{}, nil + } + // Use the template specified in the setting. + info, err = GetTenantRecordByName(ctx, settings, txn, roachpb.TenantName(tmplName)) + if err != nil { + return nil, errors.Wrapf(err, "retrieving default tenant configuration template %q", tmplName) + } + } else { + if likeTenantID != 0 && likeTenantName != "" { + return nil, errors.AssertionFailedf("programming error: conflicting input tenant spec from caller") + } + // No pre-loaded info, but we have a LIKE clause. Is it by-ID or by-Name? + if likeTenantID != 0 { + // By-ID. + tid, err := roachpb.MakeTenantID(likeTenantID) + if err != nil { + return nil, errors.Wrap(err, "invalid LIKE tenant ID") + } + info, err = GetTenantRecordByID(ctx, txn, tid, settings) + if err != nil { + return nil, errors.Wrap(err, "retrieving LIKE tenant record") + } + } else { + // By-name. + info, err = GetTenantRecordByName(ctx, settings, txn, roachpb.TenantName(likeTenantName)) + if err != nil { + return nil, errors.Wrap(err, "retrieving LIKE tenant record") + } + } + } + } + + // For now, prevent use of the record for the system tenant. The + // user may have the mistaken assumption that "LIKE system" would + // create a tenant with all the special cases of the system tenant, + // and we do not guarantee that for now. + if roachpb.MustMakeTenantID(info.ID).IsSystem() { + return nil, errors.WithHint( + pgerror.New(pgcode.WrongObjectType, "using the system tenant as config template"), + "Create another secondary tenant as template, grant it extra capabilities, and then use that as config template.") + } + + // Now we have our info field. Expand it. + tmplInfo, err := GetExtendedTenantInfo(ctx, txn, info) + if err != nil { + return nil, errors.Wrap(err, "retrieving tenant template details") + } + + // Clear out the fields we can't reuse in a fresh tenant record. + tmplInfo.ID = 0 + tmplInfo.Name = "" + tmplInfo.DataState = mtinfopb.DataStateReady + tmplInfo.ServiceMode = mtinfopb.ServiceModeNone + tmplInfo.DroppedName = "" + tmplInfo.DeprecatedID = 0 + tmplInfo.DeprecatedDataState = 0 + tmplInfo.TenantReplicationJobID = 0 + if tmplInfo.Usage != nil { + tmplInfo.Usage.Consumption = kvpb.TenantConsumption{} + } + + return tmplInfo, nil +} diff --git a/pkg/sql/tenant_creation.go b/pkg/sql/tenant_creation.go index 3195226200e2..ca852c6fa845 100644 --- a/pkg/sql/tenant_creation.go +++ b/pkg/sql/tenant_creation.go @@ -59,7 +59,10 @@ func (p *planner) CreateTenant( return tid, pgerror.WithCandidateCode(err, pgcode.Syntax) } } - return p.createTenantInternal(ctx, ctcfg) + + configTemplate := mtinfopb.TenantInfoWithUsage{} + + return p.createTenantInternal(ctx, ctcfg, &configTemplate) } type createTenantConfig struct { @@ -69,7 +72,7 @@ type createTenantConfig struct { } func (p *planner) createTenantInternal( - ctx context.Context, ctcfg createTenantConfig, + ctx context.Context, ctcfg createTenantConfig, configTemplate *mtinfopb.TenantInfoWithUsage, ) (tid roachpb.TenantID, err error) { var tenantID uint64 if ctcfg.ID != nil { @@ -100,16 +103,17 @@ func (p *planner) createTenantInternal( return tid, err } - info := &mtinfopb.TenantInfoWithUsage{ - SQLInfo: mtinfopb.SQLInfo{ - ID: tenantID, - // We synchronously initialize the tenant's keyspace below, so - // we can skip the ADD state and go straight to the READY state. - DataState: mtinfopb.DataStateReady, - Name: name, - ServiceMode: serviceMode, - }, - } + info := configTemplate + + // Override the template fields for a fresh tenant. The other + // template fields remain unchanged (i.e. we reuse the template's + // configuration). + info.ID = tenantID + info.Name = name + // We synchronously initialize the tenant's keyspace below, so + // we can skip the ADD state and go straight to the READY state. + info.DataState = mtinfopb.DataStateReady + info.ServiceMode = serviceMode initialTenantZoneConfig, err := GetHydratedZoneConfigForTenantsRange(ctx, p.Txn(), p.Descriptors()) if err != nil { @@ -332,6 +336,25 @@ func CreateTenantRecord( logcrash.ReportOrPanic(ctx, &settings.SV, "inserting tenant %+v: unexpected number of rows affected: %d", info, num) } + for _, so := range info.SettingOverrides { + var reason interface{} + if so.Reason != nil { + reason = *so.Reason + } + if _, err := txn.ExecEx( + ctx, "create-tenant-setting-override", txn.KV(), sessiondata.NodeUserSessionDataOverride, + `INSERT INTO system.tenant_settings ( + tenant_id, name, value, value_type, reason) + VALUES ($1, $2, $3, $4, $5)`, + tenID, so.Name, so.Value, so.ValueType, reason); err != nil { + if pgerror.GetPGCode(err) == pgcode.UniqueViolation { + return roachpb.TenantID{}, pgerror.Newf(pgcode.DuplicateObject, "tenant \"%d\" already has an override for setting %q", + tenID, so.Name) + } + return roachpb.TenantID{}, errors.Wrap(err, "inserting tenant setting overrides") + } + } + if u := info.Usage; u != nil { consumption, err := protoutil.Marshal(&u.Consumption) if err != nil {