Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: support tenant creation and deletion, track in system.tenants table #48778

Merged
merged 6 commits into from
May 14, 2020

Conversation

nvanbenschoten
Copy link
Member

@nvanbenschoten nvanbenschoten commented May 13, 2020

Most of #47904.

This commit implements the initial structure for tenant creation and deletion. It does so by introducing a new system table to track tenants in a multi-tenant cluster and two new builtin functions to manipulate this table and the overall multi-tenant state.

The change starts by introducing a new system.tenants table with the following schema:

CREATE TABLE system.tenants (
    id     INT8 NOT NULL PRIMARY KEY,
    active BOOL NOT NULL DEFAULT true,
    info   BYTES
);

The id column is self-explanatory. The active column is used to coordinate tenant destruction in an asynchronous job. The info column is an opaque byte slice to allow users to associate arbitrary information with specific tenants. I don't know exactly how this third field will be used (mapping tenants back to CockroachCloud user IDs?), but it seems like a good idea to add some flexibility since we do intend to eventually expose this externally.

The table is given an ID of 8, which means that it falls within the "system config span". I believe this is ok, because the entire concept of the "system config span" should be going away with #47150. The table is also only exposed to the system-tenant and is never created for secondary tenants.

The change then introduces two new builtin functions: crdb_internal.create_tenant and crdb_internal.destroy_tenant. These do what you would expect - creating and destroying tenant keyspaces, along with updating metadata in system.tenants.

@nvanbenschoten nvanbenschoten requested review from tbg, asubiotto, a team and dt and removed request for a team May 13, 2020 04:40
@cockroach-teamcity
Copy link
Member

This change is Reviewable

@nvanbenschoten nvanbenschoten removed the request for review from dt May 13, 2020 04:41
@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/createTenant branch from d59a46a to e32ad86 Compare May 13, 2020 05:18
@blathers-crl
Copy link

blathers-crl bot commented May 13, 2020

❌ The GitHub CI (Cockroach) build has failed on e32ad865.

🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is otan.

@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/createTenant branch from e32ad86 to 0db5977 Compare May 13, 2020 06:17
@nvanbenschoten nvanbenschoten requested a review from a team as a code owner May 13, 2020 06:17
@knz
Copy link
Contributor

knz commented May 13, 2020

would a JSONB field for info not be more useful?

Copy link
Contributor

@asubiotto asubiotto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked at #48513 and I'm not sure I understand the thinking behind having a single DescIDSequence for all tenants. Why is it not better to have one per tenant and stored in tenant keyspace?

Reviewed 10 of 10 files at r1, 3 of 3 files at r2, 6 of 6 files at r3, 17 of 17 files at r4, 3 of 3 files at r5, 4 of 4 files at r6, 29 of 29 files at r7.
Reviewable status: :shipit: complete! 0 of 0 LGTMs obtained (waiting on @tbg)

@tbg
Copy link
Member

tbg commented May 13, 2020

@asubiotto isn't the suggestion that each tenant uses a sequence? It's not sharing the sequence between tenants. So I think he's suggesting what you're suggesting, perhaps ambiguously framed.

@tbg tbg requested a review from asubiotto May 13, 2020 09:47
Copy link
Member

@tbg tbg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm: (I did not look at first three commits). I assume you'll want to wait for @asubiotto's review though.

Reviewed 10 of 10 files at r1, 3 of 3 files at r2, 6 of 6 files at r3, 17 of 17 files at r4, 3 of 3 files at r5, 4 of 4 files at r6, 29 of 29 files at r7.
Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @nvanbenschoten)


docs/generated/sql/functions.md, line 1061 at r7 (raw file):

</table>

### Multi-tenancy functions

Is this internal documentation? We'll want to avoid leaking anything about these into the official docs.


pkg/sql/descriptor.go, line 60 at r4 (raw file):

func GenerateUniqueDescID(ctx context.Context, db *kv.DB) (sqlbase.ID, error) {
	// Increment unique descriptor counter.
	newVal, err := kv.IncrementValRetryable(ctx, db, keys.DescIDGenerator, 1)

Can DescIDGenerator be unexported?


pkg/sql/tenant.go, line 67 at r7 (raw file):

		keys.MakeSQLCodec(roachpb.MakeTenantID(tenID)),
		zonepb.DefaultZoneConfigRef(),
		zonepb.DefaultSystemZoneConfigRef(),

These are unused, right? Can we avoid supplying them?


pkg/sql/tenant.go, line 100 at r7 (raw file):

		// NOTE: even if we got this wrong, the rest of the function would fail
		// for a non-system tenant because they would be missing a system.tenant
		// table.

It would (at some point) also fail because the KV layer won't let them.


pkg/sql/tenant.go, line 129 at r7 (raw file):

	// TODO(nvanbenschoten): actually clear tenant keyspace. Do we want to do
	// this synchronously or schedule it as a long-running job. Probably the

Definitely not in the same transaction. I would say active = false is irrevocable and we start ClearRanging one the txn commits (blocking return of destroy_tenant). If that fails too early, can just re-run the command. The // tenant already destroyed case would go away.

Are you intentionally keeping inactive tenants around? I thought they'd be gone once they're gone, but could be recreated.


pkg/sql/logictest/testdata/logic_test/grant_table, line 168 at r7 (raw file):

SELECT * FROM [SHOW GRANTS]
 WHERE schema_name NOT IN ('crdb_internal', 'pg_catalog', 'information_schema')
ORDER BY 1,2,3

Why the large diff in this method? Is that just bad diffing?


pkg/sql/sqlbase/metadata.go, line 231 at r5 (raw file):

	// this cache for system descriptors that are only found in non-system
	// tenants will not be found. That is ok for now, but we'll need to split
	// this cache or do something smarter should it become a problem.

I would rather fix this right now before we forget about it. The only caller into this cache is

descID := sqlbase.LookupSystemTableDescriptorID(ctx, settings, dbID, object)

which has the codec available. So you can just pre-generate two maps here, and index into the right one. The only plumbing is passing the codec one layer down into UncachedPhysicalAccessor.GetObjectDesc, and that only affects eight call sites.


pkg/sql/sqlbase/system.go, line 108 at r7 (raw file):

	id     INT8 NOT NULL PRIMARY KEY,
	active BOOL NOT NULL DEFAULT true,
	info   BYTES,

It seems unclear what we ultimately want here. I'm fine leaving this info field in, but let's add a comment that it's just a placeholder and may be kept, replaced, or even just removed.

@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/createTenant branch from 0db5977 to 8b8375b Compare May 14, 2020 18:49
Copy link
Member Author

@nvanbenschoten nvanbenschoten left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TFTRs!

would a JSONB field for info not be more useful?

I was thinking that we might want to store encoded protobufs in the field as well, so I think it's best to keep the data type more general. It's easy enough to go from JSON to BYTES and back.

isn't the suggestion that each tenant uses a sequence? It's not sharing the sequence between tenants. So I think he's suggesting what you're suggesting, perhaps ambiguously framed.

Right, #48513 wasn't intending to mean that we use a "single SQL sequence as DescIDGenerator for all non-system tenants", it was intending to mean that we use a "SQL sequence as DescIDGenerator for each non-system tenants". I can see how that was ambiguous though.

Reviewable status: :shipit: complete! 1 of 0 LGTMs obtained (waiting on @tbg)


docs/generated/sql/functions.md, line 1061 at r7 (raw file):

Previously, tbg (Tobias Grieger) wrote…

Is this internal documentation? We'll want to avoid leaking anything about these into the official docs.

Done.


pkg/sql/descriptor.go, line 60 at r4 (raw file):

Previously, tbg (Tobias Grieger) wrote…

Can DescIDGenerator be unexported?

Done.


pkg/sql/tenant.go, line 67 at r7 (raw file):

Previously, tbg (Tobias Grieger) wrote…

These are unused, right? Can we avoid supplying them?

Done by removing the system.zones table entirely from secondary tenants.


pkg/sql/tenant.go, line 100 at r7 (raw file):

Previously, tbg (Tobias Grieger) wrote…

It would (at some point) also fail because the KV layer won't let them.

Good point. KV level verification of tenant key accesses is going to be a great way to flush out bugs.


pkg/sql/tenant.go, line 129 at r7 (raw file):

Definitely not in the same transaction. I would say active = false is irrevocable and we start ClearRanging one the txn commits (blocking return of destroy_tenant). If that fails too early, can just re-run the command. The // tenant already destroyed case would go away.

I like that. I updated #48775 and the comment with the idea.

Are you intentionally keeping inactive tenants around? I thought they'd be gone once they're gone, but could be recreated.

No, I'm not intentionally keeping them around, I just figured that a soft delete would give us more flexibility until we decide on exactly how we want to implement space reclamation.


pkg/sql/logictest/testdata/logic_test/grant_table, line 168 at r7 (raw file):

Previously, tbg (Tobias Grieger) wrote…

Why the large diff in this method? Is that just bad diffing?

It looks like rowsort was hiding the fact that this output was in the completely wrong order (ORDER BY 1,2,3). Oliver also hit this in 86641f9#diff-b254deb01f059b658ce2401c034f3bdfL169.

I've removed the rowsort because it's not needed with the explicit ordering.


pkg/sql/sqlbase/metadata.go, line 231 at r5 (raw file):

Previously, tbg (Tobias Grieger) wrote…

I would rather fix this right now before we forget about it. The only caller into this cache is

descID := sqlbase.LookupSystemTableDescriptorID(ctx, settings, dbID, object)

which has the codec available. So you can just pre-generate two maps here, and index into the right one. The only plumbing is passing the codec one layer down into UncachedPhysicalAccessor.GetObjectDesc, and that only affects eight call sites.

Done.


pkg/sql/sqlbase/system.go, line 108 at r7 (raw file):

Previously, tbg (Tobias Grieger) wrote…

It seems unclear what we ultimately want here. I'm fine leaving this info field in, but let's add a comment that it's just a placeholder and may be kept, replaced, or even just removed.

Done.

Fixes cockroachdb#48513.

This commit creates a new SQL sequence called "descriptor_id_seq" that
is used by all non-system tenants to allocate descriptor IDs. This is
necessary because non-system tenants should not be using the global
`DescIDGenerator` key to allocate IDs.

The next commit includes tests that exercise the MetadataSchema portion
of this change.
This commit reworks the SQLCodec struct to be incomparable so that users
can't shoot themselves in the foot by trying to compare them directly to
other SQLCodec instances to determine tenant ID equality.

For instance, a common (but broken) pattern that had been emerging was
comparing a SQLCodec instance against `keys.SystemSQLCodec`. This was
not guaranteed to work, so it's better to disallow it. Instead, we
expose a new `ForSystemTenant` method for this one task. All other
tenant ID introspection is intentionally not possible.
This commit updates the SQL migrations framework to properly interact with
secondary tenants. This involves two main changes:
- using a separate migration keyspace for secondary tenants to store their
  completed migration and migration lease keys in.
- introducing the concept of cluster-wide SQL migrations that should only
  be run by the system-tenant. These migrations typically have to do with
  cluster settings, which is a cluster-wide concept.
Most of cockroachdb#47904.

First commit from cockroachdb#48504.
Second and third commit from cockroachdb#48773.

This commit implements the initial structure for tenant creation and
deletion. It does so by introducing a new system table to track tenants
in a multi-tenant cluster and two new builtin functions to manipulate
this table and the overall multi-tenant state.

The change starts by introducing a new `system.tenants` table with the
following schema:
```
CREATE TABLE system.tenants (
    id     INT8 NOT NULL PRIMARY KEY,
    active BOOL NOT NULL DEFAULT true,
    info   BYTES
);
```

The `id` column is self-explanatory. The `active` column is used to
coordinate tenant destruction in an asynchronous job. The `info` column
is an opaque byte slice to allow users to associate arbitrary
information with specific tenants. I don't know exactly how this third
field will be used (mapping tenants back to CockroachCloud user IDs?),
but it seems like a good idea to add some flexibility since we do intend
to eventually expose this externally.

The table is given an ID of 8, which means that it falls within the
"system config span". I believe this is ok, because the entire concept
of the "system config span" should be going away with cockroachdb#47150. The table
is also only exposed to the system-tenant and is never created for
secondary tenants.

The change then introduces two new builtin functions:
`crdb_internal.create_tenant` and `crdb_internal.destroy_tenant`. These
do what you would expect - creating and destroying tenant keyspaces,
along with updating metadata in system.tenants.
This commit marks the following two functions as undocumented builtins:
- `crdb_internal.create_tenant`
- `crdb_internal.destroy_tenant`

This prevents information about them from ending up in official docs.
We already had a notion of "Private" builtin functions, but the semantics
did not quite match what we wanted here. Notably, "Private" functions
can't be called from SQL at all and instead throw an error like:
```
[email protected]:50029/movr> select crdb_internal.create_tenant(5);
ERROR: crdb_internal.create_tenant(): crdb_internal.create_tenant(): function reserved for internal use
SQLSTATE: 42939
```
Relates to cockroachdb#48375.

Secondary tenants will not have a say over zone configurations, so
there's no reason to give them empty system.zones tables. This commit
addresses this by removing the table from secondary tenants. This allows
us to avoid populating this table with default zone configurations,
which would have been ignored.
@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/createTenant branch from 8b8375b to 6927a10 Compare May 14, 2020 20:17
@nvanbenschoten
Copy link
Member Author

bors r+

@craig
Copy link
Contributor

craig bot commented May 14, 2020

Build succeeded

@craig craig bot merged commit 08a969c into cockroachdb:master May 14, 2020
@nvanbenschoten nvanbenschoten deleted the nvanbenschoten/createTenant branch May 18, 2020 15:19
@lunevalex lunevalex added the A-multitenancy Related to multi-tenancy label Jun 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-multitenancy Related to multi-tenancy
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants