builtins: add create_tenant(<tenant-name>) overload#91518
builtins: add create_tenant(<tenant-name>) overload#91518craig[bot] merged 1 commit intocockroachdb:masterfrom
Conversation
lidorcarmel
left a comment
There was a problem hiding this comment.
Reviewed 7 of 7 files at r1, all commit messages.
Reviewable status:complete! 0 of 0 LGTMs obtained (waiting on @adityamaru and @knz)
pkg/sql/tenant.go line 236 at r1 (raw file):
func (p *planner) CreateTenant(ctx context.Context, name string) (roachpb.TenantID, error) { if err := p.RequireAdminRole(ctx, "create tenant"); err != nil { return roachpb.SystemTenantID, err
why a real id on failure and not roachpb.TenantID{} as you do below?
Code quote:
roachpb.SystemTenantIDpkg/sql/tenant.go line 240 at r1 (raw file):
Previously, adityamaru (Aditya Maru) wrote…
@knz just confirming this query could result in us re-using a previously used ID, but this is okay because the tenant has to be GC'ed for us to be able to reuse the ID.
Alternatively, we could change this query to just find the higher PK id and just use the next one.
I think changing this as you say makes sense. otherwise when debugging we will always need to think about "is id x the same x as the one we deleted yesterday?" and stuff like that. even if it's rare, having an id that was never used before simplifies things.
thinking a bit.. IIUC we allow creating tenants with whatever id the caller wants, right? so if I create a tenant with id MAX-1, that's not going to be great if we change this code to use the max id we ever saw plus one, right? in that case it's not going to work and the existing code is better.
pkg/sql/tenant.go line 244 at r1 (raw file):
FROM (VALUES (1) UNION ALL SELECT id FROM system.tenants) AS u(id) WHERE NOT EXISTS (SELECT 1 FROM system.tenants t WHERE t.id=u.id+1) AND NOT EXISTS (SELECT 1 FROM system.tenants t WHERE t.name=$1)
IIUC, if we call this from 2 places at the same time, both will try to create tenants with the same id and will fail, I think the error will be "tenant 7 already exists":
Line 111 in 19382ae
even though both callers only passed a name, and those names were unique.
not ideal I guess.
does it make sense to push the id generation down to the query that does the INSERT?
pkg/sql/tenant.go line 254 at r1 (raw file):
} nextID := *row[0].(*tree.DInt) return roachpb.MakeTenantID(uint64(nextID)), p.CreateTenantWithID(ctx, uint64(nextID), name)
you probably want to return an empty id if CreateTenantWithID fails, right?
pkg/sql/sem/builtins/builtins.go line 4844 at r1 (raw file):
return args[0], nil }, Info: "Creates a new tenant with the provided ID. Must be run by the System tenant.",
nit: ".. with the provided ID and name..."
no what you touched but why not fix..
Code quote:
ID. 283d123 to
cdb89a1
Compare
adityamaru
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @knz and @lidorcarmel)
pkg/sql/tenant.go line 236 at r1 (raw file):
Previously, lidorcarmel (Lidor Carmel) wrote…
why a real id on failure and not
roachpb.TenantID{}as you do below?
nice catch, my mistake.
pkg/sql/tenant.go line 240 at r1 (raw file):
Previously, lidorcarmel (Lidor Carmel) wrote…
I think changing this as you say makes sense. otherwise when debugging we will always need to think about "is id x the same x as the one we deleted yesterday?" and stuff like that. even if it's rare, having an id that was never used before simplifies things.
thinking a bit.. IIUC we allow creating tenants with whatever id the caller wants, right? so if I create a tenant with id
MAX-1, that's not going to be great if we change this code to use the max id we ever saw plus one, right? in that case it's not going to work and the existing code is better.
Yeah I think I agree with you about recycling IDs leading to more confusion. Though I cannot claim to have thought about this as much as @knz so I'll defer to him about what we should pursue.
pkg/sql/tenant.go line 244 at r1 (raw file):
Previously, lidorcarmel (Lidor Carmel) wrote…
IIUC, if we call this from 2 places at the same time, both will try to create tenants with the same id and will fail, I think the error will be "tenant 7 already exists":
Line 111 in 19382ae
even though both callers only passed a name, and those names were unique.
not ideal I guess.
does it make sense to push the id generation down to the query that does the INSERT?
Just thinking out loud but if you run two create_tenant calls with unique names, each one is going to be run in its own planner txn. i.e. all the steps including computing a unique ID and writing to the system.tenants table are done under the scope of p.Txn(). So I believe when the txn that finishes later goes to commit a key with a conflicting PK it will see that someone has already written at a timestamp below what it read when computing the unique ID to assign to the tenant. So this will cause the later txn to error out with a retry error?
I'm not sure I understand how pushing ID generation into the INSERT achieves different behavior, considering the read/write both happen under the same txn.
pkg/sql/tenant.go line 254 at r1 (raw file):
Previously, lidorcarmel (Lidor Carmel) wrote…
you probably want to return an empty id if
CreateTenantWithIDfails, right?
hopefully the caller checks error before using the return type but why risk it, changed
pkg/sql/sem/builtins/builtins.go line 4844 at r1 (raw file):
Previously, lidorcarmel (Lidor Carmel) wrote…
nit: ".. with the provided ID and name..."
no what you touched but why not fix..
done.
knz
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @lidorcarmel)
pkg/sql/tenant.go line 244 at r1 (raw file):
IIUC, if we call this from 2 places at the same time, both will try to create tenants with the same id and will fail,
This is not how SQL atomicity work. The two txns would be mutually ordered and would compute differnt IDs.
knz
left a comment
There was a problem hiding this comment.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @lidorcarmel)
pkg/sql/tenant.go line 244 at r1 (raw file):
Previously, knz (Raphael 'kena' Poss) wrote…
IIUC, if we call this from 2 places at the same time, both will try to create tenants with the same id and will fail,
This is not how SQL atomicity work. The two txns would be mutually ordered and would compute differnt IDs.
Oh wait I misread. This is indeed a problem, Lidor was right.
The original code I provided does the SELECT and the INSERT inside a single statement. that's the goal.
lidorcarmel
left a comment
There was a problem hiding this comment.
Reviewed 3 of 3 files at r2, all commit messages.
Reviewable status:complete! 0 of 0 LGTMs obtained (waiting on @adityamaru and @knz)
pkg/sql/tenant.go line 240 at r1 (raw file):
Previously, adityamaru (Aditya Maru) wrote…
Yeah I think I agree with you about recycling IDs leading to more confusion. Though I cannot claim to have thought about this as much as @knz so I'll defer to him about what we should pursue.
lgtm. maybe it's worth deprecating the old api? so that ids will only be generated automatically? but that's not really related to this pr.. just a thought.
pkg/sql/tenant.go line 244 at r1 (raw file):
Previously, knz (Raphael 'kena' Poss) wrote…
Oh wait I misread. This is indeed a problem, Lidor was right.
The original code I provided does the SELECT and the INSERT inside a single statement. that's the goal.
now I'm confused.. Aditya is right that both statements (SELECT and INSERT) are done within the same txn, so we should be good?
pkg/sql/tenant.go line 254 at r1 (raw file):
Previously, adityamaru (Aditya Maru) wrote…
hopefully the caller checks error before using the return type but why risk it, changed
lgtm, hopefully indeed.
I see, can you help me understand how this is different from running the |
cdb89a1 to
1338646
Compare
knz
left a comment
There was a problem hiding this comment.
Reviewed 3 of 3 files at r3, all commit messages.
Reviewable status:complete! 0 of 0 LGTMs obtained (waiting on @lidorcarmel)
pkg/sql/tenant.go line 244 at r1 (raw file):
Previously, lidorcarmel (Lidor Carmel) wrote…
now I'm confused.. Aditya is right that both statements (SELECT and INSERT) are done within the same txn, so we should be good?
Oh are they the same txn? It wasn't obvious to me. In that case, yes we're good. Sorry for the noise.
knz
left a comment
There was a problem hiding this comment.
@jaylim-crl @jeffswenson Can you check for us how the create_tenant() function is called from the CC control plane?
This PR as-is adds an overload, which means that a prepared statement with the syntax create_tenant($1) and no type hint from the client will not produce an error (that the type is ambiguous). If the CC control plane adds the type hint, we're fine. Otherwise, we may want to strategize this change a bit better.
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @lidorcarmel)
knz
left a comment
There was a problem hiding this comment.
will now produce an error
Reviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @lidorcarmel)
jaylim-crl
left a comment
There was a problem hiding this comment.
@knz the call looks like the following:
SELECT
CASE
WHEN NOT EXISTS(SELECT * FROM system.tenants WHERE id = $1)
THEN crdb_internal.create_tenant($1)
ENDReviewable status:
complete! 0 of 0 LGTMs obtained (waiting on @lidorcarmel)
|
@adityamaru can you check how the query that jay pasted would behave wrt type checking with your change? |
|
We could also make changes to the CC control plane where necessary, provided that the change is backwards compatible (i.e. working with existing versions). |
|
ok thanks. The change proposed here would be to modify the query to read |
It would fail! I fixed a few tests where I had to provide a type hint as you suggested. Good catch I didn't think about CC. @jaylim-crl do you think you could push out a fix for this? If not we can think of alternatives. edit: oh nvm looks like Jeff's already on it 🚀 |
|
@jeffswenson just confirming that with your change merged I'm good to merge this? |
Yep! You can merge this. The CC change will roll out long before 23.1 is rolled out in production. |
|
TFTRs! bors r+ |
|
Looks like this PR killed the CI and might need to be rebased: bors r- |
|
Canceled. |
1338646 to
b2c3202
Compare
This change adds another overload to the `crdb_internal.create_tenant` builtin that only accepts the name the of the tenant to be created. The underlying logic automatically picks the next unique ID based on the existing tenants in the `system.tenants` table. The builtin returns the ID assigned to the newly created tenant or an error if a tenant with the same name already exists. Just like other overloads this buitlin is admin-only and can only be run by the system tenant. Informs: cockroachdb#91235 Release note: None
b2c3202 to
8d15a3a
Compare
| FROM (VALUES (1) UNION ALL SELECT id FROM system.tenants) AS u(id) | ||
| WHERE NOT EXISTS (SELECT 1 FROM system.tenants t WHERE t.id=u.id+1) | ||
| AND NOT EXISTS (SELECT 1 FROM system.tenants t WHERE t.name=$1) | ||
| ORDER BY id LIMIT 1 |
There was a problem hiding this comment.
note, i had to add an order by clause so that we get the first available ID.
|
bors r+ |
|
Build succeeded: |
This change adds another overload to the
crdb_internal.create_tenantbuiltin that only accepts the name the of the tenant to be created. The underlying logic automatically picks the next unique ID based on the existing tenants in thesystem.tenantstable. The builtin returns the ID assigned to the newly created tenant or an error if a tenant with the same name already exists. Just like other overloads this buitlin is admin-only and can only be run by the system tenant.Informs: #91235
Release note: None